View Javadoc
1   /*
2    * Copyright (C) 2016 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.ar.slam;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.geometry.Point3D;
20  import com.irurueta.geometry.Quaternion;
21  import com.irurueta.statistics.MultivariateNormalDist;
22  
23  import java.io.Serializable;
24  
25  /**
26   * Base class to estimate position, velocity, acceleration and orientation of
27   * a device using sensor data such as accelerometers and gyroscopes.
28   * Implementations of this class are designed taking into account sensors
29   * available on Android devices.
30   *
31   * @param <D> calibrator type associated to implementations of SLAM calibration
32   *            data.
33   */
34  @SuppressWarnings("DuplicatedCode")
35  public abstract class BaseSlamEstimator<D extends BaseCalibrationData> implements Serializable {
36  
37      /**
38       * Number of components in 3D.
39       */
40      protected static final int N_COMPONENTS_3D = 3;
41  
42      /**
43       * Conversion of nanoseconds to milliseconds.
44       */
45      protected static final double NANOS_TO_SECONDS = 1e-9;
46  
47      /**
48       * Indicates whether sample accumulation must be enabled or not.
49       */
50      protected static final boolean DEFAULT_ENABLE_SAMPLE_ACCUMULATION = true;
51  
52      /**
53       * Current position of the device along x-axis expressed in meters (m).
54       */
55      protected double statePositionX;
56  
57      /**
58       * Current position of the device along y-axis expressed in meters (m).
59       */
60      protected double statePositionY;
61  
62      /**
63       * Current position of the device along z-axis expressed in meters (m).
64       */
65      protected double statePositionZ;
66  
67      /**
68       * Current linear velocity of the device along x-axis expressed in meters
69       * per second (m/s).
70       */
71      protected double stateVelocityX;
72  
73      /**
74       * Current linear velocity of the device along y-axis expressed in meters
75       * per second (m/s).
76       */
77      protected double stateVelocityY;
78  
79      /**
80       * Current linear velocity of the device along z-axis expressed in meters
81       * per second (m/s).
82       */
83      protected double stateVelocityZ;
84  
85      /**
86       * Current linear acceleration of the device along x-axis expressed in
87       * meters per squared second (m/s^2).
88       */
89      protected double stateAccelerationX;
90  
91      /**
92       * Current linear acceleration of the device along y-axis expressed in
93       * meters per squared second (m/s^2).
94       */
95      protected double stateAccelerationY;
96  
97      /**
98       * Current linear acceleration of the device along z-axis expressed in
99       * meters per squared second (m/s^2).
100      */
101     protected double stateAccelerationZ;
102 
103     /**
104      * A value of quaternion containing current device orientation.
105      */
106     protected double stateQuaternionA;
107 
108     /**
109      * B value of quaternion containing current device orientation.
110      */
111     protected double stateQuaternionB;
112 
113     /**
114      * C value of quaternion containing current device orientation.
115      */
116     protected double stateQuaternionC;
117 
118     /**
119      * D value of quaternion containing current device orientation.
120      */
121     protected double stateQuaternionD;
122 
123     /**
124      * Angular speed of rotation of the device along x-axis expressed in radians
125      * per second (rad/s).
126      */
127     protected double stateAngularSpeedX;
128 
129     /**
130      * Angular speed of rotation of the device along y-axis expressed in radians
131      * per second (rad/s).
132      */
133     protected double stateAngularSpeedY;
134 
135     /**
136      * Angular speed of rotation of the device along z-axis expressed in radians
137      * per second (rad/s).
138      */
139     protected double stateAngularSpeedZ;
140 
141     /**
142      * Indicates whether an error occurred during the estimation.
143      * If an error occurs the estimator should be restarted since state values
144      * might become unreliable.
145      */
146     protected boolean error;
147 
148     /**
149      * Indicates whether accumulation of samples is enabled or not.
150      */
151     protected boolean accumulationEnabled = DEFAULT_ENABLE_SAMPLE_ACCUMULATION;
152 
153     /**
154      * Timestamp expressed in nanoseconds since the epoch time of the last
155      * accelerometer sample.
156      */
157     protected long accelerometerTimestampNanos = -1;
158 
159     /**
160      * Timestamp expressed in nanoseconds since the epoch time of the last
161      * gyroscope sample.
162      */
163     protected long gyroscopeTimestampNanos = -1;
164 
165     /**
166      * Number of accelerometer samples accumulated since last full sample.
167      */
168     protected int accumulatedAccelerometerSamples = 0;
169 
170     /**
171      * Number of gyroscope samples accumulated since last full sample.
172      */
173     protected int accumulatedGyroscopeSamples = 0;
174 
175     /**
176      * Average of acceleration along x-axis accumulated since last full sample.
177      * Expressed in meters per squared second (m/s^2).
178      */
179     protected double accumulatedAccelerationSampleX;
180 
181     /**
182      * Average of acceleration along y-axis accumulated since last full sample.
183      * Expressed in meters per squared second (m/s^2).
184      */
185     protected double accumulatedAccelerationSampleY;
186 
187     /**
188      * Average of acceleration along z-axis accumulated since last full sample.
189      * Expressed in meters per squared second (m/s^2).
190      */
191     protected double accumulatedAccelerationSampleZ;
192 
193     /**
194      * Average of angular speed along x-axis accumulated since last full sample.
195      * Expressed in radians per second (rad/s).
196      */
197     protected double accumulatedAngularSpeedSampleX;
198 
199     /**
200      * Average of angular speed along y-axis accumulated since last full sample.
201      * Expressed in radians per second (rad/s).
202      */
203     protected double accumulatedAngularSpeedSampleY;
204 
205     /**
206      * Average of angular speed along z-axis accumulated since last full sample.
207      * Expressed in radians per second (red/s).
208      */
209     protected double accumulatedAngularSpeedSampleZ;
210 
211     /**
212      * Listener in charge of handling events raised by instances of this class.
213      */
214     protected transient BaseSlamEstimatorListener<D> listener;
215 
216     /**
217      * Calibration data. When provided, its mean and covariance are used
218      * to correct control samples and adjust process covariance matrix during
219      * Kalman filtering in prediction stage.
220      */
221     protected D calibrationData;
222 
223     /**
224      * Multivariate distribution to be reused during propagation of calibrated
225      * covariance.
226      */
227     protected transient MultivariateNormalDist normalDist;
228 
229     /**
230      * Constructor.
231      */
232     protected BaseSlamEstimator() {
233         reset();
234     }
235 
236     /**
237      * Resets position and timestamp to zero while keeping other state parameters.
238      */
239     public final void resetPosition() {
240         reset(0.0, 0.0, 0.0, stateVelocityX, stateVelocityY,
241                 stateVelocityZ, stateAccelerationX, stateAccelerationY, stateAccelerationZ,
242                 stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD,
243                 stateAngularSpeedX, stateAngularSpeedY, stateAngularSpeedZ);
244     }
245 
246     /**
247      * Resets linear velocity and timestamp to zero while keeping other state parameters.
248      */
249     public final void resetVelocity() {
250         reset(statePositionX, statePositionY, statePositionZ, 0.0, 0.0,
251                 0.0, stateAccelerationX, stateAccelerationY, stateAccelerationZ,
252                 stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD,
253                 stateAngularSpeedX, stateAngularSpeedY, stateAngularSpeedZ);
254     }
255 
256     /**
257      * Resets position, linear velocity and timestamp to zero while keeping other state parameters.
258      */
259     public final void resetPositionAndVelocity() {
260         reset(0.0, 0.0, 0.0, 0.0, 0.0,
261                 0.0, stateAccelerationX, stateAccelerationY, stateAccelerationZ,
262                 stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD,
263                 stateAngularSpeedX, stateAngularSpeedY, stateAngularSpeedZ);
264     }
265 
266     /**
267      * Resets acceleration and timestamp to zero while keeping other state parameters.
268      */
269     public final void resetAcceleration() {
270         reset(statePositionX, statePositionY, statePositionZ,
271                 stateVelocityX, stateVelocityY, stateVelocityZ, 0.0,
272                 0.0, 0.0, stateQuaternionA, stateQuaternionB,
273                 stateQuaternionC, stateQuaternionD, stateAngularSpeedX, stateAngularSpeedY,
274                 stateAngularSpeedZ);
275     }
276 
277     /**
278      * Resets orientation and timestamp to zero while keeping other state parameters.
279      */
280     public final void resetOrientation() {
281         reset(statePositionX, statePositionY, statePositionZ,
282                 stateVelocityX, stateVelocityY, stateVelocityZ,
283                 stateAccelerationX, stateAccelerationY, stateAccelerationZ,
284                 1.0, 0.0, 0.0, 0.0,
285                 stateAngularSpeedX, stateAngularSpeedY, stateAngularSpeedZ);
286     }
287 
288     /**
289      * Resets angular speed and timestamp to zero while keeping other state parameters.
290      */
291     public final void resetAngularSpeed() {
292         reset(statePositionX, statePositionY, statePositionZ,
293                 stateVelocityX, stateVelocityY, stateVelocityZ,
294                 stateAccelerationX, stateAccelerationY, stateAccelerationZ,
295                 stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD,
296                 0.0, 0.0, 0.0);
297     }
298 
299     /**
300      * Resets position, linear velocity, linear acceleration, orientation and
301      * angular speed of the device to zero.
302      */
303     public final void reset() {
304         // NOTE: initial orientation is expressed as quaternion
305         // (1.0, 0.0, 0.0, 0.0) which is equivalent to no rotation.
306         reset(0.0, 0.0, 0.0, 0.0, 0.0,
307                 0.0, 0.0, 0.0, 0.0,
308                 1.0, 0.0, 0.0, 0.0,
309                 0.0, 0.0, 0.0);
310     }
311 
312     /**
313      * Obtains current x-position of the device expressed in meters (m).
314      *
315      * @return x-position of the device expressed in meters (m).
316      */
317     public double getStatePositionX() {
318         return statePositionX;
319     }
320 
321     /**
322      * Obtains current y-position of the device expressed in meters (m).
323      *
324      * @return y-position of the device expressed in meters (m).
325      */
326     public double getStatePositionY() {
327         return statePositionY;
328     }
329 
330     /**
331      * Obtains current z-position of the device expressed in meters (m).
332      *
333      * @return z-position of the device expressed in meters (m).
334      */
335     public double getStatePositionZ() {
336         return statePositionZ;
337     }
338 
339     /**
340      * Gets x,y,z coordinates of the device position expressed in meters (m).
341      *
342      * @return position of the device.
343      */
344     public double[] getStatePosition() {
345         return new double[]{statePositionX, statePositionY, statePositionZ};
346     }
347 
348     /**
349      * Gets x,y,z coordinates of the device position expressed in meters (m) and
350      * stores the result into provided array.
351      *
352      * @param result array where position coordinates will be stored.
353      * @throws IllegalArgumentException if the array does not have length 3.
354      */
355     public void getStatePosition(final double[] result) {
356         if (result.length != N_COMPONENTS_3D) {
357             // result must have length 3
358             throw new IllegalArgumentException();
359         }
360         result[0] = statePositionX;
361         result[1] = statePositionY;
362         result[2] = statePositionZ;
363     }
364 
365     /**
366      * Gets current linear velocity of the device along x-axis expressed in
367      * meters per second (m/s).
368      *
369      * @return current velocity along x-axis expressed in meters per second
370      * (m/s).
371      */
372     public double getStateVelocityX() {
373         return stateVelocityX;
374     }
375 
376     /**
377      * Gets current linear velocity of the device along y-axis expressed in
378      * meters per second (m/s).
379      *
380      * @return current velocity along y-axis expressed in meters per second
381      * (m/s).
382      */
383     public double getStateVelocityY() {
384         return stateVelocityY;
385     }
386 
387     /**
388      * Gets current linear velocity of the device along z-axis expressed in
389      * meters per second (m/s).
390      *
391      * @return current velocity along z-axis expressed in meters per second
392      * (m/s).
393      */
394     public double getStateVelocityZ() {
395         return stateVelocityZ;
396     }
397 
398     /**
399      * Gets x,y,z coordinates of current linear velocity of the device expressed
400      * in meters per second (m/s).
401      *
402      * @return current linear velocity of the device.
403      */
404     public double[] getStateVelocity() {
405         return new double[]{stateVelocityX, stateVelocityY, stateVelocityZ};
406     }
407 
408     /**
409      * Gets x,y,z coordinates of current linear velocity of the device expressed
410      * in meters per second (m/s).
411      *
412      * @param result array where linear velocity of the device will be stored.
413      * @throws IllegalArgumentException if result array does not have length 3.
414      */
415     public void getStateVelocity(final double[] result) {
416         if (result.length != N_COMPONENTS_3D) {
417             // result must have length 3
418             throw new IllegalArgumentException();
419         }
420         result[0] = stateVelocityX;
421         result[1] = stateVelocityY;
422         result[2] = stateVelocityZ;
423     }
424 
425     /**
426      * Gets current linear acceleration of the device along x-axis expressed in
427      * meters per squared second (m/s^2).
428      *
429      * @return linear acceleration of the device along x-axis.
430      */
431     public double getStateAccelerationX() {
432         return stateAccelerationX;
433     }
434 
435     /**
436      * Gets current linear acceleration of the device along y-axis expressed in
437      * meters per squared second (m/s^2).
438      *
439      * @return linear acceleration of the device along y-axis.
440      */
441     public double getStateAccelerationY() {
442         return stateAccelerationY;
443     }
444 
445     /**
446      * Gets current linear acceleration of the device along z-axis expressed in
447      * meters per squared second (m/s^2).
448      *
449      * @return linear acceleration of the device along z-axis.
450      */
451     public double getStateAccelerationZ() {
452         return stateAccelerationZ;
453     }
454 
455     /**
456      * Gets current x,y,z linear acceleration coordinates of the device
457      * expressed in meters per squared second (m/s^2).
458      *
459      * @return current linear acceleration of the device.
460      */
461     public double[] getStateAcceleration() {
462         return new double[]{stateAccelerationX, stateAccelerationY, stateAccelerationZ};
463     }
464 
465     /**
466      * Gets current x,y,z linear acceleration coordinates of the device
467      * expressed in meters per squared second (m/s^2).
468      *
469      * @param result array where resulting linear acceleration coordinates will
470      *               be stored.
471      * @throws IllegalArgumentException if array does not have length 3.
472      */
473     public void getStateAcceleration(final double[] result) {
474         if (result.length != N_COMPONENTS_3D) {
475             // result must have length 3
476             throw new IllegalArgumentException();
477         }
478         result[0] = stateAccelerationX;
479         result[1] = stateAccelerationY;
480         result[2] = stateAccelerationZ;
481     }
482 
483     /**
484      * Gets A value of quaternion containing current device orientation.
485      *
486      * @return A value of quaternion containing current device orientation.
487      */
488     public double getStateQuaternionA() {
489         return stateQuaternionA;
490     }
491 
492     /**
493      * Gets B value of quaternion containing current device orientation.
494      *
495      * @return B value of quaternion containing current device orientation.
496      */
497     public double getStateQuaternionB() {
498         return stateQuaternionB;
499     }
500 
501     /**
502      * Gets C value of quaternion containing current device orientation.
503      *
504      * @return C value of quaternion containing current device orientation.
505      */
506     public double getStateQuaternionC() {
507         return stateQuaternionC;
508     }
509 
510     /**
511      * Gets D value of quaternion containing current device orientation.
512      *
513      * @return D value of quaternion containing current device orientation.
514      */
515     public double getStateQuaternionD() {
516         return stateQuaternionD;
517     }
518 
519     /**
520      * Gets A, B, C, D values of quaternion containing current device
521      * orientation.
522      *
523      * @return A, B, C, D values of quaternion containing current device
524      * orientation.
525      */
526     public double[] getStateQuaternionArray() {
527         return new double[]{stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD};
528     }
529 
530     /**
531      * Gets A, B, C, D values of quaternion containing current device
532      * orientation.
533      *
534      * @param result array where A, B, C, D values of quaternion will be stored.
535      *               Must have length 4.
536      * @throws IllegalArgumentException if provided array does not have length
537      *                                  4.
538      */
539     public void getStateQuaternionArray(final double[] result) {
540         if (result.length != Quaternion.N_PARAMS) {
541             throw new IllegalArgumentException("result must have length 4");
542         }
543         result[0] = stateQuaternionA;
544         result[1] = stateQuaternionB;
545         result[2] = stateQuaternionC;
546         result[3] = stateQuaternionD;
547     }
548 
549     /**
550      * Gets quaternion containing current device orientation.
551      *
552      * @return quaternion containing current device orientation.
553      */
554     public Quaternion getStateQuaternion() {
555         return new Quaternion(stateQuaternionA, stateQuaternionB, stateQuaternionC, stateQuaternionD);
556     }
557 
558     /**
559      * Gets quaternion containing current device orientation.
560      *
561      * @param result instance where quaternion data will be stored.
562      */
563     public void getStateQuaternion(final Quaternion result) {
564         result.setA(stateQuaternionA);
565         result.setB(stateQuaternionB);
566         result.setC(stateQuaternionC);
567         result.setD(stateQuaternionD);
568     }
569 
570     /**
571      * Gets angular speed along x-axis expressed in radians per second (rad/s).
572      *
573      * @return angular speed along x-axis.
574      */
575     public double getStateAngularSpeedX() {
576         return stateAngularSpeedX;
577     }
578 
579     /**
580      * Gets angular speed along y-axis expressed in radians per second (rad/s).
581      *
582      * @return angular speed along y-axis.
583      */
584     public double getStateAngularSpeedY() {
585         return stateAngularSpeedY;
586     }
587 
588     /**
589      * Gets angular speed along z-axis expressed in radians per second (rad/s).
590      *
591      * @return angular speed along z-axis.
592      */
593     public double getStateAngularSpeedZ() {
594         return stateAngularSpeedZ;
595     }
596 
597     /**
598      * Gets angular speed of the device along x,y,z axes expressed in radians
599      * per second (rad/s).
600      *
601      * @return device's angular speed.
602      */
603     public double[] getStateAngularSpeed() {
604         return new double[]{stateAngularSpeedX, stateAngularSpeedY, stateAngularSpeedZ};
605     }
606 
607     /**
608      * Gets angular speed of the device along x,y,z axes expressed in radians
609      * per second (rad/s) and stores the result into provided array.
610      *
611      * @param result array where angular speed will be stored. Must have length
612      *               3.
613      * @throws IllegalArgumentException if provided array does not have length
614      *                                  3.
615      */
616     public void getStateAngularSpeed(final double[] result) {
617         if (result.length != N_COMPONENTS_3D) {
618             // result must have length 3
619             throw new IllegalArgumentException();
620         }
621         result[0] = stateAngularSpeedX;
622         result[1] = stateAngularSpeedY;
623         result[2] = stateAngularSpeedZ;
624     }
625 
626     /**
627      * Gets covariance matrix of state variables (position, velocity, acceleration, orientation and
628      * angular speed).
629      * Actual meaning of elements in returned matrix will depend on actual implementation of the estimator.
630      *
631      * @return covariance matrix of state variables.
632      */
633     public abstract Matrix getStateCovariance();
634 
635     /**
636      * Indicates whether an error occurred during the estimation.
637      * If an error occurs the estimator should be restarted since state values
638      * might become unreliable.
639      *
640      * @return true if an error occurred since last start time, false otherwise.
641      */
642     public boolean hasError() {
643         return error;
644     }
645 
646     /**
647      * Indicates whether accumulation of samples is enabled or not.
648      *
649      * @return true if accumulation of samples is enabled, false otherwise.
650      */
651     public boolean isAccumulationEnabled() {
652         return accumulationEnabled;
653     }
654 
655     /**
656      * Specifies whether accumulation of samples is enabled or not.
657      *
658      * @param accumulationEnabled true if accumulation of samples is enabled,
659      *                            false otherwise.
660      */
661     public void setAccumulationEnabled(final boolean accumulationEnabled) {
662         this.accumulationEnabled = accumulationEnabled;
663     }
664 
665     /**
666      * Gets timestamp expressed in nanoseconds since the epoch time of the last
667      * accelerometer sample, or -1 if no sample has been set yet.
668      *
669      * @return timestamp expressed in nanoseconds since the epoch time of the
670      * last accelerometer sample, or -1.
671      */
672     public long getAccelerometerTimestampNanos() {
673         return accelerometerTimestampNanos;
674     }
675 
676     /**
677      * Gets timestamp expressed in nanoseconds since the epoch time of the last
678      * gyroscope sample, or -1 if no sample has been set yet.
679      *
680      * @return timestamp expressed in nanoseconds since the epoch time of the
681      * last gyroscope sample, or -1.
682      */
683     public long getGyroscopeTimestampNanos() {
684         return gyroscopeTimestampNanos;
685     }
686 
687     /**
688      * Gets number of accelerometer samples accumulated since last full sample.
689      *
690      * @return number of accelerometer samples accumulated since last full
691      * sample.
692      */
693     public int getAccumulatedAccelerometerSamples() {
694         return accumulatedAccelerometerSamples;
695     }
696 
697     /**
698      * Gets number of gyroscope samples accumulated since last full sample.
699      *
700      * @return number of gyroscope samples accumulated since last full sample.
701      */
702     public int getAccumulatedGyroscopeSamples() {
703         return accumulatedGyroscopeSamples;
704     }
705 
706     /**
707      * Indicates whether the accelerometer sample has been received since the
708      * last full sample (accelerometer + gyroscope).
709      *
710      * @return true if accelerometer sample has been received, false otherwise.
711      */
712     public boolean isAccelerometerSampleReceived() {
713         return accumulatedAccelerometerSamples > 0;
714     }
715 
716     /**
717      * Indicates whether the gyroscope sample has been received since the last
718      * full sample (accelerometer + gyroscope).
719      *
720      * @return true if gyroscope sample has been received, false otherwise.
721      */
722     public boolean isGyroscopeSampleReceived() {
723         return accumulatedGyroscopeSamples > 0;
724     }
725 
726     /**
727      * Indicates whether a full sample (accelerometer + gyroscope) has been
728      * received or not.
729      *
730      * @return true if full sample has been received, false otherwise.
731      */
732     public boolean isFullSampleAvailable() {
733         return isAccelerometerSampleReceived() && isGyroscopeSampleReceived();
734     }
735 
736     /**
737      * Gets average acceleration along x-axis accumulated since last full
738      * sample. Expressed in meters per squared second (m/s^2).
739      *
740      * @return average acceleration along x-axis accumulated since last full
741      * sample.
742      */
743     public double getAccumulatedAccelerationSampleX() {
744         return accumulatedAccelerationSampleX;
745     }
746 
747     /**
748      * Gets average acceleration along y-axis accumulated since last full
749      * sample. Expressed in meters per squared second (m/s^2).
750      *
751      * @return average acceleration along y-axis accumulated since last full
752      * sample.
753      */
754     public double getAccumulatedAccelerationSampleY() {
755         return accumulatedAccelerationSampleY;
756     }
757 
758     /**
759      * Gets average acceleration along z-axis accumulated since last full
760      * sample. Expressed in meters per squared second (m/s^2).
761      *
762      * @return average acceleration along z-axis accumulated since last full
763      * sample.
764      */
765     public double getAccumulatedAccelerationSampleZ() {
766         return accumulatedAccelerationSampleZ;
767     }
768 
769     /**
770      * Gets average acceleration along x,y,z axes accumulated since last full
771      * sample. Expressed in meters per squared second (m/s^2).
772      *
773      * @return average acceleration along x,y,z axes expressed in meters per
774      * squared second (m/s^2).
775      */
776     public double[] getAccumulatedAccelerationSample() {
777         return new double[]{
778                 accumulatedAccelerationSampleX,
779                 accumulatedAccelerationSampleY,
780                 accumulatedAccelerationSampleZ
781         };
782     }
783 
784     /**
785      * Gets average acceleration along x,y,z axes accumulated since last full
786      * sample. Expressed in meters per squared second (m/s^2).
787      *
788      * @param result array where average acceleration along x,y,z axes will be
789      *               stored.
790      * @throws IllegalArgumentException if provided array does not have length
791      *                                  3.
792      */
793     public void getAccumulatedAccelerationSample(final double[] result) {
794         if (result.length != N_COMPONENTS_3D) {
795             throw new IllegalArgumentException("result must have length 3");
796         }
797         result[0] = accumulatedAccelerationSampleX;
798         result[1] = accumulatedAccelerationSampleY;
799         result[2] = accumulatedAccelerationSampleZ;
800     }
801 
802     /**
803      * Gets average angular speed along x-axis accumulated since last full
804      * sample. Expressed in radians per second (rad/s).
805      *
806      * @return average angular speed along x-axis expressed in radians per
807      * second (rad/s).
808      */
809     public double getAccumulatedAngularSpeedSampleX() {
810         return accumulatedAngularSpeedSampleX;
811     }
812 
813     /**
814      * Gets average angular speed along y-axis accumulated since last full
815      * sample. Expressed in radians per second (rad/s).
816      *
817      * @return average angular speed along y-axis expressed in radians per
818      * second (rad/s).
819      */
820     public double getAccumulatedAngularSpeedSampleY() {
821         return accumulatedAngularSpeedSampleY;
822     }
823 
824     /**
825      * Gets average angular speed along z-axis accumulated since last full
826      * sample. Expressed in radians per second (rad/s).
827      *
828      * @return average angular speed along z-axis expressed in radians per
829      * second.
830      */
831     public double getAccumulatedAngularSpeedSampleZ() {
832         return accumulatedAngularSpeedSampleZ;
833     }
834 
835     /**
836      * Gets average angular speed along x,y,z axes accumulated since last full
837      * sample. Expressed in radians per second (rad/s).
838      *
839      * @return average angular speed along x,y,z axes expressed in radians per
840      * second.
841      */
842     public double[] getAccumulatedAngularSpeedSample() {
843         return new double[]{
844                 accumulatedAngularSpeedSampleX,
845                 accumulatedAngularSpeedSampleY,
846                 accumulatedAngularSpeedSampleZ
847         };
848     }
849 
850     /**
851      * Gets average angular speed along x,y,z axes accumulated since last full
852      * sample. Expressed in radians per second (rad/s).
853      *
854      * @param result array where average angular speed along x,y,z axes will be
855      *               stored.
856      * @throws IllegalArgumentException if provided array does not have length
857      *                                  3.
858      */
859     public void getAccumulatedAngularSpeedSample(final double[] result) {
860         if (result.length != N_COMPONENTS_3D) {
861             throw new IllegalArgumentException("result must have length 3");
862         }
863         result[0] = accumulatedAngularSpeedSampleX;
864         result[1] = accumulatedAngularSpeedSampleY;
865         result[2] = accumulatedAngularSpeedSampleZ;
866     }
867 
868     /**
869      * Provides a new accelerometer sample.
870      * If accumulation is enabled, samples are averaged until a full sample is
871      * received.
872      * When a full sample (accelerometer + gyroscope) is received, internal
873      * state gets also updated.
874      *
875      * @param timestamp     timestamp of accelerometer sample since epoch time and
876      *                      expressed in nanoseconds.
877      * @param accelerationX linear acceleration along x-axis expressed in meters
878      *                      per squared second (m/s^2).
879      * @param accelerationY linear acceleration along y-axis expressed in meters
880      *                      per squared second (m/s^2).
881      * @param accelerationZ linear acceleration along z-axis expressed in meters
882      *                      per squared second (m/s^2).
883      */
884     public void updateAccelerometerSample(
885             final long timestamp, final float accelerationX, final float accelerationY, final float accelerationZ) {
886         if (!isFullSampleAvailable()) {
887             accelerometerTimestampNanos = timestamp;
888             if (isAccumulationEnabled() && isAccelerometerSampleReceived()) {
889                 // accumulation enabled
890                 final var nextSamples = accumulatedAccelerometerSamples + 1;
891                 accumulatedAccelerationSampleX = (accumulatedAccelerationSampleX * accumulatedAccelerometerSamples
892                         + accelerationX) / nextSamples;
893                 accumulatedAccelerationSampleY = (accumulatedAccelerationSampleY * accumulatedAccelerometerSamples
894                         + accelerationY) / nextSamples;
895                 accumulatedAccelerationSampleZ = (accumulatedAccelerationSampleZ * accumulatedAccelerometerSamples
896                         + accelerationZ) / nextSamples;
897                 accumulatedAccelerometerSamples = nextSamples;
898             } else {
899                 // accumulation disabled
900                 accumulatedAccelerationSampleX = accelerationX;
901                 accumulatedAccelerationSampleY = accelerationY;
902                 accumulatedAccelerationSampleZ = accelerationZ;
903                 accumulatedAccelerometerSamples++;
904             }
905             notifyFullSampleAndResetSampleReceive();
906         }
907     }
908 
909     /**
910      * Provides a new accelerometer sample.
911      * If accumulation is enabled, samples are averaged until a full sample is
912      * received.
913      * When a full sample (accelerometer + gyroscope) is received, internal
914      * state gets also updated.
915      *
916      * @param timestamp timestamp of accelerometer sample since epoch time and
917      *                  expressed in nanoseconds.
918      * @param data      array containing x,y,z components of linear acceleration
919      *                  expressed in meters per squared second (m/s^2).
920      * @throws IllegalArgumentException if provided array does not have length
921      *                                  3.
922      */
923     public void updateAccelerometerSample(final long timestamp, final float[] data) {
924         if (data.length != N_COMPONENTS_3D) {
925             throw new IllegalArgumentException("acceleration must have length 3");
926         }
927         updateAccelerometerSample(timestamp, data[0], data[1], data[2]);
928     }
929 
930     /**
931      * Provides a new gyroscope sample.
932      * If accumulation is enabled, samples are averaged until a full sample is
933      * received.
934      * When a full sample (accelerometer + gyroscope) is received, internal
935      * state gets also updated.
936      *
937      * @param timestamp     timestamp of gyroscope sample since epoch time and
938      *                      expressed in nanoseconds.
939      * @param angularSpeedX angular speed of rotation along x-axis expressed in
940      *                      radians per second (rad/s).
941      * @param angularSpeedY angular speed of rotation along y-axis expressed in
942      *                      radians per second (rad/s).
943      * @param angularSpeedZ angular speed of rotation along z-axis expressed in
944      *                      radians per second (rad/s).
945      */
946     public void updateGyroscopeSample(
947             final long timestamp, final float angularSpeedX, final float angularSpeedY, final float angularSpeedZ) {
948         if (!isFullSampleAvailable()) {
949             gyroscopeTimestampNanos = timestamp;
950             if (isAccumulationEnabled() && isGyroscopeSampleReceived()) {
951                 // accumulation enabled
952                 final var nextSamples = accumulatedGyroscopeSamples + 1;
953                 accumulatedAngularSpeedSampleX = (accumulatedAngularSpeedSampleX * accumulatedGyroscopeSamples
954                         + angularSpeedX) / nextSamples;
955                 accumulatedAngularSpeedSampleY = (accumulatedAngularSpeedSampleY * accumulatedGyroscopeSamples
956                         + angularSpeedY) / nextSamples;
957                 accumulatedAngularSpeedSampleZ = (accumulatedAngularSpeedSampleZ * accumulatedGyroscopeSamples
958                         + angularSpeedZ) / nextSamples;
959                 accumulatedGyroscopeSamples = nextSamples;
960             } else {
961                 // accumulation disabled
962                 accumulatedAngularSpeedSampleX = angularSpeedX;
963                 accumulatedAngularSpeedSampleY = angularSpeedY;
964                 accumulatedAngularSpeedSampleZ = angularSpeedZ;
965                 accumulatedGyroscopeSamples++;
966             }
967             notifyFullSampleAndResetSampleReceive();
968         }
969     }
970 
971     /**
972      * Provides a new gyroscope sample.
973      * If accumulation is enabled, samples are averaged until a full sample is
974      * received.
975      * When a full sample (accelerometer + gyroscope) is received, internal
976      * state gets also updated.
977      *
978      * @param timestamp timestamp of gyroscope sample since epoch time and
979      *                  expressed in nanoseconds.
980      * @param data      angular speed of rotation along x,y,z axes expressed in
981      *                  radians per second (rad/s).
982      * @throws IllegalArgumentException if provided array does not have length
983      *                                  3.
984      */
985     public void updateGyroscopeSample(final long timestamp, final float[] data) {
986         if (data.length != N_COMPONENTS_3D) {
987             throw new IllegalArgumentException("angular speed must have length 3");
988         }
989         updateGyroscopeSample(timestamp, data[0], data[1], data[2]);
990     }
991 
992     /**
993      * Gets most recent timestamp of received partial samples (accelerometer or
994      * gyroscope).
995      *
996      * @return most recent timestamp of received partial sample.
997      */
998     public long getMostRecentTimestampNanos() {
999         return Math.max(accelerometerTimestampNanos, gyroscopeTimestampNanos);
1000     }
1001 
1002     /**
1003      * Corrects system state with provided position measure having an accuracy
1004      * determined by provided covariance matrix.
1005      *
1006      * @param positionX          new position along x-axis expressed in meters (m).
1007      * @param positionY          new position along y-axis expressed in meters (m).
1008      * @param positionZ          new position along z-axis expressed in meters (m).
1009      * @param positionCovariance new position covariance matrix determining
1010      *                           new position accuracy or null if last available covariance does not need
1011      *                           to be updated.
1012      * @throws IllegalArgumentException if provided covariance matrix is not
1013      *                                  3x3.
1014      */
1015     public void correctWithPositionMeasure(
1016             final double positionX, final double positionY, final double positionZ, final Matrix positionCovariance) {
1017         setPositionCovarianceMatrix(positionCovariance);
1018         correctWithPositionMeasure(positionX, positionY, positionZ);
1019     }
1020 
1021     /**
1022      * Corrects system state with provided position measure having an accuracy
1023      * determined by provided covariance matrix.
1024      *
1025      * @param position           x,y,z coordinates of position expressed in meters (m).
1026      *                           Must have length 3.
1027      * @param positionCovariance new position covariance matrix determining new
1028      *                           position accuracy or null if last available covariance does not need to
1029      *                           be updated.
1030      * @throws IllegalArgumentException if provided covariance matrix is not
1031      *                                  3x3 or if provided position array does not have length 3.
1032      */
1033     public void correctWithPositionMeasure(final double[] position, final Matrix positionCovariance) {
1034         if (position.length != N_COMPONENTS_3D) {
1035             throw new IllegalArgumentException("position must have length 3");
1036         }
1037         correctWithPositionMeasure(position[0], position[1], position[2], positionCovariance);
1038     }
1039 
1040     /**
1041      * Corrects system state with provided position measure having an accuracy
1042      * determined by provided covariance matrix.
1043      *
1044      * @param position           position expressed in meters (m).
1045      * @param positionCovariance new position covariance matrix determining new
1046      *                           position accuracy or null if last available covariance does not need to
1047      *                           be updated.
1048      * @throws IllegalArgumentException if provided covariance matrix is not
1049      *                                  3x3.
1050      */
1051     public void correctWithPositionMeasure(final Point3D position, final Matrix positionCovariance) {
1052         correctWithPositionMeasure(position.getInhomX(), position.getInhomY(), position.getInhomZ(),
1053                 positionCovariance);
1054     }
1055 
1056     /**
1057      * Updates covariance matrix of position measures.
1058      * If null is provided, covariance matrix is not updated.
1059      *
1060      * @param positionCovariance new position covariance determining position
1061      *                           accuracy or null if last available covariance does not need to be
1062      *                           updated.
1063      * @throws IllegalArgumentException if provided covariance matrix is not
1064      *                                  3x3.
1065      */
1066     public abstract void setPositionCovarianceMatrix(final Matrix positionCovariance);
1067 
1068     /**
1069      * Gets current covariance matrix of position measures determining current
1070      * accuracy of provided position measures.
1071      *
1072      * @return covariance matrix of position measures.
1073      */
1074     public abstract Matrix getPositionCovarianceMatrix();
1075 
1076     /**
1077      * Corrects system state with provided position measure using current
1078      * position accuracy.
1079      *
1080      * @param positionX new position along x-axis expressed in meters (m).
1081      * @param positionY new position along y-axis expressed in meters (m).
1082      * @param positionZ new position along z-axis expressed in meters (m).
1083      */
1084     public abstract void correctWithPositionMeasure(
1085             final double positionX, final double positionY, final double positionZ);
1086 
1087     /**
1088      * Corrects system state with provided position measure using current
1089      * position accuracy.
1090      *
1091      * @param position x,y,z coordinates of position expressed in meters (m).
1092      *                 Must have length 3.
1093      * @throws IllegalArgumentException if provided array does not have length
1094      *                                  3.
1095      */
1096     public void correctWithPositionMeasure(final double[] position) {
1097         correctWithPositionMeasure(position, null);
1098     }
1099 
1100     /**
1101      * Corrects system state with provided position measure using current
1102      * position accuracy.
1103      *
1104      * @param position position expressed in meters (m).
1105      */
1106     public void correctWithPositionMeasure(final Point3D position) {
1107         correctWithPositionMeasure(position, null);
1108     }
1109 
1110     /**
1111      * Gets listener in charge of handling events raised by instances of this
1112      * class.
1113      *
1114      * @return listener in charge of handling events raised by instances of this
1115      * class.
1116      */
1117     public BaseSlamEstimatorListener<D> getListener() {
1118         return listener;
1119     }
1120 
1121     /**
1122      * Sets listener in charge of handling events raised by instances of this
1123      * class.
1124      *
1125      * @param listener listener in charge of handling events raised by instances
1126      *                 of this class.
1127      */
1128     public void setListener(final BaseSlamEstimatorListener<D> listener) {
1129         this.listener = listener;
1130     }
1131 
1132     /**
1133      * Gets calibration data. When provided, its mean and covariance are used
1134      * to correct control samples and adjust process covariance matrix during
1135      * Kalman filtering in prediction stage.
1136      *
1137      * @return calibration data.
1138      */
1139     public D getCalibrationData() {
1140         return calibrationData;
1141     }
1142 
1143     /**
1144      * Sets calibration data. When provided, its mean and covariance are used
1145      * to correct control samples and adjust process covariance matrix during
1146      * Kalman filtering in prediction stage.
1147      *
1148      * @param calibrationData calibration data.
1149      */
1150     public void setCalibrationData(final D calibrationData) {
1151         this.calibrationData = calibrationData;
1152     }
1153 
1154     /**
1155      * Resets position, linear velocity, linear acceleration, orientation and
1156      * angular speed to provided values.
1157      *
1158      * @param statePositionX     position along x-axis expressed in meters (m).
1159      * @param statePositionY     position along y-axis expressed in meters (m).
1160      * @param statePositionZ     position along z-axis expressed in meters (m).
1161      * @param stateVelocityX     linear velocity along x-axis expressed in meters
1162      *                           per second (m/s).
1163      * @param stateVelocityY     linear velocity along y-axis expressed in meters
1164      *                           per second (m/s).
1165      * @param stateVelocityZ     linear velocity along z-axis expressed in meters
1166      *                           per second (m/s).
1167      * @param stateAccelerationX linear acceleration along x-axis expressed in
1168      *                           meters per squared second (m/s^2).
1169      * @param stateAccelerationY linear acceleration along y-axis expressed in
1170      *                           meters per squared second (m/s^2).
1171      * @param stateAccelerationZ linear acceleration along z-axis expressed in
1172      *                           meters per squared second (m/s^2).
1173      * @param stateQuaternionA   A value of orientation quaternion.
1174      * @param stateQuaternionB   B value of orientation quaternion.
1175      * @param stateQuaternionC   C value of orientation quaternion.
1176      * @param stateQuaternionD   D value of orientation quaternion.
1177      * @param stateAngularSpeedX angular speed along x-axis expressed in radians
1178      *                           per second (rad/s).
1179      * @param stateAngularSpeedY angular speed along y-axis expressed in radians
1180      *                           per second (rad/s).
1181      * @param stateAngularSpeedZ angular speed along z-axis expressed in radians
1182      *                           per second (rad/s).
1183      */
1184     protected void reset(
1185             final double statePositionX, final double statePositionY, final double statePositionZ,
1186             final double stateVelocityX, final double stateVelocityY, final double stateVelocityZ,
1187             final double stateAccelerationX, final double stateAccelerationY, final double stateAccelerationZ,
1188             final double stateQuaternionA, final double stateQuaternionB,
1189             final double stateQuaternionC, final double stateQuaternionD,
1190             final double stateAngularSpeedX, final double stateAngularSpeedY, final double stateAngularSpeedZ) {
1191         this.statePositionX = statePositionX;
1192         this.statePositionY = statePositionY;
1193         this.statePositionZ = statePositionZ;
1194         this.stateVelocityX = stateVelocityX;
1195         this.stateVelocityY = stateVelocityY;
1196         this.stateVelocityZ = stateVelocityZ;
1197         this.stateAccelerationX = stateAccelerationX;
1198         this.stateAccelerationY = stateAccelerationY;
1199         this.stateAccelerationZ = stateAccelerationZ;
1200         this.stateQuaternionA = stateQuaternionA;
1201         this.stateQuaternionB = stateQuaternionB;
1202         this.stateQuaternionC = stateQuaternionC;
1203         this.stateQuaternionD = stateQuaternionD;
1204         this.stateAngularSpeedX = stateAngularSpeedX;
1205         this.stateAngularSpeedY = stateAngularSpeedY;
1206         this.stateAngularSpeedZ = stateAngularSpeedZ;
1207         accelerometerTimestampNanos = gyroscopeTimestampNanos = -1;
1208     }
1209 
1210     /**
1211      * Notifies that a full sample has been received and resets flags indicating
1212      * whether partial samples have been received.
1213      */
1214     protected void notifyFullSampleAndResetSampleReceive() {
1215         if (isFullSampleAvailable()) {
1216             processFullSample();
1217             accumulatedAccelerometerSamples = accumulatedGyroscopeSamples = 0;
1218         }
1219     }
1220 
1221     /**
1222      * Method to be implemented in subclasses to process a full sample.
1223      */
1224     protected abstract void processFullSample();
1225 
1226     /**
1227      * Listener for implementations of this class.
1228      *
1229      * @param <D> calibrator type associated to implementations of SLAM calibration
1230      *            data.
1231      */
1232     public interface BaseSlamEstimatorListener<D extends BaseCalibrationData> {
1233         /**
1234          * Called when a full sample (accelerometer + gyroscope, etc.) has been
1235          * received and is about to be processed to update internal state.
1236          *
1237          * @param estimator SLAM estimator.
1238          */
1239         void onFullSampleReceived(final BaseSlamEstimator<D> estimator);
1240 
1241         /**
1242          * Called when a full sample (accelerometer + gyroscope, etc.) has been
1243          * received and has already been processed, and hence internal state has
1244          * also been updated.
1245          *
1246          * @param estimator SLAM estimator.
1247          */
1248         void onFullSampleProcessed(final BaseSlamEstimator<D> estimator);
1249 
1250         /**
1251          * Called when internal state is about to be corrected by using an
1252          * external measure.
1253          *
1254          * @param estimator SLAM estimator.
1255          */
1256         void onCorrectWithPositionMeasure(final BaseSlamEstimator<D> estimator);
1257 
1258         /**
1259          * Called after internal state has been corrected using an external
1260          * measure.
1261          *
1262          * @param estimator SLAM estimator.
1263          */
1264         void onCorrectedWithPositionMeasure(final BaseSlamEstimator<D> estimator);
1265     }
1266 }