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 }