View Javadoc
1   /*
2    * Copyright (C) 2020 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.navigation.inertial.calibration.noise;
17  
18  import com.irurueta.navigation.LockedException;
19  import com.irurueta.navigation.inertial.BodyKinematics;
20  import com.irurueta.navigation.inertial.calibration.AccelerationTriad;
21  import com.irurueta.navigation.inertial.calibration.AccelerometerNoiseRootPsdSource;
22  import com.irurueta.navigation.inertial.calibration.AngularSpeedTriad;
23  import com.irurueta.navigation.inertial.calibration.GyroscopeNoiseRootPsdSource;
24  import com.irurueta.navigation.inertial.calibration.TimeIntervalEstimator;
25  import com.irurueta.units.Acceleration;
26  import com.irurueta.units.AccelerationUnit;
27  import com.irurueta.units.AngularSpeed;
28  import com.irurueta.units.AngularSpeedUnit;
29  import com.irurueta.units.Time;
30  import com.irurueta.units.TimeConverter;
31  import com.irurueta.units.TimeUnit;
32  
33  import java.util.LinkedList;
34  
35  /**
36   * Estimates accelerometer and angular speed noise variances and PSD's
37   * (Power Spectral Densities) along with their average values for a windowed
38   * amount of samples.
39   * This estimator must be used when the body where the accelerometer and
40   * gyroscope are attached remains static on the same position with zero
41   * velocity and constant (or zero) angular speed while capturing data.
42   * To compute PSD's, this estimator assumes that measurement samples are obtained
43   * at a constant provided rate equal to {@link #getTimeInterval()} seconds.
44   * If not available, sampling rate average can be estimated using
45   * {@link TimeIntervalEstimator}.
46   * This estimator does NOT require the knowledge of current location and body
47   * orientation.
48   * Because body location and orientation is not known, estimated average values
49   * cannot be used to determine biases. Only norm of noise estimations
50   * (variance or standard deviation) can be safely used.
51   */
52  @SuppressWarnings("DuplicatedCode")
53  public class WindowedBodyKinematicsNoiseEstimator implements AccelerometerNoiseRootPsdSource,
54          GyroscopeNoiseRootPsdSource {
55      /**
56       * Number of samples to keep within the window by default.
57       * For an accelerometer generating 100 samples/second, this is equivalent to
58       * 1 second.
59       * For an accelerometer generating 50 samples/second, this is equivalent to
60       * 2 seconds.
61       */
62      public static final int DEFAULT_WINDOW_SIZE = WindowedTriadNoiseEstimator.DEFAULT_WINDOW_SIZE;
63  
64      /**
65       * Minimum allowed window size.
66       */
67      public static final int MIN_WINDOW_SIZE = WindowedTriadNoiseEstimator.MIN_WINDOW_SIZE;
68  
69      /**
70       * Default time interval between accelerometer samples expressed in seconds
71       * (s).
72       */
73      public static final double DEFAULT_TIME_INTERVAL_SECONDS =
74              WindowedTriadNoiseEstimator.DEFAULT_TIME_INTERVAL_SECONDS;
75  
76      /**
77       * Length of number of samples to keep within the window being processed.
78       * Window size must always be larger than allowed minimum value.
79       */
80      private int windowSize = DEFAULT_WINDOW_SIZE;
81  
82      /**
83       * Time interval expressed in seconds (s) between consecutive accelerometer
84       * samples.
85       */
86      private double timeInterval = DEFAULT_TIME_INTERVAL_SECONDS;
87  
88      /**
89       * Keeps the list of body kinematics samples that remain within the window.
90       */
91      private final LinkedList<BodyKinematics> windowedSamples = new LinkedList<>();
92  
93      /**
94       * Listener to handle events raised by this estimator.
95       */
96      private WindowedBodyKinematicsNoiseEstimatorListener listener;
97  
98      /**
99       * Estimated average of x coordinate of specific force expressed in
100      * meters per squared second (m/s^2).
101      */
102     private double avgSpecificForceX;
103 
104     /**
105      * Estimated average of y coordinate of specific force expressed in
106      * meters per squared second (m/s^2).
107      */
108     private double avgSpecificForceY;
109 
110     /**
111      * Estimated average of z coordinate of specific force expressed in
112      * meters per squared second (m/s^2).
113      */
114     private double avgSpecificForceZ;
115 
116     /**
117      * Estimated average of x coordinate of angular rate expressed in
118      * radians per second (rad/s).
119      */
120     private double avgAngularRateX;
121 
122     /**
123      * Estimated average of y coordinate of angular rate expressed in
124      * radians per second (rad/s).
125      */
126     private double avgAngularRateY;
127 
128     /**
129      * Estimated average of z coordinate of angular rate expressed in
130      * radians per second (rad/s).
131      */
132     private double avgAngularRateZ;
133 
134     /**
135      * Estimated variance of x coordinate of specific force expressed
136      * in (m^2/s^4).
137      */
138     private double varianceSpecificForceX;
139 
140     /**
141      * Estimated variance of y coordinate of specific force expressed
142      * in (m^2/s^4).
143      */
144     private double varianceSpecificForceY;
145 
146     /**
147      * Estimated variance of z coordinate of specific force expressed
148      * in (m^2/s^4).
149      */
150     private double varianceSpecificForceZ;
151 
152     /**
153      * Estimated variance of x coordinate of angular rate expressed
154      * in (rad^2/s^2).
155      */
156     private double varianceAngularRateX;
157 
158     /**
159      * Estimated variance of y coordinate of angular rate expressed
160      * in (rad^2/s^2).
161      */
162     private double varianceAngularRateY;
163 
164     /**
165      * Estimated variance of z coordinate of angular rate expressed
166      * in (rad^2/s^2).
167      */
168     private double varianceAngularRateZ;
169 
170     /**
171      * Number of processed acceleration triad samples.
172      */
173     private int numberOfProcessedSamples;
174 
175     /**
176      * Indicates whether estimator is running or not.
177      */
178     private boolean running;
179 
180     /**
181      * Constructor.
182      */
183     public WindowedBodyKinematicsNoiseEstimator() {
184     }
185 
186     /**
187      * Constructor.
188      *
189      * @param listener listener to handle events raised by this estimator.
190      */
191     public WindowedBodyKinematicsNoiseEstimator(final WindowedBodyKinematicsNoiseEstimatorListener listener) {
192         this.listener = listener;
193     }
194 
195     /**
196      * Gets length of number of samples to keep within the window being processed.
197      * Window size must always be larger than allowed minimum value.
198      *
199      * @return length of number of samples to keep within the window.
200      */
201     public int getWindowSize() {
202         return windowSize;
203     }
204 
205     /**
206      * Sets length of number of samples to keep within the window being processed.
207      * Window size must always be larger than allowed minimum value.
208      * When window size is modified, instance state is reset.
209      *
210      * @param windowSize length of number of samples to keep within the window.
211      * @throws IllegalArgumentException if provided value is not valid.
212      * @throws LockedException          if estimator is currently running.
213      */
214     public void setWindowSize(final int windowSize) throws LockedException {
215         if (running) {
216             throw new LockedException();
217         }
218 
219         // check that window is larger than minimum allowed value
220         if (windowSize < MIN_WINDOW_SIZE) {
221             throw new IllegalArgumentException();
222         }
223 
224         this.windowSize = windowSize;
225         reset();
226     }
227 
228     /**
229      * Gets time interval between body kinematics samples expressed in
230      * seconds (s).
231      *
232      * @return time interval between accelerometer triad samples.
233      */
234     public double getTimeInterval() {
235         return timeInterval;
236     }
237 
238     /**
239      * Sets time interval between body kinematics samples expressed in
240      * seconds (s).
241      *
242      * @param timeInterval time interval between accelerometer triad samples.
243      * @throws IllegalArgumentException if provided value is negative.
244      * @throws LockedException          if estimator is currently running.
245      */
246     public void setTimeInterval(final double timeInterval) throws LockedException {
247         if (running) {
248             throw new LockedException();
249         }
250 
251         if (timeInterval < 0.0) {
252             throw new IllegalArgumentException();
253         }
254 
255         this.timeInterval = timeInterval;
256     }
257 
258     /**
259      * Gets time interval between body kinematics samples.
260      *
261      * @return time interval between accelerometer triad samples.
262      */
263     public Time getTimeIntervalAsTime() {
264         return new Time(timeInterval, TimeUnit.SECOND);
265     }
266 
267     /**
268      * Gets time interval between body kinematics samples.
269      *
270      * @param result instance where time interval will be stored.
271      */
272     public void getTimeIntervalAsTime(final Time result) {
273         result.setValue(timeInterval);
274         result.setUnit(TimeUnit.SECOND);
275     }
276 
277     /**
278      * Sets time interval between body kinematics samples.
279      *
280      * @param timeInterval time interval between accelerometer triad samples.
281      * @throws LockedException if estimator is currently running.
282      */
283     public void setTimeInterval(final Time timeInterval) throws LockedException {
284         setTimeInterval(TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(),
285                 TimeUnit.SECOND));
286     }
287 
288     /**
289      * Gets listener to handle events raised by this estimator.
290      *
291      * @return listener to handle events raised by this estimator.
292      */
293     public WindowedBodyKinematicsNoiseEstimatorListener getListener() {
294         return listener;
295     }
296 
297     /**
298      * Sets listener to handle events raised by this estimator.
299      *
300      * @param listener listener to handle events raised by this estimator.
301      * @throws LockedException if this estimator is running.
302      */
303     public void setListener(final WindowedBodyKinematicsNoiseEstimatorListener listener) throws LockedException {
304         if (running) {
305             throw new LockedException();
306         }
307 
308         this.listener = listener;
309     }
310 
311     /**
312      * Gets first provided body kinematics within the window.
313      *
314      * @return first provided body kinematics within the window or null if not
315      * available.
316      */
317     public BodyKinematics getFirstWindowedBodyKinematics() {
318         return windowedSamples.isEmpty() ? null : windowedSamples.getFirst();
319     }
320 
321     /**
322      * Gets first provided body kinematics within the window.
323      *
324      * @param result instance where first provided body kinematics will be stored.
325      * @return true if result instance was updated, false otherwise.
326      */
327     public boolean getFirstWindowedBodyKinematics(final BodyKinematics result) {
328         if (windowedSamples.isEmpty()) {
329             return false;
330         } else {
331             result.copyFrom(windowedSamples.getFirst());
332             return true;
333         }
334     }
335 
336     /**
337      * Gets last provided body kinematics within the window.
338      *
339      * @return last provided body kinematics within the window or null if not
340      * available.
341      */
342     public BodyKinematics getLastWindowedBodyKinematics() {
343         return windowedSamples.isEmpty() ? null : windowedSamples.getLast();
344     }
345 
346     /**
347      * Gets last provided body kinematics within the window.
348      *
349      * @param result instance where last provided body kinematics will be stored.
350      * @return true if result instance was updated, false otherwise.
351      */
352     public boolean getLastWindowedBodyKinematics(final BodyKinematics result) {
353         if (windowedSamples.isEmpty()) {
354             return false;
355         } else {
356             result.copyFrom(windowedSamples.getLast());
357             return true;
358         }
359     }
360 
361     /**
362      * Gets estimated average of x coordinate of accelerometer sensed specific force
363      * expressed in meters per squared second (m/s^2).
364      * This value will depend of body location and orientation, hence it should never
365      * be used as a calibration bias.
366      *
367      * @return average of x coordinate of sensed specific force.
368      */
369     public double getAvgSpecificForceX() {
370         return avgSpecificForceX;
371     }
372 
373     /**
374      * Gets estimated average of x coordinate of accelerometer sensed specific force.
375      * This value will depend of body location and orientation, hence it should never
376      * be used as a calibration bias.
377      *
378      * @return average of x coordinate of sensed specific force.
379      */
380     public Acceleration getAvgSpecificForceXAsMeasurement() {
381         return new Acceleration(avgSpecificForceX, AccelerationUnit.METERS_PER_SQUARED_SECOND);
382     }
383 
384     /**
385      * Gets estimated average of x coordinate of accelerometer sensed specific force.
386      * This value will depend of body location and orientation, hence it should never
387      * be used as a calibration bias.
388      *
389      * @param result instance where average of x coordinate of sensed specific force
390      *               will be stored.
391      */
392     public void getAvgSpecificForceXAsMeasurement(final Acceleration result) {
393         result.setValue(avgSpecificForceX);
394         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
395     }
396 
397     /**
398      * Gets estimated average of y coordinate of accelerometer sensed specific force
399      * expressed in meters per squared second (m/s^2).
400      * This value will depend of body location and orientation, hence it should never
401      * be used as a calibration bias.
402      *
403      * @return average of y coordinate of sensed specific force.
404      */
405     public double getAvgSpecificForceY() {
406         return avgSpecificForceY;
407     }
408 
409     /**
410      * Gets estimated average of y coordinate of accelerometer sensed specific force.
411      * This value will depend of body location and orientation, hence it should never
412      * be used as a calibration bias.
413      *
414      * @return average of y coordinate of sensed specific force.
415      */
416     public Acceleration getAvgSpecificForceYAsMeasurement() {
417         return new Acceleration(avgSpecificForceY, AccelerationUnit.METERS_PER_SQUARED_SECOND);
418     }
419 
420     /**
421      * Gets estimated average of y coordinate of accelerometer sensed specific force.
422      * This value will depend of body location and orientation, hence it should never
423      * be used as a calibration bias.
424      *
425      * @param result instance where average of y coordinate of sensed specific force
426      *               will be stored.
427      */
428     public void getAvgSpecificForceYAsMeasurement(final Acceleration result) {
429         result.setValue(avgSpecificForceY);
430         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
431     }
432 
433     /**
434      * Gets estimated average of z coordinate of accelerometer sensed specific force
435      * expressed in meters per squared second (m/s^2).
436      * This value will depend of body location and orientation, hence it should never
437      * be used as a calibration bias.
438      *
439      * @return average of z coordinate of sensed specific force.
440      */
441     public double getAvgSpecificForceZ() {
442         return avgSpecificForceZ;
443     }
444 
445     /**
446      * Gets estimated average of z coordinate of accelerometer sensed specific force.
447      * This value will depend of body location and orientation, hence it should never
448      * be used as a calibration bias.
449      *
450      * @return average of z coordinate of sensed specific force.
451      */
452     public Acceleration getAvgSpecificForceZAsMeasurement() {
453         return new Acceleration(avgSpecificForceZ, AccelerationUnit.METERS_PER_SQUARED_SECOND);
454     }
455 
456     /**
457      * Gets estimated average of z coordinate of accelerometer sensed specific force.
458      * This value will depend of body location and orientation, hence it should never
459      * be used as a calibration bias.
460      *
461      * @param result instance where average of z coordinate of sensed specific force
462      *               will be stored.
463      */
464     public void getAvgSpecificForceZAsMeasurement(final Acceleration result) {
465         result.setValue(avgSpecificForceZ);
466         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
467     }
468 
469     /**
470      * Gets estimated average of accelerometer sensed specific force as a measurement
471      * triad.
472      *
473      * @return average accelerometer triad.
474      */
475     public AccelerationTriad getAvgSpecificForceAsTriad() {
476         return new AccelerationTriad(AccelerationUnit.METERS_PER_SQUARED_SECOND,
477                 avgSpecificForceX, avgSpecificForceY, avgSpecificForceZ);
478     }
479 
480     /**
481      * Gets estimated average of accelerometer sensed specific force as a measurement
482      * triad.
483      *
484      * @param result instance where average accelerometer triad will be stored.
485      */
486     public void getAvgSpecificForceAsTriad(final AccelerationTriad result) {
487         result.setValueCoordinatesAndUnit(avgSpecificForceX, avgSpecificForceY, avgSpecificForceZ,
488                 AccelerationUnit.METERS_PER_SQUARED_SECOND);
489     }
490 
491     /**
492      * Gets norm of estimated average acceleration expressed in meters per squared
493      * second (m/s^2). This value is independent of body orientation.
494      *
495      * @return norm of estimated average acceleration.
496      */
497     public double getAvgSpecificForceNorm() {
498         return Math.sqrt(avgSpecificForceX * avgSpecificForceX + avgSpecificForceY * avgSpecificForceY
499                 + avgSpecificForceZ * avgSpecificForceZ);
500     }
501 
502     /**
503      * Gets norm of estimated average acceleration within current window.
504      *
505      * @return norm of estimated average acceleration.
506      */
507     public Acceleration getAvgSpecificForceNormAsMeasurement() {
508         return new Acceleration(getAvgSpecificForceNorm(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
509     }
510 
511     /**
512      * Gets norm of estimated average acceleration.
513      *
514      * @param result instance where norm of estimated average acceleration will be stored.
515      */
516     public void getAvgSpecificForceNormAsMeasurement(final Acceleration result) {
517         result.setValue(getAvgSpecificForceNorm());
518         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
519     }
520 
521     /**
522      * Gets estimated average of x coordinate of gyroscope sensed angular rate
523      * expressed in radians per second (rad/s).
524      * This value will depend of body location and orientation, hence it should
525      * never be used as a calibration bias.
526      *
527      * @return average of x coordinate of sensed angular rate.
528      */
529     public double getAvgAngularRateX() {
530         return avgAngularRateX;
531     }
532 
533     /**
534      * Gets estimated average of x coordinate of gyroscope sensed angular rate.
535      * This value will depend of body location and orientation, hence it should never
536      * be used as a calibration bias.
537      *
538      * @return average of x coordinate of sensed angular rate.
539      */
540     public AngularSpeed getAvgAngularRateXAsMeasurement() {
541         return new AngularSpeed(avgAngularRateX, AngularSpeedUnit.RADIANS_PER_SECOND);
542     }
543 
544     /**
545      * Gets estimated average of x coordinate of gyroscope sensed angular rate.
546      * This value will depend of body location and orientation, hence it should never
547      * be used as a calibration bias.
548      *
549      * @param result instance where average of x coordinate of sensed angular rate
550      *               will be stored.
551      */
552     public void getAvgAngularRateXAsMeasurement(final AngularSpeed result) {
553         result.setValue(avgAngularRateX);
554         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
555     }
556 
557     /**
558      * Gets estimated average of y coordinate of gyroscope sensed angular rate
559      * expressed in radians per second (rad/s).
560      * This value will depend of body location and orientation, hence it should never
561      * be used as a calibration bias.
562      *
563      * @return average of y coordinate of sensed angular rate.
564      */
565     public double getAvgAngularRateY() {
566         return avgAngularRateY;
567     }
568 
569     /**
570      * Gets estimated average of y coordinate of gyroscope sensed angular rate.
571      * This value will depend of body location and orientation, hence it should never
572      * be used as a calibration bias.
573      *
574      * @return average of y coordinate of sensed angular rate.
575      */
576     public AngularSpeed getAvgAngularRateYAsMeasurement() {
577         return new AngularSpeed(avgAngularRateY, AngularSpeedUnit.RADIANS_PER_SECOND);
578     }
579 
580     /**
581      * Gets estimated average of y coordinate of gyroscope sensed angular rate.
582      * This value will depend of body location and orientation, hence it should never
583      * be used as a calibration bias.
584      *
585      * @param result instance where average of y coordinate of sensed angular rate
586      *               will be stored.
587      */
588     public void getAvgAngularRateYAsMeasurement(final AngularSpeed result) {
589         result.setValue(avgAngularRateY);
590         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
591     }
592 
593     /**
594      * Gets estimated average of z coordinate of gyroscope sensed angular rate
595      * expressed in radians per second (rad/s).
596      * This value will depend of body location and orientation, hence it should never
597      * be used as a calibration bias.
598      *
599      * @return average of z coordinate of sensed angular rate.
600      */
601     public double getAvgAngularRateZ() {
602         return avgAngularRateZ;
603     }
604 
605     /**
606      * Gets estimated average of z coordinate of gyroscope sensed angular rate.
607      * This value will depend of body location and orientation, hence it should never
608      * be used as a calibration bias.
609      *
610      * @return average of z coordinate of sensed angular rate.
611      */
612     public AngularSpeed getAvgAngularRateZAsMeasurement() {
613         return new AngularSpeed(avgAngularRateZ, AngularSpeedUnit.RADIANS_PER_SECOND);
614     }
615 
616     /**
617      * Gets estimated average of z coordinate of gyroscope sensed angular rate.
618      * This value will depend of body location and orientation, hence it should never
619      * be used as a calibration bias.
620      *
621      * @param result instance where average of z coordinate of sensed angular rate
622      *               will be stored.
623      */
624     public void getAvgAngularRateZAsMeasurement(final AngularSpeed result) {
625         result.setValue(avgAngularRateZ);
626         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
627     }
628 
629     /**
630      * Gets estimated average of gyroscope sensed angular speed as a measurement
631      * triad.
632      *
633      * @return average angular speed triad.
634      */
635     public AngularSpeedTriad getAvgAngularRateTriad() {
636         return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
637                 avgAngularRateX, avgAngularRateY, avgAngularRateZ);
638     }
639 
640     /**
641      * Gets estimated average of gyroscope sensed angular speed as a measurement
642      * triad.
643      *
644      * @param result instance where average angular speed triad will be stored.
645      */
646     public void getAvgAngularRateTriad(final AngularSpeedTriad result) {
647         result.setValueCoordinatesAndUnit(avgAngularRateX, avgAngularRateY, avgAngularRateZ,
648                 AngularSpeedUnit.RADIANS_PER_SECOND);
649     }
650 
651     /**
652      * Gets norm of estimated average angular speed expressed in radians per
653      * second (rad/s). This value is independent of body orientation.
654      *
655      * @return norm of estimated average angular speed.
656      */
657     public double getAvgAngularRateNorm() {
658         return Math.sqrt(avgAngularRateX * avgAngularRateX
659                 + avgAngularRateY * avgAngularRateY
660                 + avgAngularRateZ * avgAngularRateZ);
661     }
662 
663     /**
664      * Gets norm of estimated average angular speed.
665      * This value is independent of body orientation.
666      *
667      * @return norm of estimated average angular speed.
668      */
669     public AngularSpeed getAvgAngularRateNormAsMeasurement() {
670         return new AngularSpeed(getAvgAngularRateNorm(), AngularSpeedUnit.RADIANS_PER_SECOND);
671     }
672 
673     /**
674      * Gets norm of estimated average angular speed.
675      * This value is independent of body orientation.
676      *
677      * @param result instance where norm of estimated average angular speed will be stored.
678      */
679     public void getAvgAngularRateNormAsMeasurement(final AngularSpeed result) {
680         result.setValue(getAvgAngularRateNorm());
681         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
682     }
683 
684     /**
685      * Gets estimated average of body kinematics.
686      *
687      * @return estimated average of body kinematics.
688      */
689     public BodyKinematics getAvgBodyKinematics() {
690         final var result = new BodyKinematics();
691         getAvgBodyKinematics(result);
692         return result;
693     }
694 
695     /**
696      * Gets estimated average of body kinematics.
697      *
698      * @param result instance where estimated average of body kinematics will be stored.
699      */
700     public void getAvgBodyKinematics(final BodyKinematics result) {
701         result.setSpecificForceCoordinates(getAvgSpecificForceX(), getAvgSpecificForceY(), getAvgSpecificForceZ());
702         result.setAngularRateCoordinates(getAvgAngularRateX(), getAvgAngularRateY(), getAvgAngularRateZ());
703     }
704 
705     /**
706      * Gets estimated variance of x coordinate of accelerometer sensed specific force
707      * expressed in (m^2/s^4).
708      *
709      * @return estimated variance of x coordinate of sensed specific force.
710      */
711     public double getVarianceSpecificForceX() {
712         return varianceSpecificForceX;
713     }
714 
715     /**
716      * Gets estimated variance of y coordinate of accelerometer sensed specific
717      * force expressed in (m^2/s^4).
718      *
719      * @return estimated variance of y coordinate of sensed specific force.
720      */
721     public double getVarianceSpecificForceY() {
722         return varianceSpecificForceY;
723     }
724 
725     /**
726      * Gets estimated variance of z coordinate of accelerometer sensed specific
727      * force expressed in (m^2/s^4).
728      *
729      * @return estimated variance of z coordinate of sensed specific force.
730      */
731     public double getVarianceSpecificForceZ() {
732         return varianceSpecificForceZ;
733     }
734 
735     /**
736      * Gets estimated variance of x coordinate of gyroscope sensed angular rate
737      * expressed in (rad^2/s^2).
738      *
739      * @return estimated variance of x coordinate of sensed angular rate.
740      */
741     public double getVarianceAngularRateX() {
742         return varianceAngularRateX;
743     }
744 
745     /**
746      * Gets estimated variance of y coordinate of gyroscope sensed angular rate
747      * expressed in (rad^2/s^2).
748      *
749      * @return estimated variance of y coordinate of sensed angular rate.
750      */
751     public double getVarianceAngularRateY() {
752         return varianceAngularRateY;
753     }
754 
755     /**
756      * Gets estimated variance of z coordinate of gyroscope sensed angular rate
757      * expressed in (rad^2/s^2).
758      *
759      * @return estimated variance of z coordinate of sensed angular rate.
760      */
761     public double getVarianceAngularRateZ() {
762         return varianceAngularRateZ;
763     }
764 
765     /**
766      * Gets estimated standard deviation of x coordinate of accelerometer
767      * sensed specific force expressed in meters per squared second (m/s^2).
768      *
769      * @return estimated standard deviation of x coordinate of sensed specific
770      * force.
771      */
772     public double getStandardDeviationSpecificForceX() {
773         return Math.sqrt(varianceSpecificForceX);
774     }
775 
776     /**
777      * Gets estimated standard deviation of x coordinate of accelerometer
778      * sensed specific force.
779      *
780      * @return estimated standard deviation of x coordinate of sensed specific
781      * force.
782      */
783     public Acceleration getStandardDeviationSpecificForceXAsMeasurement() {
784         return new Acceleration(getStandardDeviationSpecificForceX(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
785     }
786 
787     /**
788      * Gets estimated standard deviation of x coordinate of accelerometer
789      * sensed specific force.
790      *
791      * @param result instance where estimated standard deviation of x
792      *               coordinate of sensed specific force will be stored.
793      */
794     public void getStandardDeviationSpecificForceXAsMeasurement(final Acceleration result) {
795         result.setValue(getStandardDeviationSpecificForceX());
796         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
797     }
798 
799     /**
800      * Gets estimated standard deviation of y coordinate of accelerometer
801      * sensed specific force expressed in meters per squared second (m/s^2).
802      *
803      * @return estimated standard deviation of y coordinate of sensed specific
804      * force.
805      */
806     public double getStandardDeviationSpecificForceY() {
807         return Math.sqrt(varianceSpecificForceY);
808     }
809 
810     /**
811      * Gets estimated standard deviation of y coordinate of accelerometer
812      * sensed specific force.
813      *
814      * @return estimated standard deviation of y coordinate of sensed specific
815      * force.
816      */
817     public Acceleration getStandardDeviationSpecificForceYAsMeasurement() {
818         return new Acceleration(getStandardDeviationSpecificForceY(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
819     }
820 
821     /**
822      * Gets estimated standard deviation of y coordinate of accelerometer
823      * sensed specific force.
824      *
825      * @param result instance where estimated standard deviation of y
826      *               coordinate of sensed specific force will be stored.
827      */
828     public void getStandardDeviationSpecificForceYAsMeasurement(final Acceleration result) {
829         result.setValue(getStandardDeviationSpecificForceY());
830         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
831     }
832 
833     /**
834      * Gets estimated standard deviation of z coordinate of accelerometer
835      * sensed specific force expressed in meters per squared second (m/s^2).
836      *
837      * @return estimated standard deviation of z coordinate of sensed specific
838      * force.
839      */
840     public double getStandardDeviationSpecificForceZ() {
841         return Math.sqrt(varianceSpecificForceZ);
842     }
843 
844     /**
845      * Gets estimated standard deviation of z coordinate of accelerometer
846      * sensed specific force.
847      *
848      * @return estimated standard deviation of z coordinate of sensed specific
849      * force.
850      */
851     public Acceleration getStandardDeviationSpecificForceZAsMeasurement() {
852         return new Acceleration(getStandardDeviationSpecificForceZ(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
853     }
854 
855     /**
856      * Gets estimated standard deviation of z coordinate of accelerometer
857      * sensed specific force.
858      *
859      * @param result instance where estimated standard deviation of z
860      *               coordinate of sensed specific force will be stored.
861      */
862     public void getStandardDeviationSpecificForceZAsMeasurement(final Acceleration result) {
863         result.setValue(getStandardDeviationSpecificForceZ());
864         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
865     }
866 
867     /**
868      * Gets estimated standard deviation triad of accelerometer measurements.
869      *
870      * @return estimated standard deviation triad of accelerometer measurements.
871      */
872     public AccelerationTriad getStandardDeviationSpecificForceTriad() {
873         return new AccelerationTriad(AccelerationUnit.METERS_PER_SQUARED_SECOND,
874                 getStandardDeviationSpecificForceX(),
875                 getStandardDeviationSpecificForceY(),
876                 getStandardDeviationSpecificForceZ());
877     }
878 
879     /**
880      * Gets estimated standard deviation triad of accelerometer measurements.
881      *
882      * @param result instance where estimated standard deviation triad of
883      *               accelerometer measurements will be stored.
884      */
885     public void getStandardDeviationSpecificForceTriad(final AccelerationTriad result) {
886         result.setValueCoordinatesAndUnit(getStandardDeviationSpecificForceX(),
887                 getStandardDeviationSpecificForceY(),
888                 getStandardDeviationSpecificForceZ(),
889                 AccelerationUnit.METERS_PER_SQUARED_SECOND);
890     }
891 
892     /**
893      * Gets norm of estimated standard deviation of accelerometer measurements
894      * expressed in meters per squared second (m/s^2).
895      *
896      * @return norm of estimated standard deviation of accelerometer
897      * measurements.
898      */
899     public double getStandardDeviationSpecificForceNorm() {
900         final var fx = getStandardDeviationSpecificForceX();
901         final var fy = getStandardDeviationSpecificForceY();
902         final var fz = getStandardDeviationSpecificForceZ();
903         return Math.sqrt(fx * fx + fy * fy + fz * fz);
904     }
905 
906     /**
907      * Gets norm of estimated standard deviation of accelerometer measurements.
908      *
909      * @return norm of estimated standard deviation of measurements.
910      */
911     public Acceleration getStandardDeviationSpecificForceNormAsMeasurement() {
912         return new Acceleration(getStandardDeviationSpecificForceNorm(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
913     }
914 
915     /**
916      * Gets norm of estimated standard deviation of accelerometer measurements.
917      *
918      * @param result instance where norm of estimated standard deviation will be
919      *               stored.
920      */
921     public void getStandardDeviationSpecificForceNormAsMeasurement(final Acceleration result) {
922         result.setValue(getStandardDeviationSpecificForceNorm());
923         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
924     }
925 
926     /**
927      * Gets average of estimated standard deviation coordinates of accelerometer
928      * measurements expressed in meters per squared second (m/s^2).
929      *
930      * @return average of estimated standard deviation coordinates.
931      */
932     public double getAverageStandardDeviationSpecificForce() {
933         final var fx = getStandardDeviationSpecificForceX();
934         final var fy = getStandardDeviationSpecificForceY();
935         final var fz = getStandardDeviationSpecificForceZ();
936         return (fx + fy + fz) / 3.0;
937     }
938 
939     /**
940      * Gets average of estimated standard deviation coordinates of accelerometer
941      * measurements.
942      *
943      * @return average of estimated standard deviation coordinates.
944      */
945     public Acceleration getAverageStandardDeviationSpecificForceAsMeasurement() {
946         return new Acceleration(getAverageStandardDeviationSpecificForce(), AccelerationUnit.METERS_PER_SQUARED_SECOND);
947     }
948 
949     /**
950      * Gets average of estimated standard deviation coordinates of accelerometer
951      * measurements.
952      *
953      * @param result instance where average of estimated standard deviation coordinates
954      *               will be stored.
955      */
956     public void getAverageStandardDeviationSpecificForceAsMeasurement(final Acceleration result) {
957         result.setValue(getAverageStandardDeviationSpecificForce());
958         result.setUnit(AccelerationUnit.METERS_PER_SQUARED_SECOND);
959     }
960 
961     /**
962      * Gets estimated standard deviation of x coordinate of gyroscope
963      * expressed in radians per second (rad/s).
964      *
965      * @return estimated standard deviation of x coordinate of gyroscope.
966      */
967     public double getStandardDeviationAngularRateX() {
968         return Math.sqrt(varianceAngularRateX);
969     }
970 
971     /**
972      * Gets estimated standard deviation of x coordinate of gyroscope.
973      *
974      * @return estimated standard deviation of x coordinate of gyroscope.
975      */
976     public AngularSpeed getStandardDeviationAngularRateXAsMeasurement() {
977         return new AngularSpeed(getStandardDeviationAngularRateX(), AngularSpeedUnit.RADIANS_PER_SECOND);
978     }
979 
980     /**
981      * Gets estimated standard deviation of x coordinate of gyroscope.
982      *
983      * @param result estimated standard deviation of x coordinate of gyroscope.
984      */
985     public void getStandardDeviationAngularRateXAsMeasurement(final AngularSpeed result) {
986         result.setValue(getStandardDeviationAngularRateX());
987         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
988     }
989 
990     /**
991      * Gets estimated standard deviation of y coordinate of gyroscope
992      * expressed in radians per second (rad/s).
993      *
994      * @return estimated standard deviation of y coordinate of gyroscope.
995      */
996     public double getStandardDeviationAngularRateY() {
997         return Math.sqrt(varianceAngularRateY);
998     }
999 
1000     /**
1001      * Gets estimated standard deviation of y coordinate of gyroscope.
1002      *
1003      * @return estimated standard deviation of y coordinate of gyroscope.
1004      */
1005     public AngularSpeed getStandardDeviationAngularRateYAsMeasurement() {
1006         return new AngularSpeed(getStandardDeviationAngularRateY(), AngularSpeedUnit.RADIANS_PER_SECOND);
1007     }
1008 
1009     /**
1010      * Gets estimated standard deviation of y coordinate of gyroscope.
1011      *
1012      * @param result estimated standard deviation of y coordinate of gyroscope.
1013      */
1014     public void getStandardDeviationAngularRateYAsMeasurement(final AngularSpeed result) {
1015         result.setValue(getStandardDeviationAngularRateY());
1016         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1017     }
1018 
1019     /**
1020      * Gets estimated standard deviation of z coordinate of gyroscope
1021      * expressed in radians per second (rad/s).
1022      *
1023      * @return estimated standard deviation of z coordinate of gyroscope.
1024      */
1025     public double getStandardDeviationAngularRateZ() {
1026         return Math.sqrt(varianceAngularRateZ);
1027     }
1028 
1029     /**
1030      * Gets estimated standard deviation of z coordinate of gyroscope.
1031      *
1032      * @return estimated standard deviation of z coordinate of gyroscope.
1033      */
1034     public AngularSpeed getStandardDeviationAngularRateZAsMeasurement() {
1035         return new AngularSpeed(getStandardDeviationAngularRateZ(), AngularSpeedUnit.RADIANS_PER_SECOND);
1036     }
1037 
1038     /**
1039      * Gets estimated standard deviation of z coordinate of gyroscope.
1040      *
1041      * @param result estimated standard deviation of z coordinate of gyroscope.
1042      */
1043     public void getStandardDeviationAngularRateZAsMeasurement(final AngularSpeed result) {
1044         result.setValue(getStandardDeviationAngularRateZ());
1045         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1046     }
1047 
1048     /**
1049      * Gets estimated standard deviation triad of angular speed measurements.
1050      *
1051      * @return estimated standard deviation triad of angular speed measurements.
1052      */
1053     public AngularSpeedTriad getStandardDeviationAngularSpeedTriad() {
1054         return new AngularSpeedTriad(AngularSpeedUnit.RADIANS_PER_SECOND,
1055                 getStandardDeviationAngularRateX(),
1056                 getStandardDeviationAngularRateY(),
1057                 getStandardDeviationAngularRateZ());
1058     }
1059 
1060     /**
1061      * Gets estimated standard deviation triad of angular speed measurements.
1062      *
1063      * @param result instance where estimated standard deviation triad of
1064      *               gyroscope measurements will be stored.
1065      */
1066     public void getStandardDeviationAngularSpeedTriad(final AngularSpeedTriad result) {
1067         result.setValueCoordinatesAndUnit(getStandardDeviationAngularRateX(),
1068                 getStandardDeviationAngularRateY(),
1069                 getStandardDeviationAngularRateZ(),
1070                 AngularSpeedUnit.RADIANS_PER_SECOND);
1071     }
1072 
1073     /**
1074      * Gets norm of estimated standard deviation of gyroscope measurements
1075      * expressed in radians per second (rad/s).
1076      *
1077      * @return norm of estimated standard deviation of gyroscope
1078      * measurements.
1079      */
1080     public double getStandardDeviationAngularSpeedNorm() {
1081         final var wx = getStandardDeviationAngularRateX();
1082         final var wy = getStandardDeviationAngularRateY();
1083         final var wz = getStandardDeviationAngularRateZ();
1084         return Math.sqrt(wx * wx + wy * wy + wz * wz);
1085     }
1086 
1087     /**
1088      * Gets norm of estimated standard deviation of gyroscope measurements.
1089      *
1090      * @return norm of estimated standard deviation of measurements.
1091      */
1092     public AngularSpeed getStandardDeviationAngularSpeedNormAsMeasurement() {
1093         return new AngularSpeed(getStandardDeviationAngularSpeedNorm(), AngularSpeedUnit.RADIANS_PER_SECOND);
1094     }
1095 
1096     /**
1097      * Gets norm of estimated standard deviation of gyroscope measurements.
1098      *
1099      * @param result instance where norm of estimated standard deviation will be
1100      *               stored.
1101      */
1102     public void getStandardDeviationAngularSpeedNormAsMeasurement(final AngularSpeed result) {
1103         result.setValue(getStandardDeviationAngularSpeedNorm());
1104         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1105     }
1106 
1107     /**
1108      * Gets average of estimated standard deviation coordinates of gyroscope
1109      * measurements expressed in radians per second (rad/s).
1110      *
1111      * @return average of estimated standard deviation coordinates.
1112      */
1113     public double getAverageStandardDeviationAngularSpeed() {
1114         final var wx = getStandardDeviationAngularRateX();
1115         final var wy = getStandardDeviationAngularRateY();
1116         final var wz = getStandardDeviationAngularRateZ();
1117         return (wx + wy + wz) / 3.0;
1118     }
1119 
1120     /**
1121      * Gets average of estimated standard deviation coordinates of gyroscope
1122      * measurements.
1123      *
1124      * @return average of estimated standard deviation coordinates.
1125      */
1126     public AngularSpeed getAverageStandardDeviationAngularSpeedAsMeasurement() {
1127         return new AngularSpeed(getAverageStandardDeviationAngularSpeed(), AngularSpeedUnit.RADIANS_PER_SECOND);
1128     }
1129 
1130     /**
1131      * Gets average of estimated standard deviation coordinates of gyroscope
1132      * measurements.
1133      *
1134      * @param result instance where average of estimated standard deviation coordinates
1135      *               will be stored.
1136      */
1137     public void getAverageStandardDeviationAngularSpeedAsMeasurement(final AngularSpeed result) {
1138         result.setValue(getAverageStandardDeviationAngularSpeed());
1139         result.setUnit(AngularSpeedUnit.RADIANS_PER_SECOND);
1140     }
1141 
1142     /**
1143      * Gets estimated standard deviations of accelerometer and gyroscope components
1144      * as a body kinematics instance.
1145      *
1146      * @return a body kinematics instance containing standard deviation values.
1147      */
1148     public BodyKinematics getStandardDeviationAsBodyKinematics() {
1149         return new BodyKinematics(getStandardDeviationSpecificForceX(),
1150                 getStandardDeviationSpecificForceY(),
1151                 getStandardDeviationSpecificForceZ(),
1152                 getStandardDeviationAngularRateX(),
1153                 getStandardDeviationAngularRateY(),
1154                 getStandardDeviationAngularRateZ());
1155     }
1156 
1157     /**
1158      * Gets estimated standard deviations of accelerometer and gyroscope components
1159      * as a body kinematics instance.
1160      *
1161      * @param result instance where data will be stored.
1162      */
1163     public void getStandardDeviationAsBodyKinematics(final BodyKinematics result) {
1164         result.setSpecificForceCoordinates(getStandardDeviationSpecificForceX(),
1165                 getStandardDeviationSpecificForceY(),
1166                 getStandardDeviationSpecificForceZ());
1167         result.setAngularRateCoordinates(getStandardDeviationAngularRateX(),
1168                 getStandardDeviationAngularRateY(),
1169                 getStandardDeviationAngularRateZ());
1170     }
1171 
1172     /**
1173      * Gets accelerometer noise PSD (Power Spectral Density) on x axis expressed
1174      * in (m^2 * s^-3).
1175      *
1176      * @return accelerometer noise PSD on x axis.
1177      */
1178     public double getSpecificForcePsdX() {
1179         return varianceSpecificForceX * timeInterval;
1180     }
1181 
1182     /**
1183      * Gets accelerometer noise PSD (Power Spectral Density) on y axis expressed
1184      * in (m^2 * s^-3).
1185      *
1186      * @return accelerometer noise PSD on y axis.
1187      */
1188     public double getSpecificForcePsdY() {
1189         return varianceSpecificForceY * timeInterval;
1190     }
1191 
1192     /**
1193      * Gets accelerometer noise PSD (Power Spectral Density) on z axis expressed
1194      * in (m^2 * s^-3).
1195      *
1196      * @return accelerometer noise PSD on z axis.
1197      */
1198     public double getSpecificForcePsdZ() {
1199         return varianceSpecificForceZ * timeInterval;
1200     }
1201 
1202     /**
1203      * Gets gyroscope noise PSD (Power Spectral Density) on x axis expressed
1204      * in (rad^2/s).
1205      *
1206      * @return gyroscope noise PSD on x axis.
1207      */
1208     public double getAngularRatePsdX() {
1209         return varianceAngularRateX * timeInterval;
1210     }
1211 
1212     /**
1213      * Gets gyroscope noise PSD (Power Spectral Density) on y axis expressed
1214      * in (rad^2/s).
1215      *
1216      * @return gyroscope noise PSD on y axis.
1217      */
1218     public double getAngularRatePsdY() {
1219         return varianceAngularRateY * timeInterval;
1220     }
1221 
1222     /**
1223      * Gets gyroscope noise PSD (Power Spectral Density) on z axis expressed
1224      * in (rad^2/s).
1225      *
1226      * @return gyroscope noise PSD on z axis.
1227      */
1228     public double getAngularRatePsdZ() {
1229         return varianceAngularRateZ * timeInterval;
1230     }
1231 
1232     /**
1233      * Gets accelerometer noise root PSD (Power Spectral Density) on x axis
1234      * expressed in (m * s^-1.5).
1235      *
1236      * @return accelerometer noise root PSD on x axis.
1237      */
1238     public double getSpecificForceRootPsdX() {
1239         return Math.sqrt(getSpecificForcePsdX());
1240     }
1241 
1242     /**
1243      * Gets accelerometer noise root PSD (Power Spectral Density) on y axis
1244      * expressed in (m * s^-1.5).
1245      *
1246      * @return accelerometer noise root PSD on y axis.
1247      */
1248     public double getSpecificForceRootPsdY() {
1249         return Math.sqrt(getSpecificForcePsdY());
1250     }
1251 
1252     /**
1253      * Gets accelerometer noise root PSD (Power Spectral Density) on z axis
1254      * expressed in (m * s^-1.5).
1255      *
1256      * @return accelerometer noise root PSD on z axis.
1257      */
1258     public double getSpecificForceRootPsdZ() {
1259         return Math.sqrt(getSpecificForcePsdZ());
1260     }
1261 
1262     /**
1263      * Gets gyroscope noise root PSD (Power Spectral Density) on x axis
1264      * expressed in (rad * s^-0.5).
1265      *
1266      * @return gyroscope noise root PSD on x axis.
1267      */
1268     public double getAngularRateRootPsdX() {
1269         return Math.sqrt(getAngularRatePsdX());
1270     }
1271 
1272     /**
1273      * Gets gyroscope noise root PSD (Power Spectral Density) on y axis
1274      * expressed in (rad * s^-0.5).
1275      *
1276      * @return gyroscope noise root PSD on y axis.
1277      */
1278     public double getAngularRateRootPsdY() {
1279         return Math.sqrt(getAngularRatePsdY());
1280     }
1281 
1282     /**
1283      * Gets gyroscope noise root PSD (Power Spectral Density) on z axis
1284      * expressed in (rad * s^-0.5).
1285      *
1286      * @return gyroscope noise root PSD on z axis.
1287      */
1288     public double getAngularRateRootPsdZ() {
1289         return Math.sqrt(getAngularRatePsdZ());
1290     }
1291 
1292     /**
1293      * Gets average accelerometer noise PSD (Power Spectral Density) among
1294      * x,y,z components expressed as (m^2/s^-3).
1295      *
1296      * @return average accelerometer noise PSD.
1297      */
1298     public double getAvgSpecificForceNoisePsd() {
1299         return (getSpecificForcePsdX() + getSpecificForcePsdY() + getSpecificForcePsdZ()) / 3.0;
1300     }
1301 
1302     /**
1303      * Gets norm of noise root PSD (Power Spectral Density) among x,y,z
1304      * components expressed as (m * s^-1.5).
1305      *
1306      * @return norm of noise root PSD.
1307      */
1308     public double getSpecificForceNoiseRootPsdNorm() {
1309         return Math.sqrt(getSpecificForcePsdX() + getSpecificForcePsdY() + getSpecificForcePsdZ());
1310     }
1311 
1312     /**
1313      * Gets average gyroscope noise PSD (Power Spectral Density) among
1314      * x,y,z components expressed in (rad^2/s).
1315      *
1316      * @return average gyroscope noise PSD.
1317      */
1318     public double getAvgAngularRateNoisePsd() {
1319         return (getAngularRatePsdX() + getAngularRatePsdY() + getAngularRatePsdZ()) / 3.0;
1320     }
1321 
1322     /**
1323      * Gets norm of noise root PSD (Power Spectral Density) among x,y,z
1324      * components expressed as (rad * s^-0.5).
1325      *
1326      * @return norm of noise root PSD.
1327      */
1328     public double getAngularRateNoiseRootPsdNorm() {
1329         return Math.sqrt(getAngularRatePsdX() + getAngularRatePsdY() + getAngularRatePsdZ());
1330     }
1331 
1332     /**
1333      * Gets number of samples that have been processed so far.
1334      *
1335      * @return number of samples that have been processed so far.
1336      */
1337     public int getNumberOfProcessedSamples() {
1338         return numberOfProcessedSamples;
1339     }
1340 
1341     /**
1342      * Gets number of currently windowed samples.
1343      *
1344      * @return number of samples within the window.
1345      */
1346     public int getNumberOfSamplesInWindow() {
1347         return windowedSamples.size();
1348     }
1349 
1350     /**
1351      * Indicates whether estimator is currently running or not.
1352      *
1353      * @return true if estimator is running, false otherwise.
1354      */
1355     public boolean isRunning() {
1356         return running;
1357     }
1358 
1359     /**
1360      * Indicates whether window of samples is filled or not.
1361      *
1362      * @return true if window is filled, false otherwise.
1363      */
1364     public boolean isWindowFilled() {
1365         return getNumberOfSamplesInWindow() == windowSize;
1366     }
1367 
1368     /**
1369      * Adds a body kinematics measurement and processes current window.
1370      *
1371      * @param specificForceX x coordinate of specific force expressed in meters per squared second (m/s^2).
1372      * @param specificForceY y coordinate of specific force expressed in meters per squared second (m/s^2).
1373      * @param specificForceZ z coordinate of specific force expressed in meters per squared second (m/s^2).
1374      * @param angularRateX   x coordinate of angular rate expressed in radians per second (rad/s).
1375      * @param angularRateY   y coordinate of angular rate expressed in radians per second (rad/s).
1376      * @param angularRateZ   z coordinate of angular rate expressed in radians per second (rad/s).
1377      * @return true if provided kinematics instance has been processed, false if it has
1378      * been ignored.
1379      * @throws LockedException if estimator is currently running.
1380      */
1381     public boolean addBodyKinematicsAndProcess(
1382             final double specificForceX, final double specificForceY, final double specificForceZ,
1383             final double angularRateX, final double angularRateY, final double angularRateZ) throws LockedException {
1384         return addBodyKinematicsAndProcess(new BodyKinematics(specificForceX, specificForceY, specificForceZ,
1385                 angularRateX, angularRateY, angularRateZ));
1386     }
1387 
1388     /**
1389      * Adds a body kinematics measurement and processes current window.
1390      *
1391      * @param specificForceX x coordinate of specific force.
1392      * @param specificForceY y coordinate of specific force.
1393      * @param specificForceZ z coordinate of specific force.
1394      * @param angularRateX   x coordinate of angular rate.
1395      * @param angularRateY   y coordinate of angular rate.
1396      * @param angularRateZ   z coordinate of angular rate.
1397      * @return true if provided kinematics instance has been processed, false if it has
1398      * been ignored.
1399      * @throws LockedException if estimator is currently running.
1400      */
1401     public boolean addBodyKinematicsAndProcess(
1402             final Acceleration specificForceX, final Acceleration specificForceY, final Acceleration specificForceZ,
1403             final AngularSpeed angularRateX, final AngularSpeed angularRateY, final AngularSpeed angularRateZ)
1404             throws LockedException {
1405         return addBodyKinematicsAndProcess(new BodyKinematics(specificForceX, specificForceY, specificForceZ,
1406                 angularRateX, angularRateY, angularRateZ));
1407     }
1408 
1409     /**
1410      * Adds a body kinematics measurement and processes current window.
1411      *
1412      * @param specificForce specific force triad.
1413      * @param angularSpeed  angular speed triad.
1414      * @return true if provided kinematics instance has been processed, false if it has
1415      * been ignored.
1416      * @throws LockedException if estimator is currently running.
1417      */
1418     public boolean addBodyKinematicsAndProcess(
1419             final AccelerationTriad specificForce, final AngularSpeedTriad angularSpeed) throws LockedException {
1420         return addBodyKinematicsAndProcess(new BodyKinematics(specificForce, angularSpeed));
1421     }
1422 
1423     /**
1424      * Adds a body kinematics measurement and processes current window.
1425      *
1426      * @param kinematics body kinematics to be added and processed.
1427      * @return true if provided kinematics instance has been processed, false if it has
1428      * been ignored.
1429      * @throws LockedException if estimator is currently running.
1430      */
1431     public boolean addBodyKinematicsAndProcess(final BodyKinematics kinematics) throws LockedException {
1432         return internalAdd(kinematics, true);
1433     }
1434 
1435     /**
1436      * Adds a body kinematics measurement.
1437      *
1438      * @param specificForceX x coordinate of specific force expressed in meters per squared second (m/s^2).
1439      * @param specificForceY y coordinate of specific force expressed in meters per squared second (m/s^2).
1440      * @param specificForceZ z coordinate of specific force expressed in meters per squared second (m/s^2).
1441      * @param angularRateX   x coordinate of angular rate expressed in radians per second (rad/s).
1442      * @param angularRateY   y coordinate of angular rate expressed in radians per second (rad/s).
1443      * @param angularRateZ   z coordinate of angular rate expressed in radians per second (rad/s).
1444      * @throws LockedException if estimator is currently running.
1445      */
1446     public void addBodyKinematics(
1447             final double specificForceX, final double specificForceY, final double specificForceZ,
1448             final double angularRateX, final double angularRateY, final double angularRateZ) throws LockedException {
1449         addBodyKinematics(new BodyKinematics(specificForceX, specificForceY, specificForceZ,
1450                 angularRateX, angularRateY, angularRateZ));
1451     }
1452 
1453     /**
1454      * Adds a body kinematics measurement.
1455      *
1456      * @param specificForceX x coordinate of specific force.
1457      * @param specificForceY y coordinate of specific force.
1458      * @param specificForceZ z coordinate of specific force.
1459      * @param angularRateX   x coordinate of angular rate.
1460      * @param angularRateY   y coordinate of angular rate.
1461      * @param angularRateZ   z coordinate of angular rate.
1462      * @throws LockedException if estimator is currently running.
1463      */
1464     public void addBodyKinematics(
1465             final Acceleration specificForceX, final Acceleration specificForceY, final Acceleration specificForceZ,
1466             final AngularSpeed angularRateX, final AngularSpeed angularRateY, final AngularSpeed angularRateZ)
1467             throws LockedException {
1468         addBodyKinematics(new BodyKinematics(specificForceX, specificForceY, specificForceZ,
1469                 angularRateX, angularRateY, angularRateZ));
1470     }
1471 
1472     /**
1473      * Adds a body kinematics measurement.
1474      *
1475      * @param specificForce specific force triad.
1476      * @param angularSpeed  angular speed triad.
1477      * @throws LockedException if estimator is currently running.
1478      */
1479     public void addBodyKinematics(
1480             final AccelerationTriad specificForce, final AngularSpeedTriad angularSpeed) throws LockedException {
1481         addBodyKinematics(new BodyKinematics(specificForce, angularSpeed));
1482     }
1483 
1484     /**
1485      * Adds a body kinematics measurement.
1486      *
1487      * @param kinematics body kinematics to be added.
1488      * @throws LockedException if estimator is currently running.
1489      */
1490     public void addBodyKinematics(final BodyKinematics kinematics) throws LockedException {
1491         internalAdd(kinematics, false);
1492     }
1493 
1494     /**
1495      * Resets current estimator.
1496      *
1497      * @return true if estimator was successfully reset, false if no reset was needed.
1498      * @throws LockedException if estimator is currently running.
1499      */
1500     public boolean reset() throws LockedException {
1501         if (running) {
1502             throw new LockedException();
1503         }
1504 
1505         if (numberOfProcessedSamples == 0) {
1506             return false;
1507         }
1508 
1509         windowedSamples.clear();
1510         avgSpecificForceX = 0.0;
1511         avgSpecificForceY = 0.0;
1512         avgSpecificForceZ = 0.0;
1513         avgAngularRateX = 0.0;
1514         avgAngularRateY = 0.0;
1515         avgAngularRateZ = 0.0;
1516         varianceSpecificForceX = 0.0;
1517         varianceSpecificForceY = 0.0;
1518         varianceSpecificForceZ = 0.0;
1519         varianceAngularRateX = 0.0;
1520         varianceAngularRateY = 0.0;
1521         varianceAngularRateZ = 0.0;
1522         numberOfProcessedSamples = 0;
1523 
1524         if (listener != null) {
1525             listener.onReset(this);
1526         }
1527 
1528         return true;
1529     }
1530 
1531     /**
1532      * Internally adds a body kinematics measurement and processes current window if indicated.
1533      *
1534      * @param kinematics body kinematics to be added.
1535      * @param process    true if window of samples must also be processed, false otherwise.
1536      * @return true if result values were updated, false if not enough samples are available yet
1537      * and no average or variance values have been computed yet.
1538      * @throws LockedException if estimator is currently running.
1539      */
1540     private boolean internalAdd(final BodyKinematics kinematics, boolean process) throws LockedException {
1541         if (running) {
1542             throw new LockedException();
1543         }
1544 
1545         running = true;
1546 
1547         if (windowedSamples.isEmpty() && listener != null) {
1548             listener.onStart(this);
1549         }
1550 
1551         final var wasFilled = isWindowFilled();
1552         if (wasFilled) {
1553             // remove first sample
1554             windowedSamples.removeFirst();
1555         }
1556 
1557         windowedSamples.addLast(new BodyKinematics(kinematics));
1558 
1559         // process window
1560         final var result = process && processWindow();
1561 
1562         running = false;
1563 
1564         if (listener != null) {
1565             listener.onBodyKinematicsAdded(this);
1566 
1567             if (!wasFilled && isWindowFilled()) {
1568                 listener.onWindowFilled(this);
1569             }
1570         }
1571 
1572         return result;
1573     }
1574 
1575     /**
1576      * Processes current windowed samples.
1577      *
1578      * @return true if sample was processed, false it there are not enough samples to
1579      * process current window.
1580      */
1581     private boolean processWindow() {
1582         numberOfProcessedSamples++;
1583 
1584         final var n = windowedSamples.size();
1585         if (n <= 1) {
1586             return false;
1587         }
1588 
1589         // compute averages
1590         var avgFx = 0.0;
1591         var avgFy = 0.0;
1592         var avgFz = 0.0;
1593         var avgWx = 0.0;
1594         var avgWy = 0.0;
1595         var avgWz = 0.0;
1596         for (final var kinematics : windowedSamples) {
1597             final var fx = kinematics.getFx();
1598             final var fy = kinematics.getFy();
1599             final var fz = kinematics.getFz();
1600             final var wx = kinematics.getAngularRateX();
1601             final var wy = kinematics.getAngularRateY();
1602             final var wz = kinematics.getAngularRateZ();
1603 
1604             avgFx += fx;
1605             avgFy += fy;
1606             avgFz += fz;
1607             avgWx += wx;
1608             avgWy += wy;
1609             avgWz += wz;
1610         }
1611 
1612         avgFx /= n;
1613         avgFy /= n;
1614         avgFz /= n;
1615         avgWx /= n;
1616         avgWy /= n;
1617         avgWz /= n;
1618 
1619         // compute variances
1620         var varFx = 0.0;
1621         var varFy = 0.0;
1622         var varFz = 0.0;
1623         var varWx = 0.0;
1624         var varWy = 0.0;
1625         var varWz = 0.0;
1626         for (final var kinematics : windowedSamples) {
1627             final var fx = kinematics.getFx();
1628             final var fy = kinematics.getFy();
1629             final var fz = kinematics.getFz();
1630             final var wx = kinematics.getAngularRateX();
1631             final var wy = kinematics.getAngularRateY();
1632             final var wz = kinematics.getAngularRateZ();
1633 
1634             final var diffFx = fx - avgFx;
1635             final var diffFy = fy - avgFy;
1636             final var diffFz = fz - avgFz;
1637             final var diffWx = wx - avgWx;
1638             final var diffWy = wy - avgWy;
1639             final var diffWz = wz - avgWz;
1640 
1641             final var diffFx2 = diffFx * diffFx;
1642             final var diffFy2 = diffFy * diffFy;
1643             final var diffFz2 = diffFz * diffFz;
1644             final var diffWx2 = diffWx * diffWx;
1645             final var diffWy2 = diffWy * diffWy;
1646             final var diffWz2 = diffWz * diffWz;
1647 
1648             varFx += diffFx2;
1649             varFy += diffFy2;
1650             varFz += diffFz2;
1651             varWx += diffWx2;
1652             varWy += diffWy2;
1653             varWz += diffWz2;
1654         }
1655 
1656         final var nMinusOne = n - 1;
1657 
1658         varFx /= nMinusOne;
1659         varFy /= nMinusOne;
1660         varFz /= nMinusOne;
1661         varWx /= nMinusOne;
1662         varWy /= nMinusOne;
1663         varWz /= nMinusOne;
1664 
1665         avgSpecificForceX = avgFx;
1666         avgSpecificForceY = avgFy;
1667         avgSpecificForceZ = avgFz;
1668         avgAngularRateX = avgWx;
1669         avgAngularRateY = avgWy;
1670         avgAngularRateZ = avgWz;
1671 
1672         varianceSpecificForceX = varFx;
1673         varianceSpecificForceY = varFy;
1674         varianceSpecificForceZ = varFz;
1675         varianceAngularRateX = varWx;
1676         varianceAngularRateY = varWy;
1677         varianceAngularRateZ = varWz;
1678 
1679         return true;
1680     }
1681 
1682     /**
1683      * Gets accelerometer base noise level root PSD (Power Spectral Density)
1684      * expressed in (m * s^-1.5).
1685      *
1686      * @return accelerometer base noise level root PSD.
1687      */
1688     @Override
1689     public double getAccelerometerBaseNoiseLevelRootPsd() {
1690         return getSpecificForceNoiseRootPsdNorm();
1691     }
1692 
1693     /**
1694      * Gets gyroscope base noise level root PSD (Power Spectral Density)
1695      * expressed in (rad * s^-0.5)
1696      *
1697      * @return gyroscope base noise level root PSD.
1698      */
1699     @Override
1700     public double getGyroscopeBaseNoiseLevelRootPsd() {
1701         return getAngularRateNoiseRootPsdNorm();
1702     }
1703 }