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 import java.util.LinkedList;
27
28 /**
29 * Base class to estimate measurement noise variances and PSD's (Power Spectral Densities)
30 * along with average values for a windowed amount of samples.
31 * Implementations of this estimator must be used when the body where the measurement device
32 * is attached to remains static on the same position with zero velocity while capturing data.
33 * To compute PSD's, this estimator assumes that measurement samples are obtained
34 * at a constant provided rate equal to {@link #getTimeInterval()} seconds.
35 * If not available, accelerometer sampling rate average can be estimated using
36 * {@link TimeIntervalEstimator}.
37 * This estimator does NOT require the knowledge of current location and body
38 * orientation.
39 * Because body location and orientation is not known, estimated average values
40 * cannot be used to determine biases. Only norm of noise estimations
41 * (variance or standard deviation) can be safely used.
42 * Notice that if there are less than {@link #getWindowSize()} processed
43 * samples in the window, this estimator will assume that the remaining ones
44 * until the window is completed have zero values.
45 *
46 * @param <U> a measurement unit type.
47 * @param <M> a measurement type.
48 * @param <T> a triad type.
49 * @param <E> an estimator type.
50 * @param <L> a listener type.
51 */
52 @SuppressWarnings("DuplicatedCode")
53 public abstract class WindowedTriadNoiseEstimator<U extends Enum<?>,
54 M extends Measurement<U>, T extends Triad<U, M, T>,
55 E extends WindowedTriadNoiseEstimator<U, M, T, E, L>,
56 L extends WindowedTriadNoiseEstimatorListener<U, M, T, E>> {
57
58 /**
59 * Number of samples to keep within the window by default.
60 * For an accelerometer generating 100 samples/second, this is equivalent to
61 * 1 second.
62 * For an accelerometer generating 50 samples/second, this is equivalent to
63 * 2 seconds.
64 */
65 public static final int DEFAULT_WINDOW_SIZE = 101;
66
67 /**
68 * Minimum allowed window size.
69 */
70 public static final int MIN_WINDOW_SIZE = 3;
71
72 /**
73 * Default time interval between accelerometer samples expressed in seconds
74 * (s).
75 */
76 public static final double DEFAULT_TIME_INTERVAL_SECONDS = 0.02;
77
78 /**
79 * Length of number of samples to keep within the window being processed.
80 * Window size must always be larger than allowed minimum value and must have
81 * an odd value.
82 */
83 private int windowSize = DEFAULT_WINDOW_SIZE;
84
85 /**
86 * Time interval expressed in seconds (s) between consecutive triad
87 * samples.
88 */
89 private double timeInterval = DEFAULT_TIME_INTERVAL_SECONDS;
90
91 /**
92 * Keeps the list of triad samples that remain within the window.
93 */
94 private final LinkedList<T> windowedSamples = new LinkedList<>();
95
96 /**
97 * Listener to handle events raised by this estimator.
98 */
99 private L listener;
100
101 /**
102 * Contains estimated average of x coordinate of measurement expressed in its default
103 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
104 */
105 private double avgX;
106
107 /**
108 * Contains estimated average of y coordinate of measurement expressed in its default
109 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
110 */
111 private double avgY;
112
113 /**
114 * Contains estimated average of z coordinate of measurement expressed in its default
115 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
116 */
117 private double avgZ;
118
119 /**
120 * Contains estimated variance of x coordinate of measurement expressed in its default
121 * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
122 * flux density).
123 */
124 private double varianceX;
125
126 /**
127 * Contains estimated variance of y coordinate of measurement expressed in its default
128 * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
129 * flux density).
130 */
131 private double varianceY;
132
133 /**
134 * Contains estimated variance of x coordinate of measurement expressed in its default
135 * squared unit (m^2/s^4 for acceleration, rad^2/s^2 for angular speed or T^2 for magnetic
136 * flux density).
137 */
138 private double varianceZ;
139
140 /**
141 * Number of processed acceleration triad samples.
142 */
143 private int numberOfProcessedSamples;
144
145 /**
146 * Indicates whether estimator is running or not.
147 */
148 private boolean running;
149
150 /**
151 * Constructor.
152 */
153 protected WindowedTriadNoiseEstimator() {
154 }
155
156 /**
157 * Constructor.
158 *
159 * @param listener listener to handle events raised by this estimator.
160 */
161 protected WindowedTriadNoiseEstimator(final L listener) {
162 this.listener = listener;
163 }
164
165 /**
166 * Gets length of number of samples to keep within the window being processed.
167 * Window size must always be larger than allowed minimum value and must
168 * have an odd value.
169 *
170 * @return length of number of samples to keep within the window.
171 */
172 public int getWindowSize() {
173 return windowSize;
174 }
175
176 /**
177 * Sets length of number of samples to keep within the window being processed.
178 * Window size must always be larger than allowed minimum value and must have
179 * an odd value.
180 * When window size is modified, instance state is reset.
181 *
182 * @param windowSize length of number of samples to keep within the window.
183 * @throws IllegalArgumentException if provided value is not valid.
184 * @throws LockedException if estimator is currently running.
185 */
186 public void setWindowSize(final int windowSize) throws LockedException {
187 if (running) {
188 throw new LockedException();
189 }
190
191 // check that window is larger than minimum allowed value
192 if (windowSize < MIN_WINDOW_SIZE) {
193 throw new IllegalArgumentException();
194 }
195
196 // check that window size is not even
197 if (windowSize % 2 == 0) {
198 throw new IllegalArgumentException();
199 }
200
201 this.windowSize = windowSize;
202 reset();
203 }
204
205 /**
206 * Gets time interval between triad samples expressed in
207 * seconds (s).
208 *
209 * @return time interval between triad samples.
210 */
211 public double getTimeInterval() {
212 return timeInterval;
213 }
214
215 /**
216 * Sets time interval between triad samples expressed in
217 * seconds (s).
218 *
219 * @param timeInterval time interval between triad samples.
220 * @throws IllegalArgumentException if provided value is negative.
221 * @throws LockedException if estimator is currently running.
222 */
223 public void setTimeInterval(final double timeInterval) throws LockedException {
224 if (running) {
225 throw new LockedException();
226 }
227
228 if (timeInterval < 0.0) {
229 throw new IllegalArgumentException();
230 }
231
232 this.timeInterval = timeInterval;
233 }
234
235 /**
236 * Gets time interval between triad samples.
237 *
238 * @return time interval between triad samples.
239 */
240 public Time getTimeIntervalAsTime() {
241 return new Time(timeInterval, TimeUnit.SECOND);
242 }
243
244 /**
245 * Gets time interval between triad samples.
246 *
247 * @param result instance where time interval will be stored.
248 */
249 public void getTimeIntervalAsTime(final Time result) {
250 result.setValue(timeInterval);
251 result.setUnit(TimeUnit.SECOND);
252 }
253
254 /**
255 * Sets time interval between triad samples.
256 *
257 * @param timeInterval time interval between triad samples.
258 * @throws LockedException if estimator is currently running.
259 */
260 public void setTimeInterval(final Time timeInterval) throws LockedException {
261 setTimeInterval(TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(),
262 TimeUnit.SECOND));
263 }
264
265 /**
266 * Gets listener to handle events raised by this estimator.
267 *
268 * @return listener to handle events raised by this estimator.
269 */
270 public L getListener() {
271 return listener;
272 }
273
274 /**
275 * Sets listener to handle events raised by this estimator.
276 *
277 * @param listener listener to handle events raised by this estimator.
278 * @throws LockedException if this estimator is running.
279 */
280 public void setListener(final L listener) throws LockedException {
281 if (running) {
282 throw new LockedException();
283 }
284
285 this.listener = listener;
286 }
287
288 /**
289 * Gets first provided measurement triad within the window.
290 *
291 * @return first provided measurement triad within the window or null if not
292 * available.
293 */
294 public T getFirstWindowedTriad() {
295 return windowedSamples.isEmpty() ? null : windowedSamples.getFirst();
296 }
297
298 /**
299 * Gets first provided measurement triad within the window.
300 *
301 * @param result instance where first provided measurement triad will be stored.
302 * @return true if result instance was updated, false otherwise.
303 */
304 public boolean getFirstWindowedTriad(final T result) {
305 if (windowedSamples.isEmpty()) {
306 return false;
307 } else {
308 result.copyFrom(windowedSamples.getFirst());
309 return true;
310 }
311 }
312
313 /**
314 * Gets last provided measurement triad within the window.
315 *
316 * @return last provided measurement triad within the window or null if not
317 * available.
318 */
319 public T getLastWindowedTriad() {
320 return windowedSamples.isEmpty() ? null : windowedSamples.getLast();
321 }
322
323 /**
324 * Gets last provided measurement triad within the window.
325 *
326 * @param result instance where last provided measurement triad will be stored.
327 * @return true if result instance was updated, false otherwise.
328 */
329 public boolean getLastWindowedTriad(final T result) {
330 if (windowedSamples.isEmpty()) {
331 return false;
332 } else {
333 result.copyFrom(windowedSamples.getLast());
334 return true;
335 }
336 }
337
338 /**
339 * Gets estimated average of x coordinate of measurement expressed in its default
340 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
341 * This value will depend of body location and orientation, hence it should never
342 * be used as a calibration bias.
343 *
344 * @return average of x coordinate of measurement in current window.
345 */
346 public double getAvgX() {
347 return avgX;
348 }
349
350 /**
351 * Gets estimated average of x coordinate of measurement within current window.
352 * This value will depend of body location and orientation, hence it should never
353 * be used as a calibration bias.
354 *
355 * @return average of x coordinate of measurement in current window.
356 */
357 public M getAvgXAsMeasurement() {
358 return createMeasurement(avgX, getDefaultUnit());
359 }
360
361 /**
362 * Gets estimated average of x coordinate of measurement within current window.
363 * This value will depend of body location and orientation, hence it should never
364 * be used as a calibration bias.
365 *
366 * @param result instance where average of x coordinate of measurement will be stored.
367 */
368 public void getAvgXAsMeasurement(final M result) {
369 result.setValue(avgX);
370 result.setUnit(getDefaultUnit());
371 }
372
373 /**
374 * Gets estimated average of y coordinate of measurement expressed in its default
375 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
376 * This value will depend of body location and orientation, hence it should never
377 * be used as a calibration bias.
378 *
379 * @return average of y coordinate of measurement in current window.
380 */
381 public double getAvgY() {
382 return avgY;
383 }
384
385 /**
386 * Gets estimated average of y coordinate of measurement within current window.
387 * This value will depend of body location and orientation, hence it should never
388 * be used as a calibration bias.
389 *
390 * @return average of y coordinate of measurement in current window.
391 */
392 public M getAvgYAsMeasurement() {
393 return createMeasurement(avgY, getDefaultUnit());
394 }
395
396 /**
397 * Gets estimated average of y coordinate of measurement within current window.
398 * This value will depend of body location and orientation, hence it should never
399 * be used as a calibration bias.
400 *
401 * @param result instance where average of y coordinate of measurement will be stored.
402 */
403 public void getAvgYAsMeasurement(final M result) {
404 result.setValue(avgY);
405 result.setUnit(getDefaultUnit());
406 }
407
408 /**
409 * Gets estimated average of z coordinate of measurement expressed in its default
410 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
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 z coordinate of measurement in current window.
415 */
416 public double getAvgZ() {
417 return avgZ;
418 }
419
420 /**
421 * Gets estimated average of z coordinate of measurement within current window.
422 * This value will depend of body location and orientation, hence it should never
423 * be used as a calibration bias.
424 *
425 * @return average of z coordinate of measurement in current window.
426 */
427 public M getAvgZAsMeasurement() {
428 return createMeasurement(avgZ, getDefaultUnit());
429 }
430
431 /**
432 * Gets estimated average of z coordinate of measurement within current window.
433 * This value will depend of body location and orientation, hence it should never
434 * be used as a calibration bias.
435 *
436 * @param result instance where average of z coordinate of measurement will be stored.
437 */
438 public void getAvgZAsMeasurement(final M result) {
439 result.setValue(avgZ);
440 result.setUnit(getDefaultUnit());
441 }
442
443 /**
444 * Gets estimated average as a measurement triad.
445 *
446 * @return average measurement triad.
447 */
448 public T getAvgTriad() {
449 return createTriad(avgX, avgY, avgZ, getDefaultUnit());
450 }
451
452 /**
453 * Gets estimated average as a measurement triad.
454 *
455 * @param result instance where average values and unit will be stored.
456 */
457 public void getAvgTriad(final T result) {
458 result.setValueCoordinatesAndUnit(avgX, avgY, avgZ, getDefaultUnit());
459 }
460
461 /**
462 * Gets norm of estimated average measurement within current window expressed in its default
463 * unit (m/s^2 for acceleration, rad/s for angular speed or T for magnetic flux density).
464 * This value is independent of body orientation.
465 *
466 * @return norm of estimated average specific force.
467 */
468 public double getAvgNorm() {
469 return Math.sqrt(avgX * avgX + avgY * avgY + avgZ * avgZ);
470 }
471
472 /**
473 * Gets norm of estimated average measurement within current window.
474 *
475 * @return norm of estimated average measurement.
476 */
477 public M getAvgNormAsMeasurement() {
478 return createMeasurement(getAvgNorm(), getDefaultUnit());
479 }
480
481 /**
482 * Gets norm of estimated average measurement within current window.
483 *
484 * @param result instance where norm of estimated average measurement will be stored.
485 */
486 public void getAvgNormAsMeasurement(final M result) {
487 result.setValue(getAvgNorm());
488 result.setUnit(getDefaultUnit());
489 }
490
491 /**
492 * Gets estimated variance of x coordinate of measurement within current window
493 * expressed in its default squared unit (m^2/s^4 for acceleration,
494 * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
495 *
496 * @return estimated variance of x coordinate of measurement within current
497 * window.
498 */
499 public double getVarianceX() {
500 return varianceX;
501 }
502
503 /**
504 * Gets estimated variance of y coordinate of measurement within current window
505 * expressed in its default squared unit (m^2/s^4 for acceleration,
506 * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
507 *
508 * @return estimated variance of y coordinate of measurement within current
509 * window.
510 */
511 public double getVarianceY() {
512 return varianceY;
513 }
514
515 /**
516 * Gets estimated variance of z coordinate of measurement within current window
517 * expressed in its default squared unit (m^2/s^4 for acceleration,
518 * rad^2/s^2 for angular speed or T^2 for magnetic flux density).
519 *
520 * @return estimated variance of z coordinate of measurement within current
521 * window.
522 */
523 public double getVarianceZ() {
524 return varianceZ;
525 }
526
527 /**
528 * Gets estimated standard deviation of x coordinate of measurement within current
529 * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
530 * angular speed or T for magnetic flux density).
531 *
532 * @return estimated standard deviation of x coordinate of measurement within
533 * current window.
534 */
535 public double getStandardDeviationX() {
536 return Math.sqrt(varianceX);
537 }
538
539 /**
540 * Gets estimated standard deviation of x coordinate of measurement within current
541 * window.
542 *
543 * @return estimated standard deviation of x coordinate of measurement.
544 */
545 public M getStandardDeviationXAsMeasurement() {
546 return createMeasurement(getStandardDeviationX(), getDefaultUnit());
547 }
548
549 /**
550 * Gets estimated standard deviation of x coordinate of measurement within current
551 * window.
552 *
553 * @param result instance where estimated standard deviation of x coordinate of
554 * measurement will be stored.
555 */
556 public void getStandardDeviationXAsMeasurement(final M result) {
557 result.setValue(getStandardDeviationX());
558 result.setUnit(getDefaultUnit());
559 }
560
561 /**
562 * Gets estimated standard deviation of y coordinate of measurement within current
563 * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
564 * angular speed or T for magnetic flux density).
565 *
566 * @return estimated standard deviation of y coordinate of measurement within
567 * current window.
568 */
569 public double getStandardDeviationY() {
570 return Math.sqrt(varianceY);
571 }
572
573 /**
574 * Gets estimated standard deviation of y coordinate of measurement within current
575 * window.
576 *
577 * @return estimated standard deviation of y coordinate of measurement.
578 */
579 public M getStandardDeviationYAsMeasurement() {
580 return createMeasurement(getStandardDeviationY(), getDefaultUnit());
581 }
582
583 /**
584 * Gets estimated standard deviation of y coordinate of measurement within current
585 * window.
586 *
587 * @param result instance where estimated standard deviation of y coordinate of
588 * measurement will be stored.
589 */
590 public void getStandardDeviationYAsMeasurement(final M result) {
591 result.setValue(getStandardDeviationY());
592 result.setUnit(getDefaultUnit());
593 }
594
595 /**
596 * Gets estimated standard deviation of z coordinate of measurement within current
597 * window and expressed in its default unit (m/s^2 for acceleration, rad/s for
598 * angular speed or T for magnetic flux density).
599 *
600 * @return estimated standard deviation of z coordinate of measurement within
601 * current window.
602 */
603 public double getStandardDeviationZ() {
604 return Math.sqrt(varianceZ);
605 }
606
607 /**
608 * Gets estimated standard deviation of z coordinate of measurement within current
609 * window.
610 *
611 * @return estimated standard deviation of z coordinate of measurement.
612 */
613 public M getStandardDeviationZAsMeasurement() {
614 return createMeasurement(getStandardDeviationZ(), getDefaultUnit());
615 }
616
617 /**
618 * Gets estimated standard deviation of z coordinate of measurement within current
619 * window.
620 *
621 * @param result instance where estimated standard deviation of z coordinate of
622 * measurement will be stored.
623 */
624 public void getStandardDeviationZAsMeasurement(final M result) {
625 result.setValue(getStandardDeviationZ());
626 result.setUnit(getDefaultUnit());
627 }
628
629 /**
630 * Gets estimated standard deviation of measurement within current window.
631 *
632 * @return estimated standard deviation triad of measurement.
633 */
634 public T getStandardDeviationTriad() {
635 return createTriad(getStandardDeviationX(), getStandardDeviationY(), getStandardDeviationZ(), getDefaultUnit());
636 }
637
638 /**
639 * Gets estimated standard deviation of measurement within current window.
640 *
641 * @param result instance where estimated standard deviation triad of
642 * measurement will be stored.
643 */
644 public void getStandardDeviationTriad(final T result) {
645 result.setValueCoordinatesAndUnit(getStandardDeviationX(), getStandardDeviationY(), getStandardDeviationZ(),
646 getDefaultUnit());
647 }
648
649 /**
650 * Gets norm of estimated standard deviation of measurement within current
651 * window expressed in its default unit (m/s^2 for acceleration, rad/s for
652 * angular speed or T for magnetic flux density).
653 *
654 * @return norm of estimated standard deviation of measurement.
655 */
656 public double getStandardDeviationNorm() {
657 final var fx = getStandardDeviationX();
658 final var fy = getStandardDeviationY();
659 final var fz = getStandardDeviationZ();
660 return Math.sqrt(fx * fx + fy * fy + fz * fz);
661 }
662
663 /**
664 * Gets norm of estimated standard deviation of measurement within current window.
665 *
666 * @return norm of estimated standard deviation of measurement.
667 */
668 public M getStandardDeviationNormAsMeasurement() {
669 return createMeasurement(getStandardDeviationNorm(), getDefaultUnit());
670 }
671
672 /**
673 * Gets norm of estimated standard deviation of measurement within current window.
674 *
675 * @param result instance where norm of estimated standard deviation will be stored.
676 */
677 public void getStandardDeviationNormAsMeasurement(final M result) {
678 result.setValue(getStandardDeviationNorm());
679 result.setUnit(getDefaultUnit());
680 }
681
682 /**
683 * Gets average of estimated standard deviation coordinates of measurement within
684 * current window expressed in its default unit (m/s^2 for acceleration, rad/s for
685 * angular speed or T for magnetic flux density).
686 *
687 * @return average of estimated standard deviation coordinates.
688 */
689 public double getAverageStandardDeviation() {
690 final var fx = getStandardDeviationX();
691 final var fy = getStandardDeviationY();
692 final var fz = getStandardDeviationZ();
693 return (fx + fy + fz) / 3.0;
694 }
695
696 /**
697 * Gets average of estimated standard deviation coordinates of measurement within
698 * current window.
699 *
700 * @return average of estimated standard deviation coordinates.
701 */
702 public M getAverageStandardDeviationAsMeasurement() {
703 return createMeasurement(getAverageStandardDeviation(), getDefaultUnit());
704 }
705
706 /**
707 * Gets average of estimated standard deviation coordinates of measurement within
708 * current window.
709 *
710 * @param result instance where average of estimated standard deviation coordinates
711 * will be stored.
712 */
713 public void getAverageStandardDeviationAsMeasurement(final M result) {
714 result.setValue(getAverageStandardDeviation());
715 result.setUnit(getDefaultUnit());
716 }
717
718 /**
719 * Gets measurement noise PSD (Power Spectral Density) on x axis expressed
720 * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
721 * magnetometer.
722 *
723 * @return measurement noise PSD on x axis.
724 */
725 public double getPsdX() {
726 return varianceX * timeInterval;
727 }
728
729 /**
730 * Gets measurement noise PSD (Power Spectral Density) on y axis expressed
731 * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
732 * magnetometer.
733 *
734 * @return measurement noise PSD on y axis.
735 */
736 public double getPsdY() {
737 return varianceY * timeInterval;
738 }
739
740 /**
741 * Gets measurement noise PSD (Power Spectral Density) on z axis expressed
742 * in (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
743 * magnetometer.
744 *
745 * @return measurement noise PSD on z axis.
746 */
747 public double getPsdZ() {
748 return varianceZ * timeInterval;
749 }
750
751 /**
752 * Gets measurement noise root PSD (Power Spectral Density) on x axis expressed in
753 * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
754 * magnetometer.
755 *
756 * @return measurement noise root PSD on x axis.
757 */
758 public double getRootPsdX() {
759 return Math.sqrt(getPsdX());
760 }
761
762 /**
763 * Gets measurement noise root PSD (Power Spectral Density) on y axis expressed in
764 * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
765 * magnetometer.
766 *
767 * @return measurement noise root PSD on y axis.
768 */
769 public double getRootPsdY() {
770 return Math.sqrt(getPsdY());
771 }
772
773 /**
774 * Gets measurement noise root PSD (Power Spectral Density) on z axis expressed in
775 * (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5) for
776 * magnetometer.
777 *
778 * @return measurement noise root PSD on z axis.
779 */
780 public double getRootPsdZ() {
781 return Math.sqrt(getPsdZ());
782 }
783
784 /**
785 * Gets average measurement noise PSD (Power Spectral Density) among
786 * x,y,z components expressed as (m^2 * s^-3) for accelerometer,
787 * (rad^2/s) for gyroscope or (T^2 * s) for magnetometer.
788 *
789 * @return average measurement noise PSD.
790 */
791 public double getAvgNoisePsd() {
792 return (getPsdX() + getPsdY() + getPsdZ()) / 3.0;
793 }
794
795 /**
796 * Gets norm of noise root PSD (Power Spectral Density) among x,y,z
797 * components expressed as (m * s^-1.5) for accelerometer,
798 * (rad * s^-0.5) for gyroscope or (T * s^0.5) for magnetometer.
799 *
800 * @return norm of measurement noise root PSD.
801 */
802 public double getNoiseRootPsdNorm() {
803 return Math.sqrt(getPsdX() + getPsdY() + getPsdZ());
804 }
805
806 /**
807 * Gets number of samples that have been processed so far.
808 *
809 * @return number of samples that have been processed so far.
810 */
811 public int getNumberOfProcessedSamples() {
812 return numberOfProcessedSamples;
813 }
814
815 /**
816 * Gets number of currently windowed samples.
817 *
818 * @return number of samples within the window.
819 */
820 public int getNumberOfSamplesInWindow() {
821 return windowedSamples.size();
822 }
823
824 /**
825 * Indicates whether estimator is currently running or not.
826 *
827 * @return true if estimator is running, false otherwise.
828 */
829 public boolean isRunning() {
830 return running;
831 }
832
833 /**
834 * Indicates whether window of samples is filled or not.
835 *
836 * @return true if window is filled, false otherwise.
837 */
838 public boolean isWindowFilled() {
839 return getNumberOfSamplesInWindow() == windowSize;
840 }
841
842 /**
843 * Adds a triad of measurement samples and processes current window.
844 * Notice that if there are less than {@link #getWindowSize()} processed
845 * samples in the window, the remaining ones are considered to be zero
846 * when average values and standard deviation is estimated.
847 *
848 * @param triad measurement triad to be added and processed.
849 * @throws LockedException if estimator is currently running.
850 */
851 public void addTriadAndProcess(final T triad) throws LockedException {
852 internalAdd(triad, true);
853 }
854
855 /**
856 * Adds a triad of measurement samples and processes current window.
857 * Values are expressed in measurement default unit (m/s^2 for acceleration, rad/s for
858 * angular speed or T for magnetic flux density).
859 * Notice that if there are less than {@link #getWindowSize()} processed
860 * samples in the window, the remaining ones are considered to be zero
861 * when average values and standard deviation is estimated.
862 *
863 * @param valueX x coordinate of measurement to be added and processed.
864 * @param valueY y coordinate of measurement to be added and processed.
865 * @param valueZ z coordinate of measurement to be added and processed.
866 * @throws LockedException if estimator is currently running.
867 */
868 public void addTriadAndProcess(final double valueX, final double valueY, final double valueZ)
869 throws LockedException {
870 addTriadAndProcess(createTriad(valueX, valueY, valueZ, getDefaultUnit()));
871 }
872
873 /**
874 * Adds a triad of measurement samples and processes current window.
875 * Notice that if there are less than {@link #getWindowSize()} processed
876 * samples in the window, the remaining ones are considered to be zero
877 * when average values and standard deviation is estimated.
878 *
879 * @param valueX x coordinate of measurement to be added and processed.
880 * @param valueY y coordinate of measurement to be added and processed.
881 * @param valueZ z coordinate of measurement to be added and processed.
882 * @throws LockedException if estimator is currently running.
883 */
884 public void addTriadAndProcess(final M valueX, final M valueY, final M valueZ) throws LockedException {
885 addTriadAndProcess(createTriad(valueX, valueY, valueZ));
886 }
887
888 /**
889 * Adds a triad of measurement samples without processing current window or updating
890 * result values.
891 * Notice that if there are less than {@link #getWindowSize()} processed
892 * samples in the window, the remaining ones are considered to be zero
893 * when average values and standard deviation is estimated.
894 *
895 * @param triad measurement triad to be added.
896 * @throws LockedException if estimator is currently running.
897 */
898 public void addTriad(final T triad) throws LockedException {
899 internalAdd(triad, false);
900 }
901
902 /**
903 * Adds a triad of measurement samples without processing current window or updating
904 * result values.
905 * Values are expressed in measurement default unit (m/s^2 for acceleration, rad/s for
906 * angular speed or T for magnetic flux density).
907 * Notice that if there are less than {@link #getWindowSize()} processed
908 * samples in the window, the remaining ones are considered to be zero
909 * when average values and standard deviation is estimated.
910 *
911 * @param valueX x coordinate of measurement to be added.
912 * @param valueY y coordinate of measurement to be added.
913 * @param valueZ z coordinate of measurement to be added.
914 * @throws LockedException if estimator is currently running.
915 */
916 public void addTriad(final double valueX, final double valueY, final double valueZ) throws LockedException {
917 addTriad(createTriad(valueX, valueY, valueZ, getDefaultUnit()));
918 }
919
920 /**
921 * Adds a triad of measurement samples without processing current window or updating
922 * result values.
923 * Notice that if there are less than {@link #getWindowSize()} processed
924 * samples in the window, the remaining ones are considered to be zero
925 * when average values and standard deviation is estimated.
926 *
927 * @param valueX x coordinate of measurement to be added.
928 * @param valueY y coordinate of measurement to be added.
929 * @param valueZ z coordinate of measurement to be added.
930 * @throws LockedException if estimator is currently running.
931 */
932 public void addTriad(final M valueX, final M valueY, final M valueZ) throws LockedException {
933 addTriad(createTriad(valueX, valueY, valueZ));
934 }
935
936 /**
937 * Resets current estimator.
938 *
939 * @return true if estimator was successfully reset, false if no reset was needed.
940 * @throws LockedException if estimator is currently running.
941 */
942 public boolean reset() throws LockedException {
943 if (running) {
944 throw new LockedException();
945 }
946
947 if (numberOfProcessedSamples == 0) {
948 return false;
949 }
950
951 windowedSamples.clear();
952 avgX = 0.0;
953 avgY = 0.0;
954 avgZ = 0.0;
955 varianceX = 0.0;
956 varianceY = 0.0;
957 varianceZ = 0.0;
958 numberOfProcessedSamples = 0;
959
960 if (listener != null) {
961 //noinspection unchecked
962 listener.onReset((E) this);
963 }
964
965 return true;
966 }
967
968 /**
969 * Creates a copy of a triad.
970 *
971 * @param input triad to be copied.
972 * @return copy of a triad.
973 */
974 protected abstract T copyTriad(final T input);
975
976 /**
977 * Creates a triad with provided values and unit.
978 *
979 * @param valueX x coordinate value.
980 * @param valueY y coordinate value.
981 * @param valueZ z coordinate value.
982 * @param unit unit.
983 * @return created triad.
984 */
985 protected abstract T createTriad(final double valueX, final double valueY, final double valueZ, final U unit);
986
987 /**
988 * Creates a triad with provided values.
989 *
990 * @param valueX x coordinate value.
991 * @param valueY y coordinate value.
992 * @param valueZ z coordinate value.
993 * @return created triad.
994 */
995 protected abstract T createTriad(final M valueX, final M valueY, final M valueZ);
996
997 /**
998 * Gets default unit for a measurement.
999 *
1000 * @return default unit for a measurement.
1001 */
1002 protected abstract U getDefaultUnit();
1003
1004 /**
1005 * Creates a measurement with provided value and unit.
1006 *
1007 * @param value value to be set.
1008 * @param unit unit to be set.
1009 * @return created measurement.
1010 */
1011 protected abstract M createMeasurement(final double value, final U unit);
1012
1013 /**
1014 * Internally adds a triad of measurement samples and processes current window if indicated.
1015 *
1016 * @param triad measurement triad to be added.
1017 * @param process true if window of samples must also be processed, false otherwise.
1018 * @throws LockedException if estimator is currently running.
1019 */
1020 private void internalAdd(final T triad, final boolean process) throws LockedException {
1021 if (running) {
1022 throw new LockedException();
1023 }
1024
1025 running = true;
1026
1027 if (windowedSamples.isEmpty() && listener != null) {
1028 //noinspection unchecked
1029 listener.onStart((E) this);
1030 }
1031
1032 final var wasFilled = isWindowFilled();
1033 if (wasFilled) {
1034 // remove first sample
1035 windowedSamples.removeFirst();
1036 }
1037
1038 windowedSamples.addLast(copyTriad(triad));
1039
1040 // process window
1041 if (process) {
1042 processWindow();
1043 }
1044
1045 running = false;
1046
1047 if (listener != null) {
1048 //noinspection unchecked
1049 listener.onTriadAdded((E) this);
1050
1051 if (!wasFilled && isWindowFilled()) {
1052 //noinspection unchecked
1053 listener.onWindowFilled((E) this);
1054 }
1055 }
1056 }
1057
1058 /**
1059 * Processes current windowed samples.
1060 */
1061 private void processWindow() {
1062
1063 numberOfProcessedSamples++;
1064
1065 // compute averages
1066 var averageX = 0.0;
1067 var averageY = 0.0;
1068 var averageZ = 0.0;
1069 for (final var triad : windowedSamples) {
1070 final var valueX = triad.getValueX();
1071 final var valueY = triad.getValueY();
1072 final var valueZ = triad.getValueZ();
1073
1074 averageX += valueX;
1075 averageY += valueY;
1076 averageZ += valueZ;
1077 }
1078
1079 averageX /= windowSize;
1080 averageY /= windowSize;
1081 averageZ /= windowSize;
1082
1083 // compute variances
1084 var varX = 0.0;
1085 var varY = 0.0;
1086 var varZ = 0.0;
1087 for (final var triad : windowedSamples) {
1088 final var fx = triad.getValueX();
1089 final var fy = triad.getValueY();
1090 final var fz = triad.getValueZ();
1091
1092 final var diffX = fx - averageX;
1093 final var diffY = fy - averageY;
1094 final var diffZ = fz - averageZ;
1095
1096 final var diffX2 = diffX * diffX;
1097 final var diffY2 = diffY * diffY;
1098 final var diffZ2 = diffZ * diffZ;
1099
1100 varX += diffX2;
1101 varY += diffY2;
1102 varZ += diffZ2;
1103 }
1104
1105 final var m = windowSize - 1;
1106
1107 varX /= m;
1108 varY /= m;
1109 varZ /= m;
1110
1111 this.avgX = averageX;
1112 this.avgY = averageY;
1113 this.avgZ = averageZ;
1114
1115 varianceX = varX;
1116 varianceY = varY;
1117 varianceZ = varZ;
1118 }
1119 }