View Javadoc
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.bias;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.algebra.WrongSizeException;
20  import com.irurueta.geometry.Point3D;
21  import com.irurueta.navigation.LockedException;
22  import com.irurueta.navigation.frames.CoordinateTransformation;
23  import com.irurueta.navigation.frames.ECEFFrame;
24  import com.irurueta.navigation.frames.ECEFPosition;
25  import com.irurueta.navigation.frames.InvalidSourceAndDestinationFrameTypeException;
26  import com.irurueta.navigation.frames.NEDFrame;
27  import com.irurueta.navigation.frames.NEDPosition;
28  import com.irurueta.navigation.frames.converters.ECEFtoNEDFrameConverter;
29  import com.irurueta.navigation.frames.converters.NEDtoECEFFrameConverter;
30  import com.irurueta.navigation.inertial.BodyKinematics;
31  import com.irurueta.navigation.inertial.calibration.AccelerationTriad;
32  import com.irurueta.navigation.inertial.calibration.AngularSpeedTriad;
33  import com.irurueta.navigation.inertial.estimators.ECEFKinematicsEstimator;
34  import com.irurueta.units.Acceleration;
35  import com.irurueta.units.AccelerationUnit;
36  import com.irurueta.units.Angle;
37  import com.irurueta.units.AngularSpeed;
38  import com.irurueta.units.AngularSpeedUnit;
39  import com.irurueta.units.Distance;
40  import com.irurueta.units.Time;
41  import com.irurueta.units.TimeConverter;
42  import com.irurueta.units.TimeUnit;
43  
44  /**
45   * Approximately estimates accelerometer and gyroscope biases and noise PSD's
46   * by averaging all provided samples when body position and orientation is known
47   * while assuming that any cross coupling errors can be neglected.
48   * <p>
49   * This estimator must be used when the body where the accelerometer and gyroscope
50   * is attached remains static on the same position with zero velocity and no rotation
51   * speed while capturing data.
52   * <p>
53   * To compute PSD's this estimator assumes that accelerometer samples are obtained
54   * at a constant provided rate equal to {@link #getTimeInterval()} seconds.
55   * If not available, accelerometer and gyroscope sampling rate average can be
56   * estimated using {@link com.irurueta.navigation.inertial.calibration.TimeIntervalEstimator}.
57   * <p>
58   * Notice that in order to compute accelerometer and gyroscope biases, body position
59   * and orientation must be known to account for gravity and Earth rotation effects.
60   * <p>
61   * Even though this estimator obtains approximate bias values, the obtained
62   * result can be used to initialize some non-linear calibrators to obtain
63   * more accurate results. Such calibrators are:
64   * - com.irurueta.navigation.inertial.calibration.accelerometer.KnownFrameAccelerometerNonLinearLeastSquaresCalibrator
65   * - com.irurueta.navigation.inertial.calibration.accelerometer.KnownGravityNormAccelerometerCalibrator
66   * - com.irurueta.navigation.inertial.calibration.accelerometer.KnownPositionAccelerometerCalibrator}
67   * - com.irurueta.navigation.inertial.calibration.accelerometer.RobustKnownFrameAccelerometerCalibrator and
68   * any of its subclasses.
69   * - com.irurueta.navigation.inertial.calibration.accelerometer.RobustKnownGravityNormAccelerometerCalibrator
70   * and any of its subclasses.
71   * - com.irurueta.navigation.inertial.calibration.accelerometer.RobustKnownPositionAccelerometerCalibrator and
72   * any of its subclasses.
73   * - com.irurueta.navigation.inertial.calibration.gyroscope.KnownBiasAndFrameGyroscopeLinearLeastSquaresCalibrator
74   * - com.irurueta.navigation.inertial.calibration.gyroscope.KnownBiasAndFrameGyroscopeNonLinearLeastSquaresCalibrator
75   * - com.irurueta.navigation.inertial.calibration.gyroscope.KnownBiasEasyGyroscopeCalibrator
76   * - com.irurueta.navigation.inertial.calibration.gyroscope.KnownBiasTurntableGyroscopeCalibrator
77   * - com.irurueta.navigation.inertial.calibration.gyroscope.RobustKnownBiasAndFrameGyroscopeCalibrator and any
78   * of its subclasses.
79   * - com.irurueta.navigation.inertial.calibration.gyroscope.RobustKnownBiasEasyGyroscopeCalibrator and any of
80   * its subclasses.
81   * - com.irurueta.navigation.inertial.calibration.gyroscope.RobustKnownBiasTurntableGyroscopeCalibrator and any
82   * of its subclasses.
83   * <p>
84   * Even though this estimator can compute noise PSD's, if only noise PSD's levels
85   * are required, estimators in {@link com.irurueta.navigation.inertial.calibration.noise} package should
86   * be used instead.
87   * <p>
88   * This estimator does NOT compute average bias values over a period of time, it only
89   * computes accumulated averages.
90   */
91  public class BodyKinematicsBiasEstimator {
92  
93      /**
94       * Default time interval between kinematics samples expressed in seconds (s).
95       */
96      public static final double DEFAULT_TIME_INTERVAL_SECONDS = 0.02;
97  
98      /**
99       * Time interval expressed in seconds (s) between body kinematics samples.
100      */
101     private double timeInterval = DEFAULT_TIME_INTERVAL_SECONDS;
102 
103     /**
104      * Contains body position, velocity (which will always be zero) and orientation
105      * resolved around ECEF axes.
106      * By default it is assumed that body is located at zero NED coordinates (latitude,
107      * longitude and height) and with zero Euler angles representing rotation (roll = 0,
108      * pith = 0, yaw = 0), which for Android devices it means that the device is flat
109      * on a horizontal surface with the screen facing down.
110      */
111     private final ECEFFrame frame;
112 
113     /**
114      * Listener to handle events raised by this estimator.
115      */
116     private BodyKinematicsBiasEstimatorListener listener;
117 
118     /**
119      * Last provided body kinematics values.
120      */
121     private BodyKinematics lastBodyKinematics;
122 
123     /**
124      * Contains estimated bias of x coordinate of accelerometer sensed specific force
125      * expressed in meters per squared second (m/s^2).
126      */
127     private double biasFx;
128 
129     /**
130      * Contains estimated bias of y coordinate of accelerometer sensed specific force
131      * expressed in meters per squared second (m/s^2).
132      */
133     private double biasFy;
134 
135     /**
136      * Contains estimated bias of z coordinate of accelerometer sensed specific force
137      * expressed in meters per squared second (m/s^2).
138      */
139     private double biasFz;
140 
141     /**
142      * Contains estimated bias of x coordinate of gyroscope sensed angular rate
143      * expressed in radians per second (rad/s).
144      */
145     private double biasAngularRateX;
146 
147     /**
148      * Contains estimated bias of y coordinate of gyroscope sensed angular rate
149      * expressed in radians per second (rad/s).
150      */
151     private double biasAngularRateY;
152 
153     /**
154      * Contains estimated bias of z coordinate of gyroscope sensed angular rate
155      * expressed in radians per second (rad/s).
156      */
157     private double biasAngularRateZ;
158 
159     /**
160      * Contains estimated variance of x coordinate of accelerometer sensed specific
161      * force expressed in (m^2/s^4).
162      */
163     private double varianceFx;
164 
165     /**
166      * Contains estimated variance of y coordinate of accelerometer sensed specific
167      * force expressed in (m^2/s^4).
168      */
169     private double varianceFy;
170 
171     /**
172      * Contains estimated variance of z coordinate of accelerometer sensed specific
173      * force expressed in (m^2/s4).
174      */
175     private double varianceFz;
176 
177     /**
178      * Contains estimated variance of x coordinate of gyroscope sensed angular rate
179      * expressed in (rad^2/s^2).
180      */
181     private double varianceAngularRateX;
182 
183     /**
184      * Contains estimated variance of y coordinate of gyroscope sensed angular rate
185      * expressed in (rad^2/s^2).
186      */
187     private double varianceAngularRateY;
188 
189     /**
190      * Contains estimated variance of z coordinate of gyroscope sensed angular rate
191      * expressed in (rad^2/s^2).
192      */
193     private double varianceAngularRateZ;
194 
195     /**
196      * Number of processed body kinematics samples.
197      */
198     private int numberOfProcessedSamples;
199 
200     /**
201      * Number of processed body kinematics samples plus one.
202      */
203     private int numberOfProcessedSamplesPlusOne = 1;
204 
205     /**
206      * Indicates that estimator is running.
207      */
208     private boolean running;
209 
210     /**
211      * Theoretical expected body kinematics for provided body position and orientation,
212      * and provided time interval, assuming that body remains at the same position
213      * (zero velocity).
214      * When body remains static, sensed specific force and angular rates will remain
215      * constant due to gravity and Earth rotation.
216      */
217     private BodyKinematics expectedKinematics;
218 
219     /**
220      * Constructor.
221      * It is assumed that body is located at zero NED coordinates (latitude = 0,
222      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
223      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
224      * device is flat on a horizontal surface with the screen facing down.
225      */
226     public BodyKinematicsBiasEstimator() {
227         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame());
228         rebuildExpectedKinematics();
229     }
230 
231     /**
232      * Constructor.
233      * It is assumed that body is located at zero NED coordinates (latitude = 0,
234      * longitude = 0, and height = 0) with provided orientation.
235      *
236      * @param nedC coordinate transformation from body to local navigation
237      *             (NED) coordinates. This contains orientation respect the horizon
238      *             at current body location.
239      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
240      *                                                       transformation is not
241      *                                                       from body to local
242      *                                                       navigation coordinates.
243      */
244     public BodyKinematicsBiasEstimator(final CoordinateTransformation nedC)
245             throws InvalidSourceAndDestinationFrameTypeException {
246         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame(nedC));
247         rebuildExpectedKinematics();
248     }
249 
250     /**
251      * Constructor.
252      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
253      * pitch = 0, yaw = 0) respect the horizon at provided body location.
254      * For Android devices this means that the device is flat on a horizontal surface
255      * with the screen facing down.
256      *
257      * @param latitude  latitude expressed in radians (rad).
258      * @param longitude longitude expressed in radians (rad).
259      * @param height    height expressed in meters (m).
260      */
261     public BodyKinematicsBiasEstimator(final double latitude, final double longitude, final double height) {
262         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame(latitude, longitude, height));
263         rebuildExpectedKinematics();
264     }
265 
266     /**
267      * Constructor.
268      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
269      * pitch = 0, yaw = 0) respect the horizon at provided body location.
270      * For Android devices this means that the device is flat on a horizontal surface
271      * with the screen facing down.
272      *
273      * @param latitude  latitude.
274      * @param longitude longitude.
275      * @param height    height expressed in meters (m).
276      */
277     public BodyKinematicsBiasEstimator(final Angle latitude, final Angle longitude, final double height) {
278         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame(latitude, longitude, height));
279         rebuildExpectedKinematics();
280     }
281 
282     /**
283      * Constructor.
284      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
285      * pitch = 0, yaw = 0) respect the horizon at provided body location.
286      * For Android devices this means that the device is flat on a horizontal surface
287      * with the screen facing down.
288      *
289      * @param latitude  latitude.
290      * @param longitude longitude.
291      * @param height    height.
292      */
293     public BodyKinematicsBiasEstimator(final Angle latitude, final Angle longitude, final Distance height) {
294         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame(latitude, longitude, height));
295         rebuildExpectedKinematics();
296     }
297 
298     /**
299      * Constructor.
300      *
301      * @param position body position expressed in NED coordinates.
302      * @param nedC     coordinate transformation from body to local navigation
303      *                 (NED) coordinates. This contains orientation respect the
304      *                 horizon at current body location.
305      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
306      *                                                       transformation is not
307      *                                                       from body to local
308      *                                                       navigation coordinates.
309      */
310     public BodyKinematicsBiasEstimator(final NEDPosition position, final CoordinateTransformation nedC)
311             throws InvalidSourceAndDestinationFrameTypeException {
312         frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(new NEDFrame(position, nedC));
313         rebuildExpectedKinematics();
314     }
315 
316     /**
317      * Constructor.
318      *
319      * @param position body position expressed in ECEF coordinates.
320      * @param nedC     coordinate transformation from body to local navigation
321      *                 (NED) coordinates. This contains orientation respect the
322      *                 horizon at current body location.
323      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
324      *                                                       transformation is not
325      *                                                       from body to local
326      *                                                       navigation coordinates.
327      */
328     public BodyKinematicsBiasEstimator(final ECEFPosition position, final CoordinateTransformation nedC)
329             throws InvalidSourceAndDestinationFrameTypeException {
330         frame = new ECEFFrame(position);
331         final var nedFrame = ECEFtoNEDFrameConverter.convertECEFtoNEDAndReturnNew(frame);
332         nedFrame.setCoordinateTransformation(nedC);
333         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
334         rebuildExpectedKinematics();
335     }
336 
337     /**
338      * Constructor.
339      * It is assumed that body is located at zero NED coordinates (latitude = 0,
340      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
341      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
342      * device is flat on a horizontal surface with the screen facing down.
343      *
344      * @param listener listener to handle events raised by this estimator.
345      */
346     public BodyKinematicsBiasEstimator(final BodyKinematicsBiasEstimatorListener listener) {
347         this();
348         this.listener = listener;
349     }
350 
351     /**
352      * Constructor.
353      * It is assumed that body is located at zero NED coordinates (latitude = 0,
354      * longitude = 0, and height = 0) with provided orientation.
355      *
356      * @param nedC     coordinate transformation from body to local navigation
357      *                 (NED) coordinates. This contains orientation respect the
358      *                 horizon at current body location.
359      * @param listener listener to handle events raised by this estimator.
360      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
361      *                                                       transformation is not
362      *                                                       from body to local
363      *                                                       navigation coordinates.
364      */
365     public BodyKinematicsBiasEstimator(
366             final CoordinateTransformation nedC, final BodyKinematicsBiasEstimatorListener listener)
367             throws InvalidSourceAndDestinationFrameTypeException {
368         this(nedC);
369         this.listener = listener;
370     }
371 
372     /**
373      * Constructor.
374      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
375      * pitch = 0, yaw = 0) respect the horizon at provided body location.
376      * For Android devices this means that the device is flat on a horizontal surface
377      * with the screen facing down.
378      *
379      * @param latitude  latitude expressed in radians (rad).
380      * @param longitude longitude expressed in radians (rad).
381      * @param height    height expressed in meters (m).
382      * @param listener  listener to handle events raised by this estimator.
383      */
384     public BodyKinematicsBiasEstimator(
385             final double latitude, final double longitude, final double height,
386             final BodyKinematicsBiasEstimatorListener listener) {
387         this(latitude, longitude, height);
388         this.listener = listener;
389     }
390 
391     /**
392      * Constructor.
393      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
394      * pitch = 0, yaw = 0) respect the horizon at provided body location.
395      * For Android devices this means that the device is flat on a horizontal surface
396      * with the screen facing down.
397      *
398      * @param latitude  latitude.
399      * @param longitude longitude.
400      * @param height    height expressed in meters (m).
401      * @param listener  listener to handle events raised by this estimator.
402      */
403     public BodyKinematicsBiasEstimator(
404             final Angle latitude, final Angle longitude, final double height,
405             final BodyKinematicsBiasEstimatorListener listener) {
406         this(latitude, longitude, height);
407         this.listener = listener;
408     }
409 
410     /**
411      * Constructor.
412      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
413      * pitch = 0, yaw = 0) respect the horizon at provided body location.
414      * For Android devices this means that the device is flat on a horizontal surface
415      * with the screen facing down.
416      *
417      * @param latitude  latitude.
418      * @param longitude longitude.
419      * @param height    height.
420      * @param listener  listener to handle events raised by this estimator.
421      */
422     public BodyKinematicsBiasEstimator(
423             final Angle latitude, final Angle longitude, final Distance height,
424             final BodyKinematicsBiasEstimatorListener listener) {
425         this(latitude, longitude, height);
426         this.listener = listener;
427     }
428 
429     /**
430      * Constructor.
431      *
432      * @param position body position expressed in NED coordinates.
433      * @param nedC     coordinate transformation from body to local navigation
434      *                 (NED) coordinates. This contains orientation respect the
435      *                 horizon at current body location.
436      * @param listener listener to handle events raised by this estimator.
437      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
438      *                                                       transformation is not
439      *                                                       from body to local
440      *                                                       navigation coordinates.
441      */
442     public BodyKinematicsBiasEstimator(
443             final NEDPosition position, final CoordinateTransformation nedC,
444             final BodyKinematicsBiasEstimatorListener listener) throws InvalidSourceAndDestinationFrameTypeException {
445         this(position, nedC);
446         this.listener = listener;
447     }
448 
449     /**
450      * Constructor.
451      *
452      * @param position body position expressed in ECEF coordinates.
453      * @param nedC     coordinate transformation from body to local navigation
454      *                 (NED) coordinates. This contains orientation respect the
455      *                 horizon at current body location.
456      * @param listener listener to handle events raised by this estimator.
457      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
458      *                                                       transformation is not
459      *                                                       from body to local
460      *                                                       navigation coordinates.
461      */
462     public BodyKinematicsBiasEstimator(
463             final ECEFPosition position, final CoordinateTransformation nedC,
464             final BodyKinematicsBiasEstimatorListener listener) throws InvalidSourceAndDestinationFrameTypeException {
465         this(position, nedC);
466         this.listener = listener;
467     }
468 
469     /**
470      * Constructor.
471      * It is assumed that body is located at zero NED coordinates (latitude = 0,
472      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
473      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
474      * device is flat on a horizontal surface with the screen facing down.
475      *
476      * @param timeInterval time interval between body kinematics
477      *                     (IMU acceleration + gyroscope) samples
478      *                     expressed in seconds (s).
479      * @throws IllegalArgumentException if provided time interval is negative.
480      */
481     public BodyKinematicsBiasEstimator(final double timeInterval) {
482         this();
483         try {
484             setTimeInterval(timeInterval);
485         } catch (final LockedException ignore) {
486             // never happens
487         }
488     }
489 
490     /**
491      * Constructor.
492      * It is assumed that body is located at zero NED coordinates (latitude = 0,
493      * longitude = 0, and height = 0) with provided orientation.
494      *
495      * @param nedC         coordinate transformation from body to local navigation
496      *                     (NED) coordinates. This contains orientation respect the
497      *                     horizon at current body location.
498      * @param timeInterval time interval between body kinematics
499      *                     (IMU acceleration + gyroscope) samples
500      *                     expressed in seconds (s).
501      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
502      *                                                       transformation is not
503      *                                                       from body to local
504      *                                                       navigation coordinates.
505      * @throws IllegalArgumentException                      if provided time interval
506      *                                                       is negative.
507      */
508     public BodyKinematicsBiasEstimator(
509             final CoordinateTransformation nedC, final double timeInterval)
510             throws InvalidSourceAndDestinationFrameTypeException {
511         this(nedC);
512         try {
513             setTimeInterval(timeInterval);
514         } catch (final LockedException ignore) {
515             // never happens
516         }
517     }
518 
519     /**
520      * Constructor.
521      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
522      * pitch = 0, yaw = 0) respect the horizon at provided body location.
523      * For Android devices this means that the device is flat on a horizontal surface
524      * with the screen facing down.
525      *
526      * @param latitude     latitude expressed in radians (rad).
527      * @param longitude    longitude expressed in radians (rad).
528      * @param height       height expressed in meters (m).
529      * @param timeInterval time interval between body kinematics
530      *                     (IMU acceleration + gyroscope) samples
531      *                     expressed in seconds (s).
532      * @throws IllegalArgumentException if provided time interval is negative.
533      */
534     public BodyKinematicsBiasEstimator(
535             final double latitude, final double longitude, final double height, final double timeInterval) {
536         this(latitude, longitude, height);
537         try {
538             setTimeInterval(timeInterval);
539         } catch (final LockedException ignore) {
540             // never happens
541         }
542     }
543 
544     /**
545      * Constructor.
546      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
547      * pitch = 0, yaw = 0) respect the horizon at provided body location.
548      * For Android devices this means that the device is flat on a horizontal surface
549      * with the screen facing down.
550      *
551      * @param latitude     latitude.
552      * @param longitude    longitude.
553      * @param height       height expressed in meters (m).
554      * @param timeInterval time interval between body kinematics
555      *                     (IMU acceleration + gyroscope) samples
556      *                     expressed in seconds (s).
557      * @throws IllegalArgumentException if provided time interval is negative.
558      */
559     public BodyKinematicsBiasEstimator(
560             final Angle latitude, final Angle longitude, final double height, final double timeInterval) {
561         this(latitude, longitude, height);
562         try {
563             setTimeInterval(timeInterval);
564         } catch (final LockedException ignore) {
565             // never happens
566         }
567     }
568 
569     /**
570      * Constructor.
571      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
572      * pitch = 0, yaw = 0) respect the horizon at provided body location.
573      * For Android devices this means that the device is flat on a horizontal surface
574      * with the screen facing down.
575      *
576      * @param latitude     latitude.
577      * @param longitude    longitude.
578      * @param height       height.
579      * @param timeInterval time interval between body kinematics
580      *                     (IMU acceleration + gyroscope) samples
581      *                     expressed in seconds (s).
582      * @throws IllegalArgumentException if provided time interval is negative.
583      */
584     public BodyKinematicsBiasEstimator(
585             final Angle latitude, final Angle longitude, final Distance height, final double timeInterval) {
586         this(latitude, longitude, height);
587         try {
588             setTimeInterval(timeInterval);
589         } catch (final LockedException ignore) {
590             // never happens
591         }
592     }
593 
594     /**
595      * Constructor.
596      *
597      * @param position     body position expressed in NED coordinates.
598      * @param nedC         coordinate transformation from body to local navigation
599      *                     (NED) coordinates. This contains orientation respect the
600      *                     horizon at current body location.
601      * @param timeInterval time interval between body kinematics
602      *                     (IMU acceleration + gyroscope) samples
603      *                     expressed in seconds (s).
604      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
605      *                                                       transformation is not
606      *                                                       from body to local
607      *                                                       navigation coordinates.
608      * @throws IllegalArgumentException                      if provided time interval
609      *                                                       is negative.
610      */
611     public BodyKinematicsBiasEstimator(
612             final NEDPosition position, final CoordinateTransformation nedC, final double timeInterval)
613             throws InvalidSourceAndDestinationFrameTypeException {
614         this(position, nedC);
615         try {
616             setTimeInterval(timeInterval);
617         } catch (final LockedException ignore) {
618             // never happens
619         }
620     }
621 
622     /**
623      * Constructor.
624      *
625      * @param position     body position expressed in ECEF coordinates.
626      * @param nedC         coordinate transformation from body to local navigation
627      *                     (NED) coordinates. This contains orientation respect the
628      *                     horizon at current body location.
629      * @param timeInterval time interval between body kinematics
630      *                     (IMU acceleration + gyroscope) samples
631      *                     expressed in seconds (s).
632      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
633      *                                                       transformation is not
634      *                                                       from body to local
635      *                                                       navigation coordinates.
636      * @throws IllegalArgumentException                      if provided time interval
637      *                                                       is negative.
638      */
639     public BodyKinematicsBiasEstimator(
640             final ECEFPosition position, final CoordinateTransformation nedC, final double timeInterval)
641             throws InvalidSourceAndDestinationFrameTypeException {
642         this(position, nedC);
643         try {
644             setTimeInterval(timeInterval);
645         } catch (final LockedException ignore) {
646             // never happens
647         }
648     }
649 
650     /**
651      * Constructor.
652      * It is assumed that body is located at zero NED coordinates (latitude = 0,
653      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
654      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
655      * device is flat on a horizontal surface with the screen facing down.
656      *
657      * @param timeInterval time interval between body kinematics
658      *                     (IMU acceleration + gyroscope) samples
659      *                     expressed in seconds (s).
660      * @param listener     listener to handle events raised by this estimator.
661      * @throws IllegalArgumentException if provided time interval is negative.
662      */
663     public BodyKinematicsBiasEstimator(final double timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
664         this(timeInterval);
665         this.listener = listener;
666     }
667 
668     /**
669      * Constructor.
670      * It is assumed that body is located at zero NED coordinates (latitude = 0,
671      * longitude = 0, and height = 0) with provided orientation.
672      *
673      * @param nedC         coordinate transformation from body to local navigation
674      *                     (NED) coordinates. This contains orientation respect the
675      *                     horizon at current body location.
676      * @param timeInterval time interval between body kinematics
677      *                     (IMU acceleration + gyroscope) samples
678      *                     expressed in seconds (s).
679      * @param listener     listener to handle events raised by this estimator.
680      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
681      *                                                       transformation is not
682      *                                                       from body to local
683      *                                                       navigation coordinates.
684      * @throws IllegalArgumentException                      if provided time interval
685      *                                                       is negative.
686      */
687     public BodyKinematicsBiasEstimator(
688             final CoordinateTransformation nedC, final double timeInterval,
689             final BodyKinematicsBiasEstimatorListener listener) throws InvalidSourceAndDestinationFrameTypeException {
690         this(nedC, timeInterval);
691         this.listener = listener;
692     }
693 
694     /**
695      * Constructor.
696      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
697      * pitch = 0, yaw = 0) respect the horizon at provided body location.
698      * For Android devices this means that the device is flat on a horizontal surface
699      * with the screen facing down.
700      *
701      * @param latitude     latitude expressed in radians (rad).
702      * @param longitude    longitude expressed in radians (rad).
703      * @param height       height expressed in meters (m).
704      * @param timeInterval time interval between body kinematics
705      *                     (IMU acceleration + gyroscope) samples
706      *                     expressed in seconds (s).
707      * @param listener     listener to handle events raised by this estimator.
708      * @throws IllegalArgumentException if provided time interval is negative.
709      */
710     public BodyKinematicsBiasEstimator(
711             final double latitude, final double longitude, final double height,
712             final double timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
713         this(latitude, longitude, height, timeInterval);
714         this.listener = listener;
715     }
716 
717     /**
718      * Constructor.
719      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
720      * pitch = 0, yaw = 0) respect the horizon at provided body location.
721      * For Android devices this means that the device is flat on a horizontal surface
722      * with the screen facing down.
723      *
724      * @param latitude     latitude.
725      * @param longitude    longitude.
726      * @param height       height expressed in meters (m).
727      * @param timeInterval time interval between body kinematics
728      *                     (IMU acceleration + gyroscope) samples
729      *                     expressed in seconds (s).
730      * @param listener     listener to handle events raised by this estimator.
731      * @throws IllegalArgumentException if provided time interval is negative.
732      */
733     public BodyKinematicsBiasEstimator(
734             final Angle latitude, final Angle longitude, final double height,
735             final double timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
736         this(latitude, longitude, height, timeInterval);
737         this.listener = listener;
738     }
739 
740     /**
741      * Constructor.
742      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
743      * pitch = 0, yaw = 0) respect the horizon at provided body location.
744      * For Android devices this means that the device is flat on a horizontal surface
745      * with the screen facing down.
746      *
747      * @param latitude     latitude.
748      * @param longitude    longitude.
749      * @param height       height.
750      * @param timeInterval time interval between body kinematics
751      *                     (IMU acceleration + gyroscope) samples
752      *                     expressed in seconds (s).
753      * @param listener     listener to handle events raised by this estimator.
754      * @throws IllegalArgumentException if provided time interval is negative.
755      */
756     public BodyKinematicsBiasEstimator(
757             final Angle latitude, final Angle longitude, final Distance height,
758             final double timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
759         this(latitude, longitude, height, timeInterval);
760         this.listener = listener;
761     }
762 
763     /**
764      * Constructor.
765      *
766      * @param position     body position expressed in NED coordinates.
767      * @param nedC         coordinate transformation from body to local navigation
768      *                     (NED) coordinates. This contains orientation respect the
769      *                     horizon at current body location.
770      * @param timeInterval time interval between body kinematics
771      *                     (IMU acceleration + gyroscope) samples
772      *                     expressed in seconds (s).
773      * @param listener     listener to handle events raised by this estimator.
774      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
775      *                                                       transformation is not
776      *                                                       from body to local
777      *                                                       navigation coordinates.
778      * @throws IllegalArgumentException                      if provided time interval
779      *                                                       is negative.
780      */
781     public BodyKinematicsBiasEstimator(
782             final NEDPosition position, final CoordinateTransformation nedC,
783             final double timeInterval, final BodyKinematicsBiasEstimatorListener listener)
784             throws InvalidSourceAndDestinationFrameTypeException {
785         this(position, nedC, timeInterval);
786         this.listener = listener;
787     }
788 
789     /**
790      * Constructor.
791      *
792      * @param position     body position expressed in ECEF coordinates.
793      * @param nedC         coordinate transformation from body to local navigation
794      *                     (NED) coordinates. This contains orientation respect the
795      *                     horizon at current body location.
796      * @param timeInterval time interval between body kinematics
797      *                     (IMU acceleration + gyroscope) samples
798      *                     expressed in seconds (s).
799      * @param listener     listener to handle events raised by this estimator.
800      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
801      *                                                       transformation is not
802      *                                                       from body to local
803      *                                                       navigation coordinates.
804      * @throws IllegalArgumentException                      if provided time interval
805      *                                                       is negative.
806      */
807     public BodyKinematicsBiasEstimator(
808             final ECEFPosition position, final CoordinateTransformation nedC,
809             final double timeInterval, final BodyKinematicsBiasEstimatorListener listener)
810             throws InvalidSourceAndDestinationFrameTypeException {
811         this(position, nedC, timeInterval);
812         this.listener = listener;
813     }
814 
815     /**
816      * Constructor.
817      * It is assumed that body is located at zero NED coordinates (latitude = 0,
818      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
819      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
820      * device is flat on a horizontal surface with the screen facing down.
821      *
822      * @param timeInterval time interval between body kinematics
823      *                     (IMU acceleration + gyroscope) samples.
824      * @throws IllegalArgumentException if provided time interval is negative.
825      */
826     public BodyKinematicsBiasEstimator(final Time timeInterval) {
827         this(convertTime(timeInterval));
828     }
829 
830     /**
831      * Constructor.
832      * It is assumed that body is located at zero NED coordinates (latitude = 0,
833      * longitude = 0, and height = 0) with provided orientation.
834      *
835      * @param nedC         coordinate transformation from body to local navigation
836      *                     (NED) coordinates. This contains orientation respect the
837      *                     horizon at current body location.
838      * @param timeInterval time interval between body kinematics
839      *                     (IMU acceleration + gyroscope) samples.
840      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
841      *                                                       transformation is not
842      *                                                       from body to local
843      *                                                       navigation coordinates.
844      * @throws IllegalArgumentException                      if provided time interval
845      *                                                       is negative.
846      */
847     public BodyKinematicsBiasEstimator(final CoordinateTransformation nedC, final Time timeInterval)
848             throws InvalidSourceAndDestinationFrameTypeException {
849         this(nedC, convertTime(timeInterval));
850     }
851 
852     /**
853      * Constructor.
854      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
855      * pitch = 0, yaw = 0) respect the horizon at provided body location.
856      * For Android devices this means that the device is flat on a horizontal surface
857      * with the screen facing down.
858      *
859      * @param latitude     latitude expressed in radians (rad).
860      * @param longitude    longitude expressed in radians (rad).
861      * @param height       height expressed in meters (m).
862      * @param timeInterval time interval between body kinematics
863      *                     (IMU acceleration + gyroscope) samples.
864      * @throws IllegalArgumentException if provided time interval is negative.
865      */
866     public BodyKinematicsBiasEstimator(
867             final double latitude, final double longitude, final double height, final Time timeInterval) {
868         this(latitude, longitude, height, convertTime(timeInterval));
869     }
870 
871     /**
872      * Constructor.
873      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
874      * pitch = 0, yaw = 0) respect the horizon at provided body location.
875      * For Android devices this means that the device is flat on a horizontal surface
876      * with the screen facing down.
877      *
878      * @param latitude     latitude.
879      * @param longitude    longitude.
880      * @param height       height expressed in meters (m).
881      * @param timeInterval time interval between body kinematics
882      *                     (IMU acceleration + gyroscope) samples.
883      * @throws IllegalArgumentException if provided time interval is negative.
884      */
885     public BodyKinematicsBiasEstimator(
886             final Angle latitude, final Angle longitude, final double height, final Time timeInterval) {
887         this(latitude, longitude, height, convertTime(timeInterval));
888     }
889 
890     /**
891      * Constructor.
892      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
893      * pitch = 0, yaw = 0) respect the horizon at provided body location.
894      * For Android devices this means that the device is flat on a horizontal surface
895      * with the screen facing down.
896      *
897      * @param latitude     latitude.
898      * @param longitude    longitude.
899      * @param height       height.
900      * @param timeInterval time interval between body kinematics
901      *                     (IMU acceleration + gyroscope) samples.
902      * @throws IllegalArgumentException if provided time interval is negative.
903      */
904     public BodyKinematicsBiasEstimator(
905             final Angle latitude, final Angle longitude, final Distance height, final Time timeInterval) {
906         this(latitude, longitude, height, convertTime(timeInterval));
907     }
908 
909     /**
910      * Constructor.
911      *
912      * @param position     body position expressed in NED coordinates.
913      * @param nedC         coordinate transformation from body to local navigation
914      *                     (NED) coordinates. This contains orientation respect the
915      *                     horizon at current body location.
916      * @param timeInterval time interval between body kinematics
917      *                     (IMU acceleration + gyroscope) samples.
918      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
919      *                                                       transformation is not
920      *                                                       from body to local
921      *                                                       navigation coordinates.
922      * @throws IllegalArgumentException                      if provided time interval
923      *                                                       is negative.
924      */
925     public BodyKinematicsBiasEstimator(
926             final NEDPosition position, final CoordinateTransformation nedC, final Time timeInterval)
927             throws InvalidSourceAndDestinationFrameTypeException {
928         this(position, nedC, convertTime(timeInterval));
929     }
930 
931     /**
932      * Constructor.
933      *
934      * @param position     body position expressed in ECEF coordinates.
935      * @param nedC         coordinate transformation from body to local navigation
936      *                     (NED) coordinates. This contains orientation respect the
937      *                     horizon at current body location.
938      * @param timeInterval time interval between body kinematics
939      *                     (IMU acceleration + gyroscope) samples.
940      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
941      *                                                       transformation is not
942      *                                                       from body to local
943      *                                                       navigation coordinates.
944      * @throws IllegalArgumentException                      if provided time interval
945      *                                                       is negative.
946      */
947     public BodyKinematicsBiasEstimator(
948             final ECEFPosition position, final CoordinateTransformation nedC, final Time timeInterval)
949             throws InvalidSourceAndDestinationFrameTypeException {
950         this(position, nedC, convertTime(timeInterval));
951     }
952 
953     /**
954      * Constructor.
955      * It is assumed that body is located at zero NED coordinates (latitude = 0,
956      * longitude = 0 and height = 0) and with zero Euler angles representing rotation
957      * (roll = 0, pith = 0, yaw = 0), which for Android devices it means that the
958      * device is flat on a horizontal surface with the screen facing down.
959      *
960      * @param timeInterval time interval between body kinematics
961      *                     (IMU acceleration + gyroscope) samples.
962      * @param listener     listener to handle events raised by this estimator.
963      * @throws IllegalArgumentException if provided time interval is negative.
964      */
965     public BodyKinematicsBiasEstimator(final Time timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
966         this(convertTime(timeInterval), listener);
967     }
968 
969     /**
970      * Constructor.
971      * It is assumed that body is located at zero NED coordinates (latitude = 0,
972      * longitude = 0, and height = 0) with provided orientation.
973      *
974      * @param nedC         coordinate transformation from body to local navigation
975      *                     (NED) coordinates. This contains orientation respect the
976      *                     horizon at current body location.
977      * @param timeInterval time interval between body kinematics
978      *                     (IMU acceleration + gyroscope) samples.
979      * @param listener     listener to handle events raised by this estimator.
980      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
981      *                                                       transformation is not
982      *                                                       from body to local
983      *                                                       navigation coordinates.
984      * @throws IllegalArgumentException                      if provided time interval
985      *                                                       is negative.
986      */
987     public BodyKinematicsBiasEstimator(
988             final CoordinateTransformation nedC, final Time timeInterval,
989             final BodyKinematicsBiasEstimatorListener listener) throws InvalidSourceAndDestinationFrameTypeException {
990         this(nedC, convertTime(timeInterval), listener);
991     }
992 
993     /**
994      * Constructor.
995      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
996      * pitch = 0, yaw = 0) respect the horizon at provided body location.
997      * For Android devices this means that the device is flat on a horizontal surface
998      * with the screen facing down.
999      *
1000      * @param latitude     latitude expressed in radians (rad).
1001      * @param longitude    longitude expressed in radians (rad).
1002      * @param height       height expressed in meters (m).
1003      * @param timeInterval time interval between body kinematics
1004      *                     (IMU acceleration + gyroscope) samples.
1005      * @param listener     listener to handle events raised by this estimator.
1006      * @throws IllegalArgumentException if provided time interval is negative.
1007      */
1008     public BodyKinematicsBiasEstimator(
1009             final double latitude, final double longitude, final double height,
1010             final Time timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
1011         this(latitude, longitude, height, convertTime(timeInterval), listener);
1012     }
1013 
1014     /**
1015      * Constructor.
1016      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
1017      * pitch = 0, yaw = 0) respect the horizon at provided body location.
1018      * For Android devices this means that the device is flat on a horizontal surface
1019      * with the screen facing down.
1020      *
1021      * @param latitude     latitude.
1022      * @param longitude    longitude.
1023      * @param height       height expressed in meters (m).
1024      * @param timeInterval time interval between body kinematics
1025      *                     (IMU acceleration + gyroscope) samples.
1026      * @param listener     listener to handle events raised by this estimator.
1027      * @throws IllegalArgumentException if provided time interval is negative.
1028      */
1029     public BodyKinematicsBiasEstimator(
1030             final Angle latitude, final Angle longitude, final double height, final Time timeInterval,
1031             final BodyKinematicsBiasEstimatorListener listener) {
1032         this(latitude, longitude, height, convertTime(timeInterval), listener);
1033     }
1034 
1035     /**
1036      * Constructor.
1037      * It is assumed that body has zero Euler angles representing rotation (roll = 0,
1038      * pitch = 0, yaw = 0) respect the horizon at provided body location.
1039      * For Android devices this means that the device is flat on a horizontal surface
1040      * with the screen facing down.
1041      *
1042      * @param latitude     latitude.
1043      * @param longitude    longitude.
1044      * @param height       height.
1045      * @param timeInterval time interval between body kinematics
1046      *                     (IMU acceleration + gyroscope) samples.
1047      * @param listener     listener to handle events raised by this estimator.
1048      * @throws IllegalArgumentException if provided time interval is negative.
1049      */
1050     public BodyKinematicsBiasEstimator(
1051             final Angle latitude, final Angle longitude, final Distance height,
1052             final Time timeInterval, final BodyKinematicsBiasEstimatorListener listener) {
1053         this(latitude, longitude, height, convertTime(timeInterval), listener);
1054     }
1055 
1056     /**
1057      * Constructor.
1058      *
1059      * @param position     body position expressed in NED coordinates.
1060      * @param nedC         coordinate transformation from body to local navigation
1061      *                     (NED) coordinates. This contains orientation respect the
1062      *                     horizon at current body location.
1063      * @param timeInterval time interval between body kinematics
1064      *                     (IMU acceleration + gyroscope) samples.
1065      * @param listener     listener to handle events raised by this estimator.
1066      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1067      *                                                       transformation is not
1068      *                                                       from body to local
1069      *                                                       navigation coordinates.
1070      * @throws IllegalArgumentException                      if provided time interval
1071      *                                                       is negative.
1072      */
1073     public BodyKinematicsBiasEstimator(
1074             final NEDPosition position, final CoordinateTransformation nedC,
1075             final Time timeInterval, final BodyKinematicsBiasEstimatorListener listener)
1076             throws InvalidSourceAndDestinationFrameTypeException {
1077         this(position, nedC, convertTime(timeInterval), listener);
1078     }
1079 
1080     /**
1081      * Constructor.
1082      *
1083      * @param position     body position expressed in ECEF coordinates.
1084      * @param nedC         coordinate transformation from body to local navigation
1085      *                     (NED) coordinates. This contains orientation respect the
1086      *                     horizon at current body location.
1087      * @param timeInterval time interval between body kinematics
1088      *                     (IMU acceleration + gyroscope) samples.
1089      * @param listener     listener to handle events raised by this estimator.
1090      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1091      *                                                       transformation is not
1092      *                                                       from body to local
1093      *                                                       navigation coordinates.
1094      * @throws IllegalArgumentException                      if provided time interval
1095      *                                                       is negative.
1096      */
1097     public BodyKinematicsBiasEstimator(
1098             final ECEFPosition position, final CoordinateTransformation nedC,
1099             final Time timeInterval, final BodyKinematicsBiasEstimatorListener listener)
1100             throws InvalidSourceAndDestinationFrameTypeException {
1101         this(position, nedC, convertTime(timeInterval), listener);
1102     }
1103 
1104     /**
1105      * Gets time interval between body kinematics (IMU acceleration + gyroscope)
1106      * samples expressed in seconds (s).
1107      *
1108      * @return time interval between body kinematics samples.
1109      */
1110     public double getTimeInterval() {
1111         return timeInterval;
1112     }
1113 
1114     /**
1115      * Sets time interval between body kinematics (IMU acceleration + gyroscope)
1116      * samples expressed in seconds (s).
1117      *
1118      * @param timeInterval time interval between body kinematics samples.
1119      * @throws LockedException if estimator is currently running.
1120      */
1121     public void setTimeInterval(final double timeInterval) throws LockedException {
1122         if (running) {
1123             throw new LockedException();
1124         }
1125 
1126         if (timeInterval < 0.0) {
1127             throw new IllegalArgumentException();
1128         }
1129 
1130         this.timeInterval = timeInterval;
1131 
1132         rebuildExpectedKinematics();
1133     }
1134 
1135     /**
1136      * Gets time interval between body kinematics (IMU acceleration + gyroscope)
1137      * samples.
1138      *
1139      * @return time interval between body kinematics samples.
1140      */
1141     public Time getTimeIntervalAsTime() {
1142         return new Time(timeInterval, TimeUnit.SECOND);
1143     }
1144 
1145     /**
1146      * Gets time interval between body kinematics (IMU acceleration + gyroscope)
1147      * samples.
1148      *
1149      * @param result instance where time interval will be stored.
1150      */
1151     public void getTimeIntervalAsTime(final Time result) {
1152         result.setValue(timeInterval);
1153         result.setUnit(TimeUnit.SECOND);
1154     }
1155 
1156     /**
1157      * Sets time interval between body kinematics (IMU acceleration + gyroscope)
1158      * samples.
1159      *
1160      * @param timeInterval time interval between body kinematics samples.
1161      * @throws LockedException if estimator is currently running.
1162      */
1163     public void setTimeInterval(final Time timeInterval) throws LockedException {
1164         setTimeInterval(convertTime(timeInterval));
1165     }
1166 
1167     /**
1168      * Gets current body position expressed in ECEF coordinates.
1169      *
1170      * @return current body position expressed in ECEF coordinates.
1171      */
1172     public ECEFPosition getEcefPosition() {
1173         return frame.getECEFPosition();
1174     }
1175 
1176     /**
1177      * Gets current body position expressed in ECEF coordinates.
1178      *
1179      * @param result instance where current body position will be stored.
1180      */
1181     public void getEcefPosition(final ECEFPosition result) {
1182         frame.getECEFPosition(result);
1183     }
1184 
1185     /**
1186      * Sets current body position expressed in ECEF coordinates.
1187      *
1188      * @param position current body position to be set.
1189      * @throws LockedException if estimator is currently running.
1190      */
1191     public void setEcefPosition(final ECEFPosition position) throws LockedException {
1192         if (running) {
1193             throw new LockedException();
1194         }
1195 
1196         frame.setPosition(position);
1197         rebuildExpectedKinematics();
1198     }
1199 
1200     /**
1201      * Sets current body position expressed in ECEF coordinates.
1202      *
1203      * @param x x position resolved around ECEF axes and expressed in meters (m).
1204      * @param y y position resolved around ECEF axes and expressed in meters (m).
1205      * @param z z position resolved around ECEF axes and expressed in meters (m).
1206      * @throws LockedException if estimator is currently running.
1207      */
1208     public void setEcefPosition(final double x, final double y, final double z) throws LockedException {
1209         if (running) {
1210             throw new LockedException();
1211         }
1212 
1213         frame.setCoordinates(x, y, z);
1214         rebuildExpectedKinematics();
1215     }
1216 
1217     /**
1218      * Sets current body position expressed in ECEF coordinates.
1219      *
1220      * @param x x position resolved around ECEF axes.
1221      * @param y y position resolved around ECEF axes.
1222      * @param z z position resolved around ECEF axes.
1223      * @throws LockedException if estimator is currently running.
1224      */
1225     public void setEcefPosition(final Distance x, final Distance y, final Distance z) throws LockedException {
1226         if (running) {
1227             throw new LockedException();
1228         }
1229 
1230         frame.setPositionCoordinates(x, y, z);
1231         rebuildExpectedKinematics();
1232     }
1233 
1234     /**
1235      * Sets current body position expressed in ECEF coordinates.
1236      *
1237      * @param position position resolved around ECEF axes and expressed in meters (m).
1238      * @throws LockedException if estimator is currently running.
1239      */
1240     public void setEcefPosition(final Point3D position) throws LockedException {
1241         if (running) {
1242             throw new LockedException();
1243         }
1244 
1245         frame.setPosition(position);
1246         rebuildExpectedKinematics();
1247     }
1248 
1249     /**
1250      * Gets ECEF frame containing current body position and orientation expressed
1251      * in ECEF coordinates. Frame also contains body velocity, but it is always
1252      * assumed to be zero during calibration.
1253      *
1254      * @return ECEF frame containing current body position and orientation resolved
1255      * around ECEF axes.
1256      */
1257     public ECEFFrame getEcefFrame() {
1258         return new ECEFFrame(frame);
1259     }
1260 
1261     /**
1262      * Gets ECEF frame containing current body position and orientation expressed
1263      * in ECEF coordinates. Frame also contains body velocity, but it is always
1264      * assumed to be zero during calibration.
1265      *
1266      * @param result instance where ECEF frame containing current body position and
1267      *               orientation resolved around ECEF axes will be stored.
1268      */
1269     public void getEcefFrame(final ECEFFrame result) {
1270         frame.copyTo(result);
1271     }
1272 
1273     /**
1274      * Gets NED frame containing current body position and orientation expressed
1275      * in NED coordinates. Frame also contains body velocity, but it is always
1276      * assumed to be zero during calibration.
1277      *
1278      * @return NED frame containing current body position and orientation resolved
1279      * around NED axes.
1280      */
1281     public NEDFrame getNedFrame() {
1282         return ECEFtoNEDFrameConverter.convertECEFtoNEDAndReturnNew(frame);
1283     }
1284 
1285     /**
1286      * Gets NED frame containing current body position and orientation expressed
1287      * in NED coordinates. Frame also contains body velocity, but it is always
1288      * assumed to be zero during calibration.
1289      *
1290      * @param result instance where NED frame containing current body position and
1291      *               orientation resolved around NED axes will be stored.
1292      */
1293     public void getNedFrame(final NEDFrame result) {
1294         ECEFtoNEDFrameConverter.convertECEFtoNED(frame, result);
1295     }
1296 
1297     /**
1298      * Gets current body position expressed in NED coordinates.
1299      *
1300      * @return current body position expressed in NED coordinates.
1301      */
1302     public NEDPosition getNedPosition() {
1303         return getNedFrame().getPosition();
1304     }
1305 
1306     /**
1307      * Gets current body position expressed in NED coordinates.
1308      *
1309      * @param result instance where current body position will be stored.
1310      */
1311     public void getNedPosition(final NEDPosition result) {
1312         getNedFrame().getPosition(result);
1313     }
1314 
1315     /**
1316      * Sets current body position expressed in NED coordinates.
1317      *
1318      * @param position current body position to be set.
1319      * @throws LockedException if estimator is currently running.
1320      */
1321     public void setNedPosition(final NEDPosition position) throws LockedException {
1322         if (running) {
1323             throw new LockedException();
1324         }
1325 
1326         final var nedFrame = getNedFrame();
1327         nedFrame.setPosition(position);
1328         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1329         rebuildExpectedKinematics();
1330     }
1331 
1332     /**
1333      * Sets current body position expressed in NED coordinates.
1334      *
1335      * @param latitude  latitude NED coordinate expressed in radians (rad).
1336      * @param longitude longitude NED coordinate expressed in radians (rad).
1337      * @param height    height NED coordinate expressed in meters (m).
1338      * @throws LockedException if estimator is currently running.
1339      */
1340     public void setNedPosition(
1341             final double latitude, final double longitude, final double height) throws LockedException {
1342         if (running) {
1343             throw new LockedException();
1344         }
1345 
1346         final var nedFrame = getNedFrame();
1347         nedFrame.setPosition(latitude, longitude, height);
1348         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1349         rebuildExpectedKinematics();
1350     }
1351 
1352     /**
1353      * Sets current body position expressed in NED coordinates.
1354      *
1355      * @param latitude  latitude NED coordinate.
1356      * @param longitude longitude NED coordinate.
1357      * @param height    height NED coordinate expressed in meters (m).
1358      * @throws LockedException if estimator is currently running.
1359      */
1360     public void setNedPosition(
1361             final Angle latitude, final Angle longitude, final double height) throws LockedException {
1362         if (running) {
1363             throw new LockedException();
1364         }
1365 
1366         final var nedFrame = getNedFrame();
1367         nedFrame.setPosition(latitude, longitude, height);
1368         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1369         rebuildExpectedKinematics();
1370     }
1371 
1372     /**
1373      * Sets current body position expressed in NED coordinates.
1374      *
1375      * @param latitude  latitude NED coordinate.
1376      * @param longitude longitude NED coordinate.
1377      * @param height    height NED coordinate.
1378      * @throws LockedException if estimator is currently running.
1379      */
1380     public void setNedPosition(
1381             final Angle latitude, final Angle longitude, final Distance height) throws LockedException {
1382         if (running) {
1383             throw new LockedException();
1384         }
1385 
1386         final var nedFrame = getNedFrame();
1387         nedFrame.setPosition(latitude, longitude, height);
1388         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1389         rebuildExpectedKinematics();
1390     }
1391 
1392     /**
1393      * Gets current body orientation as a transformation from body to ECEF coordinates.
1394      * Notice that returned orientation refers to ECEF Earth axes, which means that
1395      * orientation is not relative to the ground or horizon at current body position.
1396      * Typically it is more convenient to use {@link #getNedC()} to obtain orientation
1397      * relative to the ground or horizon at current body position. For instance, on
1398      * Android devices a NED orientation with Euler angles (roll = 0, pitch = 0,
1399      * yaw = 0) means that the device is laying flat on a horizontal surface with the
1400      * screen facing down towards the ground.
1401      *
1402      * @return current body orientation resolved on ECEF axes.
1403      */
1404     public CoordinateTransformation getEcefC() {
1405         return frame.getCoordinateTransformation();
1406     }
1407 
1408     /**
1409      * Gets current body orientation as a transformation from body to ECEF coordinates.
1410      * Notice that returned orientation refers to ECEF Earth axes, which means that
1411      * orientation is not relative to the ground or horizon at current body position.
1412      * Typically it is more convenient to use {@link #getNedC()} to obtain orientation
1413      * relative to the ground or horizon at current body position. For instance, on
1414      * Android devices a NED orientation with Euler angles (roll = 0, pitch = 0,
1415      * yaw = 0) means that the device is laying flat on a horizontal surface with the
1416      * screen facing down towards the ground.
1417      *
1418      * @param result instance where current body orientation resolved on ECEF axes
1419      *               will be stored.
1420      */
1421     public void getEcefC(final CoordinateTransformation result) {
1422         frame.getCoordinateTransformation(result);
1423     }
1424 
1425     /**
1426      * Sets current body orientation as a transformation from body to ECEF coordinates.
1427      * Notice that ECEF orientation refers to ECEF Earth axes, which means that
1428      * orientation is not relative to the ground or horizon at current body position.
1429      * Typically it is more convenient to use
1430      * {@link #setNedC(CoordinateTransformation)} to specify orientation relative to
1431      * the ground or horizon at current body position.
1432      * For instance, on Android devices a NED orientation with Euler angles (roll = 0,
1433      * pitch = 0, yaw = 0) means that the device is laying flat on a horizontal surface
1434      * with the screen facing down towards the ground.
1435      *
1436      * @param ecefC body orientation resolved on ECEF axes to be set.
1437      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1438      *                                                       transformation is not from
1439      *                                                       body to ECEF coordinates.
1440      * @throws LockedException                               if estimator is currently
1441      *                                                       running.
1442      */
1443     public void setEcefC(final CoordinateTransformation ecefC)
1444             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1445         if (running) {
1446             throw new LockedException();
1447         }
1448 
1449         frame.setCoordinateTransformation(ecefC);
1450         rebuildExpectedKinematics();
1451     }
1452 
1453     /**
1454      * Gets current body orientation as a transformation from body to NED coordinates.
1455      * Notice that returned orientation refers to current local position. This means
1456      * that two equal NED orientations will transform into different ECEF orientations
1457      * if the body is located at different positions.
1458      * As a reference, on Android devices a NED orientation with Euler angles
1459      * (roll = 0, pitch = 0, yaw = 0) means that the device is laying flat on a
1460      * horizontal surface with the screen facing down towards the ground.
1461      *
1462      * @return current body orientation resolved on NED axes.
1463      */
1464     public CoordinateTransformation getNedC() {
1465         return getNedFrame().getCoordinateTransformation();
1466     }
1467 
1468     /**
1469      * Gets current body orientation as a transformation from body to NED coordinates.
1470      * Notice that returned orientation refers to current local position. This means
1471      * that two equal NED orientations will transform into different ECEF orientations
1472      * if the body is located at different positions.
1473      * As a reference, on Android devices a NED orientation with Euler angles
1474      * (roll = 0, pitch = 0, yaw = 0) means that the device is laying flat on a
1475      * horizontal surface with the screen facing down towards the ground.
1476      *
1477      * @param result instance where current body orientation resolved on NED axes
1478      *               will be stored.
1479      */
1480     public void getNedC(final CoordinateTransformation result) {
1481         getNedFrame().getCoordinateTransformation(result);
1482     }
1483 
1484     /**
1485      * Sets current body orientation as a transformation from body to NED coordinates.
1486      * Notice that provided orientation refers to current local position. This means
1487      * that two equal NED orientations will transform into different ECEF orientations
1488      * if the body is located at different positions.
1489      * As a reference, on Android devices a NED orientation with Euler angles
1490      * (roll = 0, pitch = 0, yaw = 0) means that the device is laying flat on a
1491      * horizontal surface with the screen facing down towards the ground.
1492      *
1493      * @param nedC orientation resolved on NED axes to be set.
1494      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1495      *                                                       transformation is not from
1496      *                                                       body to NED coordinates.
1497      * @throws LockedException                               if estimator is currently
1498      *                                                       running.
1499      */
1500     public void setNedC(final CoordinateTransformation nedC)
1501             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1502         if (running) {
1503             throw new LockedException();
1504         }
1505 
1506         final var nedFrame = getNedFrame();
1507         nedFrame.setCoordinateTransformation(nedC);
1508         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1509         rebuildExpectedKinematics();
1510     }
1511 
1512     /**
1513      * Sets position and orientation both expressed on NED coordinates.
1514      *
1515      * @param nedPosition position expressed on NED coordinates.
1516      * @param nedC        body to NED coordinate transformation indicating
1517      *                    body orientation.
1518      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1519      *                                                       transformation is not from
1520      *                                                       body to NED coordinates.
1521      * @throws LockedException                               if estimator is currently
1522      *                                                       running.
1523      * @see #setNedPosition(NEDPosition)
1524      * @see #setNedC(CoordinateTransformation)
1525      */
1526     public void setNedPositionAndNedOrientation(
1527             final NEDPosition nedPosition, final CoordinateTransformation nedC)
1528             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1529         if (running) {
1530             throw new LockedException();
1531         }
1532 
1533         final var nedFrame = getNedFrame();
1534         nedFrame.setPosition(nedPosition);
1535         nedFrame.setCoordinateTransformation(nedC);
1536         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1537         rebuildExpectedKinematics();
1538     }
1539 
1540     /**
1541      * Sets position and orientation both expressed on NED coordinates.
1542      *
1543      * @param latitude  latitude expressed in radians (rad).
1544      * @param longitude longitude expressed in radians (rad).
1545      * @param height    height expressed in meters (m).
1546      * @param nedC      body to NED coordinate transformation indicating
1547      *                  body orientation.
1548      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1549      *                                                       transformation is not from
1550      *                                                       body to NED coordinates.
1551      * @throws LockedException                               if estimator is currently
1552      *                                                       running.
1553      * @see #setNedPosition(double, double, double)
1554      * @see #setNedC(CoordinateTransformation)
1555      */
1556     public void setNedPositionAndNedOrientation(
1557             final double latitude, final double longitude, final double height,
1558             final CoordinateTransformation nedC) throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1559         if (running) {
1560             throw new LockedException();
1561         }
1562 
1563         final var nedFrame = getNedFrame();
1564         nedFrame.setPosition(latitude, longitude, height);
1565         nedFrame.setCoordinateTransformation(nedC);
1566         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1567         rebuildExpectedKinematics();
1568     }
1569 
1570     /**
1571      * Sets position and orientation both expressed on NED coordinates.
1572      *
1573      * @param latitude  latitude.
1574      * @param longitude longitude.
1575      * @param height    height expressed in meters (m).
1576      * @param nedC      body to NED coordinate transformation indicating
1577      *                  body orientation.
1578      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1579      *                                                       transformation is not from
1580      *                                                       body to NED coordinates.
1581      * @throws LockedException                               if estimator is currently
1582      *                                                       running.
1583      * @see #setNedPosition(Angle, Angle, double)
1584      * @see #setNedC(CoordinateTransformation)
1585      */
1586     public void setNedPositionAndNedOrientation(
1587             final Angle latitude, final Angle longitude, final double height,
1588             final CoordinateTransformation nedC) throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1589         if (running) {
1590             throw new LockedException();
1591         }
1592 
1593         final var nedFrame = getNedFrame();
1594         nedFrame.setPosition(latitude, longitude, height);
1595         nedFrame.setCoordinateTransformation(nedC);
1596         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1597         rebuildExpectedKinematics();
1598     }
1599 
1600     /**
1601      * Sets position and orientation both expressed on NED coordinates.
1602      *
1603      * @param latitude  latitude.
1604      * @param longitude longitude.
1605      * @param height    height.
1606      * @param nedC      body to NED coordinate transformation indicating
1607      *                  body orientation.
1608      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1609      *                                                       transformation is not from
1610      *                                                       body to NED coordinates.
1611      * @throws LockedException                               if estimator is currently
1612      *                                                       running.
1613      * @see #setNedPosition(Angle, Angle, Distance)
1614      * @see #setNedC(CoordinateTransformation)
1615      */
1616     public void setNedPositionAndNedOrientation(
1617             final Angle latitude, final Angle longitude, final Distance height,
1618             final CoordinateTransformation nedC) throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1619         if (running) {
1620             throw new LockedException();
1621         }
1622 
1623         final var nedFrame = getNedFrame();
1624         nedFrame.setPosition(latitude, longitude, height);
1625         nedFrame.setCoordinateTransformation(nedC);
1626         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1627         rebuildExpectedKinematics();
1628     }
1629 
1630     /**
1631      * Sets position and orientation both expressed on ECEF coordinates.
1632      *
1633      * @param ecefPosition position expressed on ECEF coordinates.
1634      * @param ecefC        body to ECEF coordinate transformation indicating body
1635      *                     orientation.
1636      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1637      *                                                       transformation is not from
1638      *                                                       body to ECEF coordinates.
1639      * @throws LockedException                               if estimator is currently
1640      *                                                       running.
1641      * @see #setEcefPosition(ECEFPosition)
1642      * @see #setEcefC(CoordinateTransformation)
1643      */
1644     public void setEcefPositionAndEcefOrientation(
1645             final ECEFPosition ecefPosition, final CoordinateTransformation ecefC)
1646             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1647         if (running) {
1648             throw new LockedException();
1649         }
1650 
1651         frame.setPosition(ecefPosition);
1652         frame.setCoordinateTransformation(ecefC);
1653         rebuildExpectedKinematics();
1654     }
1655 
1656     /**
1657      * Sets position and orientation both expressed on ECEF coordinates.
1658      *
1659      * @param x     x coordinate of ECEF position expressed in meters (m).
1660      * @param y     y coordinate of ECEF position expressed in meters (m).
1661      * @param z     z coordinate of ECEF position expressed in meters (m).
1662      * @param ecefC body to ECEF coordinate transformation indicating body
1663      *              orientation.
1664      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1665      *                                                       transformation is not from
1666      *                                                       body to ECEF coordinates.
1667      * @throws LockedException                               if estimator is currently
1668      *                                                       running.
1669      * @see #setEcefPosition(double, double, double)
1670      * @see #setEcefC(CoordinateTransformation)
1671      */
1672     public void setEcefPositionAndEcefOrientation(
1673             final double x, final double y, final double z,
1674             final CoordinateTransformation ecefC)
1675             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1676         if (running) {
1677             throw new LockedException();
1678         }
1679 
1680         frame.setCoordinates(x, y, z);
1681         frame.setCoordinateTransformation(ecefC);
1682         rebuildExpectedKinematics();
1683     }
1684 
1685     /**
1686      * Sets position and orientation both expressed on ECEF coordinates.
1687      *
1688      * @param x     x coordinate of ECEF position.
1689      * @param y     y coordinate of ECEF position.
1690      * @param z     z coordinate of ECEF position.
1691      * @param ecefC body to ECEF coordinate transformation indicating body
1692      *              orientation.
1693      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1694      *                                                       transformation is not from
1695      *                                                       body to ECEF coordinates.
1696      * @throws LockedException                               if estimator is currently
1697      *                                                       running.
1698      * @see #setEcefPosition(Distance, Distance, Distance)
1699      * @see #setEcefC(CoordinateTransformation)
1700      */
1701     public void setEcefPositionAndEcefOrientation(
1702             final Distance x, final Distance y, final Distance z,
1703             final CoordinateTransformation ecefC)
1704             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1705         if (running) {
1706             throw new LockedException();
1707         }
1708 
1709         frame.setPositionCoordinates(x, y, z);
1710         frame.setCoordinateTransformation(ecefC);
1711         rebuildExpectedKinematics();
1712     }
1713 
1714     /**
1715      * Sets position and orientation both expressed on ECEF coordinates.
1716      *
1717      * @param position position resolved around ECEF axes and expressed in meters (m).
1718      * @param ecefC    body to ECEF coordinate transformation indicating body
1719      *                 orientation.
1720      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1721      *                                                       transformation is not from
1722      *                                                       body to ECEF coordinates.
1723      * @throws LockedException                               if estimator is currently
1724      *                                                       running.
1725      * @see #setEcefPosition(Point3D)
1726      * @see #setEcefC(CoordinateTransformation)
1727      */
1728     public void setEcefPositionAndEcefOrientation(
1729             final Point3D position, final CoordinateTransformation ecefC)
1730             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1731         if (running) {
1732             throw new LockedException();
1733         }
1734 
1735         frame.setPosition(position);
1736         frame.setCoordinateTransformation(ecefC);
1737         rebuildExpectedKinematics();
1738     }
1739 
1740     /**
1741      * Sets position expressed on NED coordinates and orientation respect to ECEF
1742      * axes.
1743      *
1744      * @param position position expressed on NED coordinates.
1745      * @param ecefC    body to ECEF coordinate transformation indicating body
1746      *                 orientation.
1747      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1748      *                                                       transformation is not from
1749      *                                                       body to ECEF coordinates.
1750      * @throws LockedException                               if estimator is currently
1751      *                                                       running.
1752      * @see #setNedPosition(NEDPosition)
1753      * @see #setEcefC(CoordinateTransformation)
1754      */
1755     public void setNedPositionAndEcefOrientation(
1756             final NEDPosition position,
1757             final CoordinateTransformation ecefC)
1758             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1759         if (running) {
1760             throw new LockedException();
1761         }
1762 
1763         final var nedFrame = getNedFrame();
1764         nedFrame.setPosition(position);
1765         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1766         frame.setCoordinateTransformation(ecefC);
1767         rebuildExpectedKinematics();
1768     }
1769 
1770     /**
1771      * Sets position expressed on NED coordinates and orientation respect to ECEF
1772      * axes.
1773      *
1774      * @param latitude  latitude expressed in radians (rad).
1775      * @param longitude longitude expressed in radians (rad).
1776      * @param height    height expressed in meters (m).
1777      * @param ecefC     body to ECEF coordinate transformation indicating body
1778      *                  orientation.
1779      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1780      *                                                       transformation is not from
1781      *                                                       body to ECEF coordinates.
1782      * @throws LockedException                               if estimator is currently
1783      *                                                       running.
1784      * @see #setNedPosition(double, double, double)
1785      * @see #setEcefC(CoordinateTransformation)
1786      */
1787     public void setNedPositionAndEcefOrientation(
1788             final double latitude, final double longitude, final double height,
1789             final CoordinateTransformation ecefC)
1790             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1791         if (running) {
1792             throw new LockedException();
1793         }
1794 
1795         final var nedFrame = getNedFrame();
1796         nedFrame.setPosition(latitude, longitude, height);
1797         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1798         frame.setCoordinateTransformation(ecefC);
1799         rebuildExpectedKinematics();
1800     }
1801 
1802     /**
1803      * Sets position expressed on NED coordinates and orientation respect to ECEF
1804      * axes.
1805      *
1806      * @param latitude  latitude.
1807      * @param longitude longitude.
1808      * @param height    height expressed in meters (m).
1809      * @param ecefC     body to ECEF coordinate transformation indicating body
1810      *                  orientation.
1811      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1812      *                                                       transformation is not from
1813      *                                                       body to ECEF coordinates.
1814      * @throws LockedException                               if estimator is currently
1815      *                                                       running.
1816      * @see #setNedPosition(Angle, Angle, double)
1817      * @see #setEcefC(CoordinateTransformation)
1818      */
1819     public void setNedPositionAndEcefOrientation(
1820             final Angle latitude, final Angle longitude, final double height,
1821             final CoordinateTransformation ecefC)
1822             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1823         if (running) {
1824             throw new LockedException();
1825         }
1826 
1827         final var nedFrame = getNedFrame();
1828         nedFrame.setPosition(latitude, longitude, height);
1829         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1830         frame.setCoordinateTransformation(ecefC);
1831         rebuildExpectedKinematics();
1832     }
1833 
1834     /**
1835      * Sets position expressed on NED coordinates and orientation respect to ECEF
1836      * axes.
1837      *
1838      * @param latitude  latitude.
1839      * @param longitude longitude.
1840      * @param height    height.
1841      * @param ecefC     body to ECEF coordinate transformation indicating body
1842      *                  orientation.
1843      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1844      *                                                       transformation is not from
1845      *                                                       body to ECEF coordinates.
1846      * @throws LockedException                               if estimator is currently
1847      *                                                       running.
1848      * @see #setNedPosition(Angle, Angle, Distance)
1849      * @see #setEcefC(CoordinateTransformation)
1850      */
1851     public void setNedPositionAndEcefOrientation(
1852             final Angle latitude, final Angle longitude, final Distance height,
1853             final CoordinateTransformation ecefC)
1854             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1855         if (running) {
1856             throw new LockedException();
1857         }
1858 
1859         final var nedFrame = getNedFrame();
1860         nedFrame.setPosition(latitude, longitude, height);
1861         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1862         frame.setCoordinateTransformation(ecefC);
1863         rebuildExpectedKinematics();
1864     }
1865 
1866     /**
1867      * Sets position expressed on ECEF coordinates and orientation respect to
1868      * NED axes.
1869      * In order to preserve provided orientation, first position is set and
1870      * then orientation is applied.
1871      *
1872      * @param ecefPosition position expressed on ECEF coordinates.
1873      * @param nedC         body to NED coordinate transformation indicating body
1874      *                     orientation.
1875      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1876      *                                                       transformation is not from
1877      *                                                       body to NED coordinates.
1878      * @throws LockedException                               if estimator is currently
1879      *                                                       running.
1880      * @see #setEcefPosition(ECEFPosition)
1881      * @see #setNedC(CoordinateTransformation)
1882      */
1883     public void setEcefPositionAndNedOrientation(
1884             final ECEFPosition ecefPosition, final CoordinateTransformation nedC)
1885             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1886         if (running) {
1887             throw new LockedException();
1888         }
1889 
1890         frame.setPosition(ecefPosition);
1891 
1892         final var nedFrame = getNedFrame();
1893         nedFrame.setCoordinateTransformation(nedC);
1894         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1895         rebuildExpectedKinematics();
1896     }
1897 
1898     /**
1899      * Sets position expressed on ECEF coordinates and orientation respect to
1900      * NED axes.
1901      * In order to preserve provided orientation, first position is set and
1902      * then orientation is applied.
1903      *
1904      * @param x    x coordinate of ECEF position expressed in meters (m).
1905      * @param y    y coordinate of ECEF position expressed in meters (m).
1906      * @param z    z coordinate of ECEF position expressed in meters (m).
1907      * @param nedC body to NED coordinate transformation indicating body
1908      *             orientation.
1909      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1910      *                                                       transformation is not from
1911      *                                                       body to NED coordinates.
1912      * @throws LockedException                               if estimator is currently
1913      *                                                       running.
1914      * @see #setEcefPosition(double, double, double)
1915      * @see #setNedC(CoordinateTransformation)
1916      */
1917     public void setEcefPositionAndNedOrientation(
1918             final double x, final double y, final double z, final CoordinateTransformation nedC)
1919             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1920         if (running) {
1921             throw new LockedException();
1922         }
1923 
1924         frame.setCoordinates(x, y, z);
1925 
1926         final var nedFrame = getNedFrame();
1927         nedFrame.setCoordinateTransformation(nedC);
1928         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1929         rebuildExpectedKinematics();
1930     }
1931 
1932     /**
1933      * Sets position expressed on ECEF coordinates and orientation respect to
1934      * NED axes.
1935      * In order to preserve provided orientation, first position is set and
1936      * then orientation is applied.
1937      *
1938      * @param x    x coordinate of ECEF position.
1939      * @param y    y coordinate of ECEF position.
1940      * @param z    z coordinate of ECEF position.
1941      * @param nedC body to NED coordinate transformation indicating body
1942      *             orientation.
1943      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1944      *                                                       transformation is not from
1945      *                                                       body to NED coordinates.
1946      * @throws LockedException                               if estimator is currently
1947      *                                                       running.
1948      * @see #setEcefPosition(Distance, Distance, Distance)
1949      * @see #setNedC(CoordinateTransformation)
1950      */
1951     public void setEcefPositionAndNedOrientation(
1952             final Distance x, final Distance y, final Distance z, final CoordinateTransformation nedC)
1953             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1954         if (running) {
1955             throw new LockedException();
1956         }
1957 
1958         frame.setPositionCoordinates(x, y, z);
1959 
1960         final var nedFrame = getNedFrame();
1961         nedFrame.setCoordinateTransformation(nedC);
1962         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1963         rebuildExpectedKinematics();
1964     }
1965 
1966     /**
1967      * Sets position expressed on ECEF coordinates and orientation respect to
1968      * NED axes.
1969      * In order to preserve provided orientation, first position is set and
1970      * then orientation is applied.
1971      *
1972      * @param position position resolved around ECEF axes and expressed in meters (m).
1973      * @param nedC     body to NED coordinate transformation indicating body
1974      *                 orientation.
1975      * @throws InvalidSourceAndDestinationFrameTypeException if provided coordinate
1976      *                                                       transformation is not from
1977      *                                                       body to NED coordinates.
1978      * @throws LockedException                               if estimator is currently
1979      *                                                       running.
1980      * @see #setEcefPosition(Point3D)
1981      * @see #setNedC(CoordinateTransformation)
1982      */
1983     public void setEcefPositionAndNedOrientation(
1984             final Point3D position, final CoordinateTransformation nedC)
1985             throws InvalidSourceAndDestinationFrameTypeException, LockedException {
1986         if (running) {
1987             throw new LockedException();
1988         }
1989 
1990         frame.setPosition(position);
1991 
1992         final var nedFrame = getNedFrame();
1993         nedFrame.setCoordinateTransformation(nedC);
1994         NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
1995         rebuildExpectedKinematics();
1996     }
1997 
1998     /**
1999      * Gets listener to handle events raised by this estimator.
2000      *
2001      * @return listener to handle events raised by this estimator.
2002      */
2003     public BodyKinematicsBiasEstimatorListener getListener() {
2004         return listener;
2005     }
2006 
2007     /**
2008      * Sets listener to handle events raised by this estimator.
2009      *
2010      * @param listener listener to handle events raised by this estimator.
2011      * @throws LockedException if this estimator is running.
2012      */
2013     public void setListener(final BodyKinematicsBiasEstimatorListener listener) throws LockedException {
2014         if (running) {
2015             throw new LockedException();
2016         }
2017 
2018         this.listener = listener;
2019     }
2020 
2021     /**
2022      * Gets last provided body kinematics values or null if not available.
2023      *
2024      * @return last provided body kinematics values or null.
2025      */
2026     public BodyKinematics getLastBodyKinematics() {
2027         return lastBodyKinematics != null ? new BodyKinematics(lastBodyKinematics) : null;
2028     }
2029 
2030     /**
2031      * Gets last provided body kinematics values.
2032      *
2033      * @param result instance where last provided body kinematics will be stored.
2034      * @return true if result instance was updated, false otherwise.
2035      */
2036     public boolean getLastBodyKinematics(final BodyKinematics result) {
2037         if (lastBodyKinematics != null) {
2038             lastBodyKinematics.copyTo(result);
2039             return true;
2040         } else {
2041             return false;
2042         }
2043     }
2044 
2045     /**
2046      * Gets estimated bias of x coordinate of accelerometer sensed specific force
2047      * expressed in meters per squared second (m/s^2).
2048      *
2049      * @return bias of x coordinate of sensed specific force.
2050      */
2051     public double getBiasFx() {
2052         return biasFx;
2053     }
2054 
2055     /**
2056      * Gets estimated bias of x coordinate of accelerometer sensed specific force.
2057      *
2058      * @return bias of x coordinate of sensed specific force.
2059      */
2060     public Acceleration getBiasFxAsAcceleration() {
2061         return new Acceleration(biasFx, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2062     }
2063 
2064     /**
2065      * Gets estimated bias of x coordinate of accelerometer sensed specific force.
2066      *
2067      * @param result instance where bias of x coordinate of sensed specific force
2068      *               will be stored.
2069      */
2070     public void getBiasFxAsAcceleration(final Acceleration result) {
2071         result.setValue(biasFx);
2072         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2073     }
2074 
2075     /**
2076      * Gets estimated bias of x coordinate of accelerometer sensed specific force
2077      * expressed in meters per squared second (m/s^2).
2078      *
2079      * @return bias of y coordinate of sensed specific force.
2080      */
2081     public double getBiasFy() {
2082         return biasFy;
2083     }
2084 
2085     /**
2086      * Gets estimated bias of y coordinate of accelerometer sensed specific force.
2087      *
2088      * @return bias of y coordinate of sensed specific force.
2089      */
2090     public Acceleration getBiasFyAsAcceleration() {
2091         return new Acceleration(biasFy, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2092     }
2093 
2094     /**
2095      * Gets estimated bias of y coordinate of accelerometer sensed specific force.
2096      *
2097      * @param result instance where bias of y coordinate of sensed specific force
2098      *               will be stored.
2099      */
2100     public void getBiasFyAsAcceleration(final Acceleration result) {
2101         result.setValue(biasFy);
2102         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2103     }
2104 
2105     /**
2106      * Gets estimated bias of z coordinate of accelerometer sensed specific force
2107      * expressed in meters per squared second (m/s^2).
2108      *
2109      * @return bias of z coordinate of sensed specific force.
2110      */
2111     public double getBiasFz() {
2112         return biasFz;
2113     }
2114 
2115     /**
2116      * Gets estimated bias of z coordinate of accelerometer sensed specific force.
2117      *
2118      * @return bias of z coordinate of sensed specific force.
2119      */
2120     public Acceleration getBiasFzAsAcceleration() {
2121         return new Acceleration(biasFz, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2122     }
2123 
2124     /**
2125      * Gets estimated bias of z coordinate of accelerometer sensed specific force.
2126      *
2127      * @param result instance where bias of z coordinate of sensed specific force
2128      *               will be stored.
2129      */
2130     public void getBiasFzAsAcceleration(final Acceleration result) {
2131         result.setValue(biasFz);
2132         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2133     }
2134 
2135     /**
2136      * Gets estimated bias of x coordinate of gyroscope sensed angular rate
2137      * expressed in radians per second (rad/s).
2138      *
2139      * @return bias of x coordinate of sensed angular rate.
2140      */
2141     public double getBiasAngularRateX() {
2142         return biasAngularRateX;
2143     }
2144 
2145     /**
2146      * Gets estimated bias of x coordinate of gyroscope sensed angular rate.
2147      *
2148      * @return bias of x coordinate of sensed angular rate.
2149      */
2150     public AngularSpeed getBiasAngularRateXAsAngularSpeed() {
2151         return new AngularSpeed(biasAngularRateX, AngularSpeedUnit.RADIANS_PER_SECOND);
2152     }
2153 
2154     /**
2155      * Gets estimated bias of x coordinate of gyroscope sensed angular rate.
2156      *
2157      * @param result instance where bias of x coordinate of sensed angular rate
2158      *               will be stored.
2159      */
2160     public void getBiasAngularRateXAsAngularSpeed(final AngularSpeed result) {
2161         result.setValue(biasAngularRateX);
2162         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2163     }
2164 
2165     /**
2166      * Gets estimated bias of y coordinate of gyroscope sensed angular rate
2167      * expressed in radians per second (rad/s).
2168      *
2169      * @return bias of y coordinate of sensed angular rate.
2170      */
2171     public double getBiasAngularRateY() {
2172         return biasAngularRateY;
2173     }
2174 
2175     /**
2176      * Gets estimated bias of y coordinate of gyroscope sensed angular rate.
2177      *
2178      * @return bias of y coordinate of sensed angular rate.
2179      */
2180     public AngularSpeed getBiasAngularRateYAsAngularSpeed() {
2181         return new AngularSpeed(biasAngularRateY, AngularSpeedUnit.RADIANS_PER_SECOND);
2182     }
2183 
2184     /**
2185      * Gets estimated bias of y coordinate of gyroscope sensed angular rate.
2186      *
2187      * @param result instance where bias of y coordinate of sensed angular rate
2188      *               will be stored.
2189      */
2190     public void getBiasAngularRateYAsAngularSpeed(final AngularSpeed result) {
2191         result.setValue(biasAngularRateY);
2192         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2193     }
2194 
2195     /**
2196      * Gets estimated bias of z coordinate of gyroscope sensed angular rate
2197      * expressed in radians per second (rad/s).
2198      *
2199      * @return bias of z coordinate of sensed angular rate.
2200      */
2201     public double getBiasAngularRateZ() {
2202         return biasAngularRateZ;
2203     }
2204 
2205     /**
2206      * Gets estimated bias of z coordinate of gyroscope sensed angular rate.
2207      *
2208      * @return bias of z coordinate of sensed angular rate.
2209      */
2210     public AngularSpeed getBiasAngularRateZAsAngularSpeed() {
2211         return new AngularSpeed(biasAngularRateZ, AngularSpeedUnit.RADIANS_PER_SECOND);
2212     }
2213 
2214     /**
2215      * Gets estimated bias of z coordinate of gyroscope sensed angular rate.
2216      *
2217      * @param result instance where bias of z coordinate of sensed angular rate
2218      *               will be stored.
2219      */
2220     public void getBiasAngularRateZAsAngularSpeed(final AngularSpeed result) {
2221         result.setValue(biasAngularRateZ);
2222         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2223     }
2224 
2225     /**
2226      * Gets estimated bias of accelerometer sensed specific force.
2227      *
2228      * @return estimated bias of accelerometer sensed specific force.
2229      */
2230     public AccelerationTriad getBiasF() {
2231         return new AccelerationTriad(AccelerationUnit.METERS_PER_SQUARED_SECOND, biasFx, biasFy, biasFz);
2232     }
2233 
2234     /**
2235      * Gets estimated bias of accelerometer sensed specific force.
2236      *
2237      * @param result instance where bias of sensed specific force will
2238      *               be stored.
2239      */
2240     public void getBiasF(final AccelerationTriad result) {
2241         result.setValueCoordinatesAndUnit(biasFx, biasFy, biasFz, AccelerationUnit.METERS_PER_SQUARED_SECOND);
2242     }
2243 
2244     /**
2245      * Gets estimated bias of gyroscope sensed angular rate.
2246      *
2247      * @return estimated bias of gyroscope sensed angular rate.
2248      */
2249     public AngularSpeedTriad getBiasAngularRate() {
2250         return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
2251                 biasAngularRateX, biasAngularRateY, biasAngularRateZ);
2252     }
2253 
2254     /**
2255      * Gets estimated bias of gyroscope sensed angular rate.
2256      *
2257      * @param result instance where bias of gyroscope sensed angular
2258      *               rate will be stored.
2259      */
2260     public void getBiasAngularRate(final AngularSpeedTriad result) {
2261         result.setValueCoordinatesAndUnit(biasAngularRateX, biasAngularRateY, biasAngularRateZ,
2262                 AngularSpeedUnit.RADIANS_PER_SECOND);
2263     }
2264 
2265     /**
2266      * Gets body kinematics containing estimated bias values for accelerometer
2267      * and gyroscope.
2268      *
2269      * @return body kinematics containing estimated bias values.
2270      */
2271     public BodyKinematics getBiasesAsBodyKinematics() {
2272         final var result = new BodyKinematics();
2273         getBiasesAsBodyKinematics(result);
2274         return result;
2275     }
2276 
2277     /**
2278      * Gets body kinematics containing estimated bias values for accelerometer
2279      * and gyroscope.
2280      *
2281      * @param result instance where body kinematics containing estimated bias
2282      *               values will be stored.
2283      */
2284     public void getBiasesAsBodyKinematics(final BodyKinematics result) {
2285         result.setSpecificForceCoordinates(biasFx, biasFy, biasFz);
2286         result.setAngularRateCoordinates(biasAngularRateX, biasAngularRateY, biasAngularRateZ);
2287     }
2288 
2289     /**
2290      * Gets estimated variance of x coordinate of accelerometer sensed specific
2291      * force expressed in (m^2/s^4).
2292      *
2293      * @return estimated variance of x coordinate of sensed specific force.
2294      */
2295     public double getVarianceFx() {
2296         return varianceFx;
2297     }
2298 
2299     /**
2300      * Gets estimated variance of y coordinate of accelerometer sensed specific
2301      * force expressed in (m^2/s^4).
2302      *
2303      * @return estimated variance of y coordinate of sensed specific force.
2304      */
2305     public double getVarianceFy() {
2306         return varianceFy;
2307     }
2308 
2309     /**
2310      * Gets estimated variance of z coordinate of accelerometer sensed specific
2311      * force expressed in (m^2/s^4).
2312      *
2313      * @return estimated variance of z coordinate of sensed specific force.
2314      */
2315     public double getVarianceFz() {
2316         return varianceFz;
2317     }
2318 
2319     /**
2320      * Gets estimated variance of x coordinate of gyroscope sensed angular rate
2321      * expressed in (rad^2/s^2).
2322      *
2323      * @return estimated variance of x coordinate of sensed angular rate.
2324      */
2325     public double getVarianceAngularRateX() {
2326         return varianceAngularRateX;
2327     }
2328 
2329     /**
2330      * Gets estimated variance of y coordinate of gyroscope sensed angular rate
2331      * expressed in (rad^2/s^2).
2332      *
2333      * @return estimated variance of y coordinate of sensed angular rate.
2334      */
2335     public double getVarianceAngularRateY() {
2336         return varianceAngularRateY;
2337     }
2338 
2339     /**
2340      * Gets estimated variance of z coordinate of gyroscope sensed angular rate
2341      * expressed in (rad^2/s^2).
2342      *
2343      * @return estimated variance of z coordinate of sensed angular rate.
2344      */
2345     public double getVarianceAngularRateZ() {
2346         return varianceAngularRateZ;
2347     }
2348 
2349     /**
2350      * Gets estimated standard deviation of x coordinate of accelerometer
2351      * sensed specific force expressed in (m/s^2).
2352      *
2353      * @return estimated standard deviation of x coordinate of sensed specific force.
2354      */
2355     public double getStandardDeviationFx() {
2356         return Math.sqrt(varianceFx);
2357     }
2358 
2359     /**
2360      * Gets estimated standard deviation of x coordinate of accelerometer
2361      * sensed specific force.
2362      *
2363      * @return estimated standard deviation of x coordinate of sensed specific force.
2364      */
2365     public Acceleration getStandardDeviationFxAsAcceleration() {
2366         return new Acceleration(getStandardDeviationFx(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
2367     }
2368 
2369     /**
2370      * Gets estimated standard deviation of x coordinate of accelerometer
2371      * sensed specific force.
2372      *
2373      * @param result instance where estimated standard deviation of x coordinate
2374      *               of sensed specific force will be stored.
2375      */
2376     public void getStandardDeviationFxAsAcceleration(final Acceleration result) {
2377         result.setValue(getStandardDeviationFx());
2378         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2379     }
2380 
2381     /**
2382      * Gets estimated standard deviation of y coordinate of accelerometer
2383      * sensed specific force expressed in (m/s^2).
2384      *
2385      * @return estimated standard deviation of y coordinate of sensed specific
2386      * force.
2387      */
2388     public double getStandardDeviationFy() {
2389         return Math.sqrt(varianceFy);
2390     }
2391 
2392     /**
2393      * Gets estimated standard deviation of y coordinate of accelerometer
2394      * sensed specific force.
2395      *
2396      * @return estimated standard deviation of y coordinate of sensed specific
2397      * force.
2398      */
2399     public Acceleration getStandardDeviationFyAsAcceleration() {
2400         return new Acceleration(getStandardDeviationFy(),
2401                 AccelerationUnit.METERS_PER_SQUARED_SECOND);
2402     }
2403 
2404     /**
2405      * Gets estimated standard deviation of y coordinate of accelerometer
2406      * sensed specific force.
2407      *
2408      * @param result instance where estimated standard deviation of y coordinate
2409      *               of sensed specific force will be stored.
2410      */
2411     public void getStandardDeviationFyAsAcceleration(final Acceleration result) {
2412         result.setValue(getStandardDeviationFy());
2413         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2414     }
2415 
2416     /**
2417      * Gets estimated standard deviation of z coordinate of accelerometer
2418      * sensed specific force expressed in (m/s^2).
2419      *
2420      * @return estimated standard deviation of z coordinate of sensed specific
2421      * force.
2422      */
2423     public double getStandardDeviationFz() {
2424         return Math.sqrt(varianceFz);
2425     }
2426 
2427     /**
2428      * Gets estimated standard deviation of z coordinate of accelerometer
2429      * sensed specific force.
2430      *
2431      * @return estimated standard deviation of z coordinate of sensed specific
2432      * force.
2433      */
2434     public Acceleration getStandardDeviationFzAsAcceleration() {
2435         return new Acceleration(getStandardDeviationFz(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
2436     }
2437 
2438     /**
2439      * Gets estimated standard deviation of z coordinate of accelerometer
2440      * sensed specific force.
2441      *
2442      * @param result instance where estimated standard deviation of z coordinate
2443      *               of sensed specific force will be stored.
2444      */
2445     public void getStandardDeviationFzAsAcceleration(final Acceleration result) {
2446         result.setValue(getStandardDeviationFz());
2447         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2448     }
2449 
2450     /**
2451      * Gets estimated standard deviation of accelerometer sensed
2452      * specific force.
2453      *
2454      * @return estimated standard deviation of accelerometer
2455      */
2456     public AccelerationTriad getStandardDeviationF() {
2457         return new AccelerationTriad(AccelerationUnit.METERS_PER_SQUARED_SECOND,
2458                 getStandardDeviationFx(),
2459                 getStandardDeviationFy(),
2460                 getStandardDeviationFz());
2461     }
2462 
2463     /**
2464      * Gets estimated standard deviation of accelerometer sensed
2465      * specific force.
2466      *
2467      * @param result instance where estimated standard deviation of
2468      *               accelerometer will be stored.
2469      */
2470     public void getStandardDeviationF(final AccelerationTriad result) {
2471         result.setValueCoordinatesAndUnit(getStandardDeviationFx(), getStandardDeviationFy(), getStandardDeviationFz(),
2472                 AccelerationUnit.METERS_PER_SQUARED_SECOND);
2473     }
2474 
2475     /**
2476      * Gets average of estimated standard deviation of accelerometer sensed specific
2477      * force for all coordinates expressed in meters per squared second (m/s^2).
2478      *
2479      * @return average of estimated standard deviation of accelerometer.
2480      */
2481     public double getAverageAccelerometerStandardDeviation() {
2482         return (getStandardDeviationFx() + getStandardDeviationFy() + getStandardDeviationFz()) / 3.0;
2483     }
2484 
2485     /**
2486      * Gets average of estimated standard deviation of accelerometer sensed specific
2487      * force for all coordinates.
2488      *
2489      * @return average of estimated standard deviation of accelerometer.
2490      */
2491     public Acceleration getAverageAccelerometerStandardDeviationAsAcceleration() {
2492         return new Acceleration(getAverageAccelerometerStandardDeviation(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
2493     }
2494 
2495     /**
2496      * Gets average of estimated standard deviation of accelerometer sensed specific
2497      * force for all coordinates.
2498      *
2499      * @param result instance where result data will be copied to.
2500      */
2501     public void getAverageAccelerometerStandardDeviationAsAcceleration(final Acceleration result) {
2502         result.setValue(getAverageAccelerometerStandardDeviation());
2503         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
2504     }
2505 
2506     /**
2507      * Gets estimated standard deviation of x coordinate of gyroscope sensed angular
2508      * rate expressed in (rad/s).
2509      *
2510      * @return estimated standard deviation of x coordinate of sensed angular rate.
2511      */
2512     public double getStandardDeviationAngularRateX() {
2513         return Math.sqrt(varianceAngularRateX);
2514     }
2515 
2516     /**
2517      * Gets estimated standard deviation of x coordinate of gyroscope sensed angular
2518      * rate.
2519      *
2520      * @return estimated standard deviation of x coordinate of sensed angular rate.
2521      */
2522     public AngularSpeed getStandardDeviationAngularRateXAsAngularSpeed() {
2523         return new AngularSpeed(getStandardDeviationAngularRateX(), AngularSpeedUnit.RADIANS_PER_SECOND);
2524     }
2525 
2526     /**
2527      * Gets estimated standard deviation of x coordinate of gyroscope sensed angular
2528      * rate.
2529      *
2530      * @param result instance where estimated standard deviation of x coordinate of
2531      *               sensed angular rate will be stored.
2532      */
2533     public void getStandardDeviationAngularRateXAsAngularSpeed(final AngularSpeed result) {
2534         result.setValue(getStandardDeviationAngularRateX());
2535         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2536     }
2537 
2538     /**
2539      * Gets estimated standard deviation of y coordinate of gyroscope sensed angular
2540      * rate expressed in (rad/s).
2541      *
2542      * @return estimated standard deviation of y coordinate of sensed angular rate.
2543      */
2544     public double getStandardDeviationAngularRateY() {
2545         return Math.sqrt(varianceAngularRateY);
2546     }
2547 
2548     /**
2549      * Gets estimated standard deviation of y coordinate of gyroscope sensed angular
2550      * rate.
2551      *
2552      * @return estimated standard deviation of y coordinate of sensed angular rate.
2553      */
2554     public AngularSpeed getStandardDeviationAngularRateYAsAngularSpeed() {
2555         return new AngularSpeed(getStandardDeviationAngularRateY(), AngularSpeedUnit.RADIANS_PER_SECOND);
2556     }
2557 
2558     /**
2559      * Gets estimated standard deviation of y coordinate of gyroscope sensed angular
2560      * rate.
2561      *
2562      * @param result instance where estimated standard deviation of y coordinate of
2563      *               sensed angular rate will be stored.
2564      */
2565     public void getStandardDeviationAngularRateYAsAngularSpeed(final AngularSpeed result) {
2566         result.setValue(getStandardDeviationAngularRateY());
2567         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2568     }
2569 
2570     /**
2571      * Gets estimated standard deviation of z coordinate of gyroscope sensed angular
2572      * rate expressed in (rad/s).
2573      *
2574      * @return estimated standard deviation of z coordinate of sensed angular rate.
2575      */
2576     public double getStandardDeviationAngularRateZ() {
2577         return Math.sqrt(varianceAngularRateZ);
2578     }
2579 
2580     /**
2581      * Gets estimated standard deviation of z coordinate of gyroscope sensed angular
2582      * rate.
2583      *
2584      * @return estimated standard deviation of z coordinate of sensed angular rate.
2585      */
2586     public AngularSpeed getStandardDeviationAngularRateZAsAngularSpeed() {
2587         return new AngularSpeed(getStandardDeviationAngularRateZ(), AngularSpeedUnit.RADIANS_PER_SECOND);
2588     }
2589 
2590     /**
2591      * Gets estimated standard deviation of z coordinate of gyroscope sensed angular
2592      * rate.
2593      *
2594      * @param result instance where estimated standard deviation of z coordinate of
2595      *               sensed angular rate will be stored.
2596      */
2597     public void getStandardDeviationAngularRateZAsAngularSpeed(final AngularSpeed result) {
2598         result.setValue(getStandardDeviationAngularRateZ());
2599         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2600     }
2601 
2602     /**
2603      * Gets estimated standard deviation of sensed angular rate.
2604      *
2605      * @return estimated standard deviation of sensed angular rate.
2606      */
2607     public AngularSpeedTriad getStandardDeviationAngularRate() {
2608         return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
2609                 getStandardDeviationAngularRateX(),
2610                 getStandardDeviationAngularRateY(),
2611                 getStandardDeviationAngularRateZ());
2612     }
2613 
2614     /**
2615      * Gets estimated standard deviation of sensed angular rate.
2616      *
2617      * @param result instance where estimated standard deviation of
2618      *               sensed angular rate.
2619      */
2620     public void getStandardDeviationAngularRate(final AngularSpeedTriad result) {
2621         result.setValueCoordinatesAndUnit(getStandardDeviationAngularRateX(),
2622                 getStandardDeviationAngularRateY(),
2623                 getStandardDeviationAngularRateZ(),
2624                 AngularSpeedUnit.RADIANS_PER_SECOND);
2625     }
2626 
2627     /**
2628      * Gets average of estimated standard deviation of gyroscope sensed angular rate
2629      * for all coordinates expressed in radians per second (rad/s).
2630      *
2631      * @return average of estimated standard deviation of gyroscope.
2632      */
2633     public double getAverageGyroscopeStandardDeviation() {
2634         return (getStandardDeviationAngularRateX() + getStandardDeviationAngularRateY()
2635                 + getStandardDeviationAngularRateZ()) / 3.0;
2636     }
2637 
2638     /**
2639      * Gets average of estimated standard deviation of gyroscope sensed angular rate
2640      * for all coordinates.
2641      *
2642      * @return average of estimated standard deviation of gyroscope.
2643      */
2644     public AngularSpeed getAverageGyroscopeStandardDeviationAsAngularSpeed() {
2645         return new AngularSpeed(getAverageGyroscopeStandardDeviation(), AngularSpeedUnit.RADIANS_PER_SECOND);
2646     }
2647 
2648     /**
2649      * Gets average of estimated standard deviation of gyroscope sensed angular rate
2650      * for all coordinates.
2651      *
2652      * @param result instance where result data will be copied to.
2653      */
2654     public void getAverageGyroscopeStandardDeviationAsAngularSpeed(final AngularSpeed result) {
2655         result.setValue(getAverageGyroscopeStandardDeviation());
2656         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
2657     }
2658 
2659     /**
2660      * Gets estimated standard deviations of accelerometer and gyroscope components
2661      * as a body kinematics instance.
2662      *
2663      * @return a body kinematics instance containing standard deviation values.
2664      */
2665     public BodyKinematics getStandardDeviationsAsBodyKinematics() {
2666         return new BodyKinematics(getStandardDeviationFx(),
2667                 getStandardDeviationFy(),
2668                 getStandardDeviationFz(),
2669                 getStandardDeviationAngularRateX(),
2670                 getStandardDeviationAngularRateY(),
2671                 getStandardDeviationAngularRateZ());
2672     }
2673 
2674     /**
2675      * Gets estimated standard deviations of accelerometer and gyroscope components
2676      * as a body kinematics instance.
2677      *
2678      * @param result instance where data will be stored.
2679      */
2680     public void getStandardDeviationsAsBodyKinematics(final BodyKinematics result) {
2681         result.setSpecificForceCoordinates(getStandardDeviationFx(),
2682                 getStandardDeviationFy(), getStandardDeviationFz());
2683         result.setAngularRateCoordinates(getStandardDeviationAngularRateX(),
2684                 getStandardDeviationAngularRateY(),
2685                 getStandardDeviationAngularRateZ());
2686     }
2687 
2688     /**
2689      * Gets accelerometer noise PSD (Power Spectral Density) on x axis expressed
2690      * in (m^2 * s^-3).
2691      *
2692      * @return accelerometer noise PSD on x axis.
2693      */
2694     public double getPSDFx() {
2695         return varianceFx * timeInterval;
2696     }
2697 
2698     /**
2699      * Gets accelerometer noise PSD (Power Spectral Density) on y axis expressed
2700      * in (m^2 * s^-3).
2701      *
2702      * @return accelerometer noise PSD on y axis.
2703      */
2704     public double getPSDFy() {
2705         return varianceFy * timeInterval;
2706     }
2707 
2708     /**
2709      * Gets accelerometer noise PSD (Power Spectral Density) on z axis expressed
2710      * in (m^2 * s^-3).
2711      *
2712      * @return accelerometer noise PSD on z axis.
2713      */
2714     public double getPSDFz() {
2715         return varianceFz * timeInterval;
2716     }
2717 
2718     /**
2719      * Gets gyroscope noise PSD (Power Spectral Density) on x axis expressed
2720      * in (rad^2/s).
2721      *
2722      * @return gyroscope noise PSD on x axis.
2723      */
2724     public double getPSDAngularRateX() {
2725         return varianceAngularRateX * timeInterval;
2726     }
2727 
2728     /**
2729      * Gets gyroscope noise PSD (Power Spectral Density) on y axis expressed
2730      * in (rad^2/s).
2731      *
2732      * @return gyroscope noise PSD on y axis.
2733      */
2734     public double getPSDAngularRateY() {
2735         return varianceAngularRateY * timeInterval;
2736     }
2737 
2738     /**
2739      * Gets gyroscope noise PSD (Power Spectral Density) on z axis expressed
2740      * in (rad^2/s).
2741      *
2742      * @return gyroscope noise PSD on z axis.
2743      */
2744     public double getPSDAngularRateZ() {
2745         return varianceAngularRateZ * timeInterval;
2746     }
2747 
2748     /**
2749      * Gets accelerometer noise root PSD (Power Spectral Density) on x axis
2750      * expressed in (m * s^-1.5).
2751      *
2752      * @return accelerometer noise root PSD on x axis.
2753      */
2754     public double getRootPSDFx() {
2755         return Math.sqrt(getPSDFx());
2756     }
2757 
2758     /**
2759      * Gets accelerometer noise root PSD (Power Spectral Density) on y axis
2760      * expressed in (m * s^-1.5).
2761      *
2762      * @return accelerometer noise root PSD on y axis.
2763      */
2764     public double getRootPSDFy() {
2765         return Math.sqrt(getPSDFy());
2766     }
2767 
2768     /**
2769      * Gets accelerometer noise root PSD (Power Spectral Density) on z axis
2770      * expressed in (m * s^-1.5).
2771      *
2772      * @return accelerometer noise root PSD on z axis.
2773      */
2774     public double getRootPSDFz() {
2775         return Math.sqrt(getPSDFz());
2776     }
2777 
2778     /**
2779      * Gets gyroscope noise root PSD (Power Spectral Density) on x axis
2780      * expressed in (rad * s^-0.5).
2781      *
2782      * @return gyroscope noise root PSD on x axis.
2783      */
2784     public double getRootPSDAngularRateX() {
2785         return Math.sqrt(getPSDAngularRateX());
2786     }
2787 
2788     /**
2789      * Gets gyroscope noise root PSD (Power Spectral Density) on y axis
2790      * expressed in (rad * s^-0.5).
2791      *
2792      * @return gyroscope noise root PSD on y axis.
2793      */
2794     public double getRootPSDAngularRateY() {
2795         return Math.sqrt(getPSDAngularRateY());
2796     }
2797 
2798     /**
2799      * Gets gyroscope noise root PSD (Power Spectral Density) on z axis
2800      * expressed in (rad * s^-0.5).
2801      *
2802      * @return gyroscope noise root PSD on z axis.
2803      */
2804     public double getRootPSDAngularRateZ() {
2805         return Math.sqrt(getPSDAngularRateZ());
2806     }
2807 
2808     /**
2809      * Gets average accelerometer noise PSD (Power Spectral Density) among
2810      * x,y,z components expressed as (m^2/s^3).
2811      *
2812      * @return average accelerometer noise PSD.
2813      */
2814     public double getAccelerometerNoisePSD() {
2815         return (getPSDFx() + getPSDFy() + getPSDFz()) / 3.0;
2816     }
2817 
2818     /**
2819      * Gets accelerometer noise root PSD (Power Spectral Density) which is the
2820      * norm of root PSD components expressed as (m * s^-1.5).
2821      *
2822      * @return average accelerometer noise root PSD.
2823      */
2824     public double getAccelerometerNoiseRootPSD() {
2825         return Math.sqrt(getPSDFx() + getPSDFy() + getPSDFz());
2826     }
2827 
2828     /**
2829      * Gets average gyroscope noise PSD (Power Spectral Density) among
2830      * x,y,z components expressed in (rad^2/s).
2831      *
2832      * @return average gyroscope noise PSD.
2833      */
2834     public double getGyroNoisePSD() {
2835         return (getPSDAngularRateX() + getPSDAngularRateY() + getPSDAngularRateZ()) / 3.0;
2836     }
2837 
2838     /**
2839      * Gets gyroscope noise root PSD (Power Spectral Density) which is the
2840      * norm of root PSD components expressed in (rad * s^-0.5).
2841      *
2842      * @return average gyroscope noise root PSD.
2843      */
2844     public double getGyroNoiseRootPSD() {
2845         return Math.sqrt(getPSDAngularRateX() + getPSDAngularRateY() + getPSDAngularRateZ());
2846     }
2847 
2848     /**
2849      * Gets estimated bias of accelerometer sensed specific force
2850      * expressed in meters per squared second (m/s^2) as a 3x1 matrix column vector.
2851      *
2852      * @return estimated bias of accelerometer sensed specific force.
2853      */
2854     public Matrix getAccelerometerBias() {
2855         Matrix result;
2856         try {
2857             result = new Matrix(BodyKinematics.COMPONENTS, 1);
2858             getAccelerometerBias(result);
2859         } catch (final WrongSizeException ignore) {
2860             // never happens
2861             result = null;
2862         }
2863 
2864         return result;
2865     }
2866 
2867     /**
2868      * Gets estimated bias of accelerometer sensed specific force
2869      * expressed in meters per squared second (m/s^2) as a 3x1 matrix column vector.
2870      *
2871      * @param result instance where data will be copied to. Must be 3x1.
2872      * @throws IllegalArgumentException if provided result matrix is not 3x1.
2873      */
2874     public void getAccelerometerBias(final Matrix result) {
2875         if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != 1) {
2876             throw new IllegalArgumentException();
2877         }
2878 
2879         result.setElementAtIndex(0, biasFx);
2880         result.setElementAtIndex(1, biasFy);
2881         result.setElementAtIndex(2, biasFz);
2882     }
2883 
2884     /**
2885      * Gets estimated bias of gyroscope sensed angular rates
2886      * expressed in radians per second (rad/s) as a 3x1 matrix column vector.
2887      *
2888      * @return estimated bias of gyroscope sensed angular rates.
2889      */
2890     public Matrix getGyroBias() {
2891         Matrix result;
2892         try {
2893             result = new Matrix(BodyKinematics.COMPONENTS, 1);
2894             getGyroBias(result);
2895         } catch (final WrongSizeException ignore) {
2896             // never happens
2897             result = null;
2898         }
2899 
2900         return result;
2901     }
2902 
2903     /**
2904      * Gets estimated bias of gyroscope sensed angular rates
2905      * expressed in radians per second (rad/s) as a 3x1 matrix column vector.
2906      *
2907      * @param result instance where data will be copied to. Must be 3x1.
2908      * @throws IllegalArgumentException if provided result matrix is not 3x1.
2909      */
2910     public void getGyroBias(final Matrix result) {
2911         if (result.getRows() != BodyKinematics.COMPONENTS || result.getColumns() != 1) {
2912             throw new IllegalArgumentException();
2913         }
2914 
2915         result.setElementAtIndex(0, biasAngularRateX);
2916         result.setElementAtIndex(1, biasAngularRateY);
2917         result.setElementAtIndex(2, biasAngularRateZ);
2918     }
2919 
2920     /**
2921      * Gets number of samples that have been processed so far.
2922      *
2923      * @return number of samples that have been processed so far.
2924      */
2925     public int getNumberOfProcessedSamples() {
2926         return numberOfProcessedSamples;
2927     }
2928 
2929     /**
2930      * Gets amount of total elapsed time since first processed measurement expressed
2931      * in seconds (s).
2932      *
2933      * @return amount of total elapsed time.
2934      */
2935     public double getElapsedTimeSeconds() {
2936         return numberOfProcessedSamples * timeInterval;
2937     }
2938 
2939     /**
2940      * Gets amount of total elapsed time since first processed measurement.
2941      *
2942      * @return amount of total elapsed time.
2943      */
2944     public Time getElapsedTime() {
2945         return new Time(getElapsedTimeSeconds(), TimeUnit.SECOND);
2946     }
2947 
2948     /**
2949      * Gets amount of total elapsed time since first processed measurement.
2950      *
2951      * @param result instance where result will be stored.
2952      */
2953     public void getElapsedTime(final Time result) {
2954         result.setValue(getElapsedTimeSeconds());
2955         result.setUnit(TimeUnit.SECOND);
2956     }
2957 
2958     /**
2959      * Indicates whether estimator is currently running or not.
2960      *
2961      * @return true if estimator is running, false otherwise.
2962      */
2963     public boolean isRunning() {
2964         return running;
2965     }
2966 
2967     /**
2968      * Gets theoretically expected body kinematics for provided body position and
2969      * orientation, and provided time interval, assuming that body remains at the
2970      * same position (zero velocity).
2971      * When body remains static, sensed specific force and angular rates will remain
2972      * constant due to gravity and Earth rotation.
2973      *
2974      * @return expected body kinematics.
2975      */
2976     public BodyKinematics getExpectedKinematics() {
2977         return new BodyKinematics(expectedKinematics);
2978     }
2979 
2980     /**
2981      * Gets theoretically expected body kinematics for provided body position and
2982      * orientation, and provided time interval, assuming that body remains at the
2983      * same position (zero velocity).
2984      * When body remains static, sensed specific force and angular rates will remain
2985      * constant due to gravity and Earth rotation.
2986      *
2987      * @param result instance where expected body kinematics will be stored.
2988      */
2989     public void getExpectedKinematics(final BodyKinematics result) {
2990         expectedKinematics.copyTo(result);
2991     }
2992 
2993     /**
2994      * Adds a sample of body kinematics (accelerometer + gyroscope readings) obtained
2995      * from an IMU.
2996      *
2997      * @param kinematics kinematics instance to be added and processed.
2998      * @throws LockedException if estimator is currently running.
2999      */
3000     public void addBodyKinematics(final BodyKinematics kinematics) throws LockedException {
3001         if (running) {
3002             throw new LockedException();
3003         }
3004 
3005         running = true;
3006 
3007         if (lastBodyKinematics == null && listener != null) {
3008             listener.onStart(this);
3009         }
3010 
3011         final var fx = kinematics.getFx();
3012         final var fy = kinematics.getFy();
3013         final var fz = kinematics.getFz();
3014         final var angularRateX = kinematics.getAngularRateX();
3015         final var angularRateY = kinematics.getAngularRateY();
3016         final var angularRateZ = kinematics.getAngularRateZ();
3017 
3018         final var expectedFx = expectedKinematics.getFx();
3019         final var expectedFy = expectedKinematics.getFy();
3020         final var expectedFz = expectedKinematics.getFz();
3021         final var expectedAngularRateX = expectedKinematics.getAngularRateX();
3022         final var expectedAngularRateY = expectedKinematics.getAngularRateY();
3023         final var expectedAngularRateZ = expectedKinematics.getAngularRateZ();
3024 
3025         final var diffFx = fx - expectedFx;
3026         final var diffFy = fy - expectedFy;
3027         final var diffFz = fz - expectedFz;
3028         final var diffAngularRateX = angularRateX - expectedAngularRateX;
3029         final var diffAngularRateY = angularRateY - expectedAngularRateY;
3030         final var diffAngularRateZ = angularRateZ - expectedAngularRateZ;
3031 
3032         // compute biases
3033         final var tmp = (double) numberOfProcessedSamples / (double) numberOfProcessedSamplesPlusOne;
3034         biasFx = biasFx * tmp + diffFx / numberOfProcessedSamplesPlusOne;
3035         biasFy = biasFy * tmp + diffFy / numberOfProcessedSamplesPlusOne;
3036         biasFz = biasFz * tmp + diffFz / numberOfProcessedSamplesPlusOne;
3037 
3038         biasAngularRateX = biasAngularRateX * tmp + diffAngularRateX / numberOfProcessedSamplesPlusOne;
3039         biasAngularRateY = biasAngularRateY * tmp + diffAngularRateY / numberOfProcessedSamplesPlusOne;
3040         biasAngularRateZ = biasAngularRateZ * tmp + diffAngularRateZ / numberOfProcessedSamplesPlusOne;
3041 
3042         // compute variances
3043         final var diffBiasFx = diffFx - biasFx;
3044         final var diffBiasFy = diffFy - biasFy;
3045         final var diffBiasFz = diffFz - biasFz;
3046         final var diffBiasAngularRateX = diffAngularRateX - biasAngularRateX;
3047         final var diffBiasAngularRateY = diffAngularRateY - biasAngularRateY;
3048         final var diffBiasAngularRateZ = diffAngularRateZ - biasAngularRateZ;
3049 
3050         final var diffBiasFx2 = diffBiasFx * diffBiasFx;
3051         final var diffBiasFy2 = diffBiasFy * diffBiasFy;
3052         final var diffBiasFz2 = diffBiasFz * diffBiasFz;
3053         final var diffBiasAngularRateX2 = diffBiasAngularRateX * diffBiasAngularRateX;
3054         final var diffBiasAngularRateY2 = diffBiasAngularRateY * diffBiasAngularRateY;
3055         final var diffBiasAngularRateZ2 = diffBiasAngularRateZ * diffBiasAngularRateZ;
3056 
3057         varianceFx = varianceFx * tmp + diffBiasFx2 / numberOfProcessedSamplesPlusOne;
3058         varianceFy = varianceFy * tmp + diffBiasFy2 / numberOfProcessedSamplesPlusOne;
3059         varianceFz = varianceFz * tmp + diffBiasFz2 / numberOfProcessedSamplesPlusOne;
3060 
3061         varianceAngularRateX = varianceAngularRateX * tmp + diffBiasAngularRateX2 / numberOfProcessedSamplesPlusOne;
3062         varianceAngularRateY = varianceAngularRateY * tmp + diffBiasAngularRateY2 / numberOfProcessedSamplesPlusOne;
3063         varianceAngularRateZ = varianceAngularRateZ * tmp + diffBiasAngularRateZ2 / numberOfProcessedSamplesPlusOne;
3064 
3065         lastBodyKinematics = kinematics;
3066 
3067         numberOfProcessedSamples++;
3068         numberOfProcessedSamplesPlusOne++;
3069 
3070         if (listener != null) {
3071             listener.onBodyKinematicsAdded(this);
3072         }
3073 
3074         running = false;
3075     }
3076 
3077     /**
3078      * Resets current estimator.
3079      *
3080      * @return true if estimator was successfully reset, false if no reset was needed.
3081      * @throws LockedException if estimator is currently running.
3082      */
3083     public boolean reset() throws LockedException {
3084         if (running) {
3085             throw new LockedException();
3086         }
3087 
3088         if (numberOfProcessedSamples == 0) {
3089             return false;
3090         }
3091 
3092         running = true;
3093         lastBodyKinematics = null;
3094         biasFx = 0.0;
3095         biasFy = 0.0;
3096         biasFz = 0.0;
3097         biasAngularRateX = 0.0;
3098         biasAngularRateY = 0.0;
3099         biasAngularRateZ = 0.0;
3100         varianceFx = 0.0;
3101         varianceFy = 0.0;
3102         varianceFz = 0.0;
3103         varianceAngularRateX = 0.0;
3104         varianceAngularRateY = 0.0;
3105         varianceAngularRateZ = 0.0;
3106         numberOfProcessedSamples = 0;
3107         numberOfProcessedSamplesPlusOne = 1;
3108 
3109         if (listener != null) {
3110             listener.onReset(this);
3111         }
3112 
3113         running = false;
3114 
3115         return true;
3116     }
3117 
3118     /**
3119      * Converts provided time instance to seconds.
3120      *
3121      * @param time instance to be converted.
3122      * @return obtained conversion in seconds.
3123      */
3124     private static double convertTime(final Time time) {
3125         return TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(), TimeUnit.SECOND);
3126     }
3127 
3128     /**
3129      * Rebuilds expected theoretical kinematics for provided body position
3130      * and orientation and provided time interval, assuming that body
3131      * remains at the same position (zero velocity).
3132      * When body remains static, sensed specific force and angular rates will remain
3133      * constant due to gravity and Earth rotation.
3134      */
3135     private void rebuildExpectedKinematics() {
3136         if (frame == null) {
3137             return;
3138         }
3139         if (expectedKinematics == null) {
3140             expectedKinematics = new BodyKinematics();
3141         }
3142 
3143         final var ecefC = getEcefC();
3144         final var x = frame.getX();
3145         final var y = frame.getY();
3146         final var z = frame.getZ();
3147         ECEFKinematicsEstimator.estimateKinematics(timeInterval, ecefC, ecefC,
3148                 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
3149                 x, y, z, expectedKinematics);
3150     }
3151 }