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.calibration.TimeIntervalEstimator;
20  import com.irurueta.navigation.inertial.calibration.Triad;
21  import com.irurueta.units.Measurement;
22  import com.irurueta.units.Time;
23  import com.irurueta.units.TimeConverter;
24  import com.irurueta.units.TimeUnit;
25  
26  /**
27   * Base class to estimate measurement noise variances and PSD's (Power Spectral Densities)
28   * along with their average values.
29   * Implementations of this estimator must be used when the body where the measurement device
30   * is attached to remains static on the same position with zero velocity, or
31   * with constant angular speed and orientation while capturing data.
32   * To compute PSD's, this estimator assumes that measurement samples are obtained
33   * at a constant provided rate equal to {@link #getTimeInterval()} seconds.
34   * If not available, accelerometer sampling rate average can be estimated using
35   * {@link TimeIntervalEstimator}.
36   * This estimator does NOT require the knowledge of current location and body
37   * orientation.
38   * Because body location and orientation is not known, estimated average values
39   * cannot be used to determine biases. Only norm of noise estimations
40   * (variance or standard deviation) can be safely used.
41   * Notice that this estimator uses a biased variance estimator, which for a large
42   * number of samples converges to the unbiased variance estimator.
43   *
44   * @param <U> a measurement unit type.
45   * @param <M> a measurement type.
46   * @param <T> a triad type.
47   * @param <E> an estimator type.
48   * @param <L> a listener type.
49   */
50  public abstract class AccumulatedTriadNoiseEstimator<U extends Enum<?>,
51          M extends Measurement<U>, T extends Triad<U, M, T>,
52          E extends AccumulatedTriadNoiseEstimator<U, M, T, E, L>,
53          L extends AccumulatedTriadNoiseEstimatorListener<U, M, T, E>> {
54  
55      /**
56       * Default time interval between accelerometer samples expressed in seconds
57       * (s).
58       */
59      public static final double DEFAULT_TIME_INTERVAL_SECONDS = 0.02;
60  
61      /**
62       * Time interval expressed in seconds (s) between consecutive accelerometer
63       * samples.
64       */
65      private double timeInterval = DEFAULT_TIME_INTERVAL_SECONDS;
66  
67      /**
68       * Listener to handle events raised by this estimator.
69       */
70      private L listener;
71  
72      /**
73       * Last provided triad.
74       */
75      private T lastTriad;
76  
77      /**
78       * Contains estimated average of x coordinate of measurement expressed in its default
79       * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
80       */
81      private double avgX;
82  
83      /**
84       * Contains estimated average of y coordinate of measurement expressed in its default
85       * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
86       */
87      private double avgY;
88  
89      /**
90       * Contains estimated average of z coordinate of measurement expressed in its default
91       * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
92       */
93      private double avgZ;
94  
95      /**
96       * Contains estimated variance of x coordinate of measurement expressed in its default
97       * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
98       * flux density).
99       */
100     private double varianceX;
101 
102     /**
103      * Contains estimated variance of y coordinate of measurement expressed in its default
104      * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
105      * flux density).
106      */
107     private double varianceY;
108 
109     /**
110      * Contains estimated variance of x coordinate of measurement expressed in its default
111      * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
112      * flux density).
113      */
114     private double varianceZ;
115 
116     /**
117      * Number of processed body kinematics samples.
118      */
119     private int numberOfProcessedSamples;
120 
121     /**
122      * Number of processed timestamp samples plus one.
123      */
124     private int numberOfProcessedSamplesPlusOne = 1;
125 
126     /**
127      * Indicates that estimator is running.
128      */
129     private boolean running;
130 
131     /**
132      * Constructor.
133      */
134     protected AccumulatedTriadNoiseEstimator() {
135     }
136 
137     /**
138      * Constructor.
139      *
140      * @param listener listener to handle events raised by this estimator.
141      */
142     protected AccumulatedTriadNoiseEstimator(final L listener) {
143         this.listener = listener;
144     }
145 
146     /**
147      * Gets time interval between triad samples expressed in
148      * seconds (s).
149      *
150      * @return time interval between accelerometer triad samples.
151      */
152     public double getTimeInterval() {
153         return timeInterval;
154     }
155 
156     /**
157      * Sets time interval between triad samples expressed in
158      * seconds (s).
159      *
160      * @param timeInterval time interval between triad samples.
161      * @throws IllegalArgumentException if provided value is negative.
162      * @throws LockedException          if estimator is currently running.
163      */
164     public void setTimeInterval(final double timeInterval) throws LockedException {
165         if (running) {
166             throw new LockedException();
167         }
168 
169         if (timeInterval < 0.0) {
170             throw new IllegalArgumentException();
171         }
172 
173         this.timeInterval = timeInterval;
174     }
175 
176     /**
177      * Gets time interval between triad samples.
178      *
179      * @return time interval between triad samples.
180      */
181     public Time getTimeIntervalAsTime() {
182         return new Time(timeInterval, TimeUnit.SECOND);
183     }
184 
185     /**
186      * Gets time interval between triad samples.
187      *
188      * @param result instance where time interval will be stored.
189      */
190     public void getTimeIntervalAsTime(final Time result) {
191         result.setValue(timeInterval);
192         result.setUnit(TimeUnit.SECOND);
193     }
194 
195     /**
196      * Sets time interval between triad samples.
197      *
198      * @param timeInterval time interval between triad samples.
199      * @throws LockedException if estimator is currently running.
200      */
201     public void setTimeInterval(final Time timeInterval) throws LockedException {
202         setTimeInterval(TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(),
203                 TimeUnit.SECOND));
204     }
205 
206     /**
207      * Gets listener to handle events raised by this estimator.
208      *
209      * @return listener to handle events raised by this estimator.
210      */
211     public L getListener() {
212         return listener;
213     }
214 
215     /**
216      * Sets listener to handle events raised by this estimator.
217      *
218      * @param listener listener to handle events raised by this estimator.
219      * @throws LockedException if this estimator is running.
220      */
221     public void setListener(final L listener) throws LockedException {
222         if (running) {
223             throw new LockedException();
224         }
225 
226         this.listener = listener;
227     }
228 
229     /**
230      * Gets last provided triad values or null if not available.
231      *
232      * @return last provided triad values or null.
233      */
234     public T getLastTriad() {
235         return lastTriad;
236     }
237 
238     /**
239      * Gets last provided triad values.
240      *
241      * @param result instance where last provided triad will be stored.
242      * @return true if result instance was updated, false otherwise.
243      */
244     public boolean getLastTriad(final T result) {
245         if (lastTriad != null) {
246             lastTriad.copyTo(result);
247             return true;
248         } else {
249             return false;
250         }
251     }
252 
253     /**
254      * Gets estimated average of x coordinate of measurement expressed in its default
255      * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
256      * This value will depend of body location and orientation, hence it should never
257      * be used as a calibration bias.
258      *
259      * @return average of x coordinate of measurement in current window.
260      */
261     public double getAvgX() {
262         return avgX;
263     }
264 
265     /**
266      * Gets estimated average of x coordinate of measurement within current window.
267      * This value will depend of body location and orientation, hence it should never
268      * be used as a calibration bias.
269      *
270      * @return average of x coordinate of measurement in current window.
271      */
272     public M getAvgXAsMeasurement() {
273         return createMeasurement(avgX, getDefaultUnit());
274     }
275 
276     /**
277      * Gets estimated average of x coordinate of measurement within current window.
278      * This value will depend of body location and orientation, hence it should never
279      * be used as a calibration bias.
280      *
281      * @param result instance where average of x coordinate of measurement will be stored.
282      */
283     public void getAvgXAsMeasurement(final M result) {
284         result.setValue(avgX);
285         result.setUnit(getDefaultUnit());
286     }
287 
288     /**
289      * Gets estimated average of y coordinate of measurement expressed in its default
290      * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
291      * This value will depend of body location and orientation, hence it should never
292      * be used as a calibration bias.
293      *
294      * @return average of y coordinate of measurement in current window.
295      */
296     public double getAvgY() {
297         return avgY;
298     }
299 
300     /**
301      * Gets estimated average of y coordinate of measurement within current window.
302      * This value will depend of body location and orientation, hence it should never
303      * be used as a calibration bias.
304      *
305      * @return average of y coordinate of measurement in current window.
306      */
307     public M getAvgYAsMeasurement() {
308         return createMeasurement(avgY, getDefaultUnit());
309     }
310 
311     /**
312      * Gets estimated average of y coordinate of measurement within current window.
313      * This value will depend of body location and orientation, hence it should never
314      * be used as a calibration bias.
315      *
316      * @param result instance where average of y coordinate of measurement will be stored.
317      */
318     public void getAvgYAsMeasurement(final M result) {
319         result.setValue(avgY);
320         result.setUnit(getDefaultUnit());
321     }
322 
323     /**
324      * Gets estimated average of z coordinate of measurement expressed in its default
325      * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
326      * This value will depend of body location and orientation, hence it should never
327      * be used as a calibration bias.
328      *
329      * @return average of z coordinate of measurement in current window.
330      */
331     public double getAvgZ() {
332         return avgZ;
333     }
334 
335     /**
336      * Gets estimated average of z coordinate of measurement within current window.
337      * This value will depend of body location and orientation, hence it should never
338      * be used as a calibration bias.
339      *
340      * @return average of z coordinate of measurement in current window.
341      */
342     public M getAvgZAsMeasurement() {
343         return createMeasurement(avgZ, getDefaultUnit());
344     }
345 
346     /**
347      * Gets estimated average of z coordinate of measurement within current window.
348      * This value will depend of body location and orientation, hence it should never
349      * be used as a calibration bias.
350      *
351      * @param result instance where average of z coordinate of measurement will be stored.
352      */
353     public void getAvgZAsMeasurement(final M result) {
354         result.setValue(avgZ);
355         result.setUnit(getDefaultUnit());
356     }
357 
358     /**
359      * Gets estimated average as a measurement triad.
360      *
361      * @return average measurement triad.
362      */
363     public T getAvgTriad() {
364         return createTriad(avgX, avgY, avgZ, getDefaultUnit());
365     }
366 
367     /**
368      * Gets estimated average as a measurement triad.
369      *
370      * @param result instance where average values and unit will be stored.
371      */
372     public void getAvgTriad(final T result) {
373         result.setValueCoordinatesAndUnit(avgX, avgY, avgZ, getDefaultUnit());
374     }
375 
376     /**
377      * Gets norm of estimated average measurement expressed in its default
378      * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
379      * This value is independent of body orientation.
380      *
381      * @return norm of estimated average specific force.
382      */
383     public double getAvgNorm() {
384         return Math.sqrt(avgX * avgX + avgY * avgY + avgZ * avgZ);
385     }
386 
387     /**
388      * Gets norm of estimated average measurement within current window.
389      *
390      * @return norm of estimated average measurement.
391      */
392     public M getAvgNormAsMeasurement() {
393         return createMeasurement(getAvgNorm(), getDefaultUnit());
394     }
395 
396     /**
397      * Gets norm of estimated average measurement within current window.
398      *
399      * @param result instance where norm of estimated average measurement will be stored.
400      */
401     public void getAvgNormAsMeasurement(final M result) {
402         result.setValue(getAvgNorm());
403         result.setUnit(getDefaultUnit());
404     }
405 
406     /**
407      * Gets estimated variance of x coordinate of measurement within current window
408      * expressed in its default squared unit (m^2/s^4 for acceleration,
409      * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
410      *
411      * @return estimated variance of x coordinate of measurement within current
412      * window.
413      */
414     public double getVarianceX() {
415         return varianceX;
416     }
417 
418     /**
419      * Gets estimated variance of y coordinate of measurement within current window
420      * expressed in its default squared unit (m^2/s^4 for acceleration,
421      * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
422      *
423      * @return estimated variance of y coordinate of measurement within current
424      * window.
425      */
426     public double getVarianceY() {
427         return varianceY;
428     }
429 
430     /**
431      * Gets estimated variance of z coordinate of measurement within current window
432      * expressed in its default squared unit (m^2/s^4 for acceleration,
433      * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
434      *
435      * @return estimated variance of z coordinate of measurement within current
436      * window.
437      */
438     public double getVarianceZ() {
439         return varianceZ;
440     }
441 
442     /**
443      * Gets estimated standard deviation of x coordinate of measurement within current
444      * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
445      * angular speed or T for magnetic flux density).
446      *
447      * @return estimated standard deviation of x coordinate of measurement within
448      * current window.
449      */
450     public double getStandardDeviationX() {
451         return Math.sqrt(varianceX);
452     }
453 
454     /**
455      * Gets estimated standard deviation of x coordinate of measurement within current
456      * window.
457      *
458      * @return estimated standard deviation of x coordinate of measurement.
459      */
460     public M getStandardDeviationXAsMeasurement() {
461         return createMeasurement(getStandardDeviationX(), getDefaultUnit());
462     }
463 
464     /**
465      * Gets estimated standard deviation of x coordinate of measurement within current
466      * window.
467      *
468      * @param result instance where estimated standard deviation of x coordinate of
469      *               measurement will be stored.
470      */
471     public void getStandardDeviationXAsMeasurement(final M result) {
472         result.setValue(getStandardDeviationX());
473         result.setUnit(getDefaultUnit());
474     }
475 
476     /**
477      * Gets estimated standard deviation of y coordinate of measurement within current
478      * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
479      * angular speed or T for magnetic flux density).
480      *
481      * @return estimated standard deviation of y coordinate of measurement within
482      * current window.
483      */
484     public double getStandardDeviationY() {
485         return Math.sqrt(varianceY);
486     }
487 
488     /**
489      * Gets estimated standard deviation of y coordinate of measurement within current
490      * window.
491      *
492      * @return estimated standard deviation of y coordinate of measurement.
493      */
494     public M getStandardDeviationYAsMeasurement() {
495         return createMeasurement(getStandardDeviationY(), getDefaultUnit());
496     }
497 
498     /**
499      * Gets estimated standard deviation of y coordinate of measurement within current
500      * window.
501      *
502      * @param result instance where estimated standard deviation of y coordinate of
503      *               measurement will be stored.
504      */
505     public void getStandardDeviationYAsMeasurement(final M result) {
506         result.setValue(getStandardDeviationY());
507         result.setUnit(getDefaultUnit());
508     }
509 
510     /**
511      * Gets estimated standard deviation of z coordinate of measurement within current
512      * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
513      * angular speed or T for magnetic flux density).
514      *
515      * @return estimated standard deviation of z coordinate of measurement within
516      * current window.
517      */
518     public double getStandardDeviationZ() {
519         return Math.sqrt(varianceZ);
520     }
521 
522     /**
523      * Gets estimated standard deviation of z coordinate of measurement within current
524      * window.
525      *
526      * @return estimated standard deviation of z coordinate of measurement.
527      */
528     public M getStandardDeviationZAsMeasurement() {
529         return createMeasurement(getStandardDeviationZ(), getDefaultUnit());
530     }
531 
532     /**
533      * Gets estimated standard deviation of z coordinate of measurement within current
534      * window.
535      *
536      * @param result instance where estimated standard deviation of z coordinate of
537      *               measurement will be stored.
538      */
539     public void getStandardDeviationZAsMeasurement(final M result) {
540         result.setValue(getStandardDeviationZ());
541         result.setUnit(getDefaultUnit());
542     }
543 
544     /**
545      * Gets estimated standard deviation of measurements.
546      *
547      * @return estimated standard deviation triad of measurements.
548      */
549     public T getStandardDeviationTriad() {
550         return createTriad(getStandardDeviationX(), getStandardDeviationY(), getStandardDeviationZ(), getDefaultUnit());
551     }
552 
553     /**
554      * Gets estimated standard deviation of measurement within current window.
555      *
556      * @param result instance where estimated standard deviation triad of
557      *               measurement will be stored.
558      */
559     public void getStandardDeviationTriad(final T result) {
560         result.setValueCoordinatesAndUnit(getStandardDeviationX(), getStandardDeviationY(), getStandardDeviationZ(),
561                 getDefaultUnit());
562     }
563 
564     /**
565      * Gets norm of estimated standard deviation of measurement
566      * expressed in its default unit (m/s^2 for acceleration, rad/s for
567      * angular speed or T for magnetic flux density).
568      *
569      * @return norm of estimated standard deviation of measurement.
570      */
571     public double getStandardDeviationNorm() {
572         final var fx = getStandardDeviationX();
573         final var fy = getStandardDeviationY();
574         final var fz = getStandardDeviationZ();
575         return Math.sqrt(fx * fx + fy * fy + fz * fz);
576     }
577 
578     /**
579      * Gets norm of estimated standard deviation of measurements.
580      *
581      * @return norm of estimated standard deviation of measurement.
582      */
583     public M getStandardDeviationNormAsMeasurement() {
584         return createMeasurement(getStandardDeviationNorm(), getDefaultUnit());
585     }
586 
587     /**
588      * Gets norm of estimated standard deviation of measurement within current window.
589      *
590      * @param result instance where norm of estimated standard deviation will be stored.
591      */
592     public void getStandardDeviationNormAsMeasurement(final M result) {
593         result.setValue(getStandardDeviationNorm());
594         result.setUnit(getDefaultUnit());
595     }
596 
597     /**
598      * Gets average of estimated standard deviation coordinates of measurement
599      * expressed in its default unit (m/s^2 for acceleration, rad/s for
600      * angular speed or T for magnetic flux density).
601      *
602      * @return average of estimated standard deviation coordinates.
603      */
604     public double getAverageStandardDeviation() {
605         final var fx = getStandardDeviationX();
606         final var fy = getStandardDeviationY();
607         final var fz = getStandardDeviationZ();
608         return (fx + fy + fz) / 3.0;
609     }
610 
611     /**
612      * Gets average of estimated standard deviation coordinates of measurement within
613      * current window.
614      *
615      * @return average of estimated standard deviation coordinates.
616      */
617     public M getAverageStandardDeviationAsMeasurement() {
618         return createMeasurement(getAverageStandardDeviation(), getDefaultUnit());
619     }
620 
621     /**
622      * Gets average of estimated standard deviation coordinates of measurement.
623      *
624      * @param result instance where average of estimated standard deviation coordinates
625      *               will be stored.
626      */
627     public void getAverageStandardDeviationAsMeasurement(final M result) {
628         result.setValue(getAverageStandardDeviation());
629         result.setUnit(getDefaultUnit());
630     }
631 
632     /**
633      * Gets measurement noise PSD (Power Spectral Density) on x axis expressed
634      * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
635      * magnetometer.
636      *
637      * @return measurement noise PSD on x axis.
638      */
639     public double getPsdX() {
640         return varianceX * timeInterval;
641     }
642 
643     /**
644      * Gets measurement noise PSD (Power Spectral Density) on y axis expressed
645      * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
646      * magnetometer.
647      *
648      * @return measurement noise PSD on y axis.
649      */
650     public double getPsdY() {
651         return varianceY * timeInterval;
652     }
653 
654     /**
655      * Gets measurement noise PSD (Power Spectral Density) on z axis expressed
656      * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
657      * magnetometer.
658      *
659      * @return measurement noise PSD on z axis.
660      */
661     public double getPsdZ() {
662         return varianceZ * timeInterval;
663     }
664 
665     /**
666      * Gets measurement noise root PSD (Power Spectral Density) on x axis expressed in
667      * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
668      * magnetometer.
669      *
670      * @return measurement noise root PSD on x axis.
671      */
672     public double getRootPsdX() {
673         return Math.sqrt(getPsdX());
674     }
675 
676     /**
677      * Gets measurement noise root PSD (Power Spectral Density) on y axis expressed in
678      * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
679      * magnetometer.
680      *
681      * @return measurement noise root PSD on y axis.
682      */
683     public double getRootPsdY() {
684         return Math.sqrt(getPsdY());
685     }
686 
687     /**
688      * Gets measurement noise root PSD (Power Spectral Density) on z axis expressed in
689      * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
690      * magnetometer.
691      *
692      * @return measurement noise root PSD on z axis.
693      */
694     public double getRootPsdZ() {
695         return Math.sqrt(getPsdZ());
696     }
697 
698     /**
699      * Gets average measurement noise PSD (Power Spectral Density) among
700      * x,y,z components expressed as (m^2 * s^-3) for accelerometer,
701      * (rad^2/s) for gyroscope or (T^2 * s) for magnetometer.
702      *
703      * @return average measurement noise PSD.
704      */
705     public double getAvgNoisePsd() {
706         return (getPsdX() + getPsdY() + getPsdZ()) / 3.0;
707     }
708 
709     /**
710      * Gets norm of noise root PSD (Power Spectral Density) among x,y,z
711      * components expressed as (m * s^-1.5) for accelerometer,
712      * (rad * s^-0.5) for gyroscope or (T * s^0.5) for magnetometer.
713      *
714      * @return norm of measurement noise root PSD.
715      */
716     public double getNoiseRootPsdNorm() {
717         return Math.sqrt(getPsdX() + getPsdY() + getPsdZ());
718     }
719 
720     /**
721      * Gets number of samples that have been processed so far.
722      *
723      * @return number of samples that have been processed so far.
724      */
725     public int getNumberOfProcessedSamples() {
726         return numberOfProcessedSamples;
727     }
728 
729     /**
730      * Indicates whether estimator is currently running or not.
731      *
732      * @return true if estimator is running, false otherwise.
733      */
734     public boolean isRunning() {
735         return running;
736     }
737 
738     /**
739      * Adds a triad of measurement samples.
740      * Values are expressed in measurement default unit (m/s^2 for acceleration, rad/s for
741      * angular speed or T for magnetic flux density).
742      *
743      * @param valueX x coordinate of measurement to be added and processed.
744      * @param valueY y coordinate of measurement to be added and processed.
745      * @param valueZ z coordinate of measurement to be added and processed.
746      * @throws LockedException if estimator is currently running.
747      */
748     @SuppressWarnings("DuplicatedCode")
749     public void addTriad(final double valueX, final double valueY, final double valueZ) throws LockedException {
750 
751         if (running) {
752             throw new LockedException();
753         }
754 
755         running = true;
756 
757         if (lastTriad == null && listener != null) {
758             //noinspection unchecked
759             listener.onStart((E) this);
760         }
761 
762         // compute averages
763         final var tmp = (double) numberOfProcessedSamples / (double) numberOfProcessedSamplesPlusOne;
764         avgX = avgX * tmp + valueX / numberOfProcessedSamplesPlusOne;
765         avgY = avgY * tmp + valueY / numberOfProcessedSamplesPlusOne;
766         avgZ = avgZ * tmp + valueZ / numberOfProcessedSamplesPlusOne;
767 
768         // compute variances
769         final var diffX = valueX - avgX;
770         final var diffY = valueY - avgY;
771         final var diffZ = valueZ - avgZ;
772         final var diffX2 = diffX * diffX;
773         final var diffY2 = diffY * diffY;
774         final var diffZ2 = diffZ * diffZ;
775 
776         varianceX = varianceX * tmp + diffX2 / numberOfProcessedSamplesPlusOne;
777         varianceY = varianceY * tmp + diffY2 / numberOfProcessedSamplesPlusOne;
778         varianceZ = varianceZ * tmp + diffZ2 / numberOfProcessedSamplesPlusOne;
779 
780         if (lastTriad == null) {
781             lastTriad = createTriad(valueX, valueY, valueZ, getDefaultUnit());
782         } else {
783             lastTriad.setValueCoordinatesAndUnit(valueX, valueY, valueZ, getDefaultUnit());
784         }
785 
786         numberOfProcessedSamples++;
787         numberOfProcessedSamplesPlusOne++;
788 
789         if (listener != null) {
790             //noinspection unchecked
791             listener.onTriadAdded((E) this);
792         }
793 
794         running = false;
795     }
796 
797     /**
798      * Adds a triad of measurement samples.
799      *
800      * @param triad measurement triad to be added and processed.
801      * @throws LockedException if estimator is currently running.
802      */
803     public void addTriad(final T triad) throws LockedException {
804         addTriad(convertToDefaultUnit(triad.getValueX(), triad.getUnit()),
805                 convertToDefaultUnit(triad.getValueY(), triad.getUnit()),
806                 convertToDefaultUnit(triad.getValueZ(), triad.getUnit()));
807     }
808 
809     /**
810      * Adds a triad of measurement samples.
811      *
812      * @param valueX x coordinate of measurement to be added and processed.
813      * @param valueY y coordinate of measurement to be added and processed.
814      * @param valueZ z coordinate of measurement to be added and processed.
815      * @throws LockedException if estimator is currently running.
816      */
817     public void addTriad(final M valueX, final M valueY, final M valueZ) throws LockedException {
818         addTriad(convertToDefaultUnit(valueX.getValue().doubleValue(), valueX.getUnit()),
819                 convertToDefaultUnit(valueY.getValue().doubleValue(), valueY.getUnit()),
820                 convertToDefaultUnit(valueZ.getValue().doubleValue(), valueZ.getUnit()));
821     }
822 
823     /**
824      * Resets current estimator.
825      *
826      * @return true if estimator was successfully reset, false if no reset was needed.
827      * @throws LockedException if estimator is currently running.
828      */
829     public boolean reset() throws LockedException {
830         if (running) {
831             throw new LockedException();
832         }
833 
834         if (numberOfProcessedSamples == 0) {
835             return false;
836         }
837 
838         running = true;
839         lastTriad = null;
840         avgX = 0.0;
841         avgY = 0.0;
842         avgZ = 0.0;
843         varianceX = 0.0;
844         varianceY = 0.0;
845         varianceZ = 0.0;
846         numberOfProcessedSamples = 0;
847         numberOfProcessedSamplesPlusOne = 1;
848 
849         if (listener != null) {
850             //noinspection unchecked
851             listener.onReset((E) this);
852         }
853 
854         running = false;
855 
856         return true;
857     }
858 
859     /**
860      * Creates a triad with provided values and unit.
861      *
862      * @param valueX x coordinate value.
863      * @param valueY y coordinate value.
864      * @param valueZ z coordinate value.
865      * @param unit   unit.
866      * @return created triad.
867      */
868     protected abstract T createTriad(final double valueX, final double valueY, final double valueZ, final U unit);
869 
870     /**
871      * Gets default unit for a measurement.
872      *
873      * @return default unit for a measurement.
874      */
875     protected abstract U getDefaultUnit();
876 
877     /**
878      * Creates a measurement with provided value and unit.
879      *
880      * @param value value to be set.
881      * @param unit  unit to be set.
882      * @return created measurement.
883      */
884     protected abstract M createMeasurement(final double value, final U unit);
885 
886     /**
887      * Converts provided value and unit into default unit.
888      *
889      * @param value measurement value to be converted.
890      * @param unit  unit of measurement value to be converted.
891      * @return converted value.
892      */
893     protected abstract double convertToDefaultUnit(final double value, final U unit);
894 }