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 }