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.intervals;
17  
18  import com.irurueta.navigation.LockedException;
19  import com.irurueta.navigation.inertial.calibration.Triad;
20  import com.irurueta.navigation.inertial.calibration.noise.AccumulatedTriadNoiseEstimator;
21  import com.irurueta.navigation.inertial.calibration.noise.WindowedTriadNoiseEstimator;
22  import com.irurueta.units.Measurement;
23  import com.irurueta.units.Time;
24  import com.irurueta.units.TimeConverter;
25  import com.irurueta.units.TimeUnit;
26  
27  /**
28   * Abstract base class for detectors in charge of determining when a static period of
29   * measurements starts and finishes.
30   * Static periods are periods of time where the device is considered to
31   * remain static (no movement applied to it).
32   *
33   * @param <U> type of unit.
34   * @param <M> a type of measurement.
35   * @param <T> a triad type.
36   * @param <D> a detector type.
37   * @param <L> a listener type.
38   */
39  public abstract class TriadStaticIntervalDetector<U extends Enum<?>, M extends Measurement<U>,
40          T extends Triad<U, M, T>, D extends TriadStaticIntervalDetector<U, M, T, D, L>,
41          L extends TriadStaticIntervalDetectorListener<U, M, T, D>> {
42  
43      /**
44       * Number of samples to keep within the window by default.
45       * For a sensor generating 100 samples/second, this is equivalent to 1 second.
46       * For a sensor generating 50 samples/second, this is equivalent to 2 seconds.
47       */
48      public static final int DEFAULT_WINDOW_SIZE = WindowedTriadNoiseEstimator.DEFAULT_WINDOW_SIZE;
49  
50      /**
51       * Number of samples to process during the initial static period to determine the sensor
52       * (accelerometer, gyroscope or magnetometer) noise level.
53       * For a sensor generating 100 samples/second, this is equivalent to 50 seconds.
54       * For a sensor generating 50 samples/second, this is equivalent to 100 seconds.
55       */
56      public static final int DEFAULT_INITIAL_STATIC_SAMPLES = 5000;
57  
58      /**
59       * Minimum allowed number of samples to be processed during the initial static period.
60       */
61      public static final int MINIMUM_INITIAL_STATIC_SAMPLES = 2;
62  
63      /**
64       * Default factor to be applied to detected base noise level in order to determine
65       * threshold for static/dynamic period changes. This factor is unit-less.
66       */
67      public static final double DEFAULT_THRESHOLD_FACTOR = 2.0;
68  
69      /**
70       * Default factor to determine that a sudden movement has occurred during initialization
71       * if instantaneous noise level exceeds accumulated noise level by this factor amount.
72       * This factor is unit-less.
73       */
74      public static final double DEFAULT_INSTANTANEOUS_NOISE_LEVEL_FACTOR = 2.0;
75  
76      /**
77       * Default overall absolute threshold to determine whether there has been excessive motion
78       * during the whole initialization phase.
79       * This threshold is expressed in meters per squared second (m/s^2) for acceleration, radians
80       * per second (rad/s) for angular speed or Teslas (T) for magnetic flux density, and by
81       * default it is set to the maximum allowed value, thus effectively disabling this error
82       * condition check during initialization.
83       */
84      public static final double DEFAULT_BASE_NOISE_LEVEL_ABSOLUTE_THRESHOLD = Double.MAX_VALUE;
85  
86      /**
87       * Number of samples to keep in window to find instantaneous noise level averaged within
88       * the window of samples.
89       * Window size should contain about 1 or 2 seconds of data to be averaged to obtain
90       * a more reliable instantaneous noise level.
91       */
92      private int windowSize = DEFAULT_WINDOW_SIZE;
93  
94      /**
95       * Number of samples to be processed initially while keeping the sensor static in order
96       * to find the base noise level when device is static.
97       */
98      private int initialStaticSamples = DEFAULT_INITIAL_STATIC_SAMPLES;
99  
100     /**
101      * Factor to be applied to detected base noise level in order to determine
102      * threshold for static/dynamic period changes. This factor is unit-less.
103      */
104     private double thresholdFactor = DEFAULT_THRESHOLD_FACTOR;
105 
106     /**
107      * Factor to determine that a sudden movement has occurred during initialization if
108      * instantaneous noise level exceeds accumulated noise level by this factor amount.
109      * This factor is unit-less.
110      */
111     private double instantaneousNoiseLevelFactor = DEFAULT_INSTANTANEOUS_NOISE_LEVEL_FACTOR;
112 
113     /**
114      * Overall absolute threshold to determine whether there has been excessive motion
115      * during the whole initialization phase.
116      * Failure will be detected if estimated base noise level exceeds this threshold when
117      * initialization completes.
118      * This threshold is expressed in meters per squared second (m/s^2) for acceleration,
119      * radians per second (rad/s) for angular speed or Teslas (T) for magnetic flux density.
120      */
121     private double baseNoiseLevelAbsoluteThreshold = DEFAULT_BASE_NOISE_LEVEL_ABSOLUTE_THRESHOLD;
122 
123     /**
124      * Listener to handle events generated by this detector.
125      */
126     private L listener;
127 
128     /**
129      * Current status of this detector.
130      */
131     private Status status = Status.IDLE;
132 
133     /**
134      * Measurement base noise level that has been detected during initialization expressed in
135      * meters per squared second (m/s^2) for acceleration, radians per second (rad/s) for
136      * angular speed or Teslas (T) for magnetic flux density.
137      */
138     private double baseNoiseLevel;
139 
140     /**
141      * Threshold to determine static/dynamic period changes expressed in meters per squared
142      * second (m/s^2) for acceleration, radians per second (rad/s) for angular speed or
143      * Teslas (T) for magnetic flux density.
144      */
145     private double threshold;
146 
147     /**
148      * Indicates whether this detector is busy processing last provided sample.
149      */
150     private boolean running;
151 
152     /**
153      * Number of samples that have been processed so far.
154      */
155     private int processedSamples;
156 
157     /**
158      * Average x-coordinate of measurements accumulated during last static
159      * period expressed in meters per squared second (m/s^2) for acceleration,
160      * radians per second (rad/s) for angular speed or Teslas (T) for
161      * magnetic flux density.
162      * This value is updated when switching from a static period to a dynamic
163      * one or after completing initialization.
164      */
165     private double accumulatedAvgX;
166 
167     /**
168      * Average y-coordinate of measurements accumulated during last static
169      * period expressed in meters per squared second (m/s^2) for acceleration,
170      * radians per second (rad/s) for angular speed or Teslas (T) for
171      * magnetic flux density.
172      * This value is updated when switching from a static period to a dynamic
173      * one or after completing initialization.
174      */
175     private double accumulatedAvgY;
176 
177     /**
178      * Average z-coordinate of measurements accumulated during last static
179      * period expressed in meters per squared second (m/s^2) for acceleration,
180      * radians per second (rad/s) for angular speed or Teslas (T) for
181      * magnetic flux density.
182      * This value is updated when switching from a static period to a dynamic
183      * one or after completing initialization.
184      */
185     private double accumulatedAvgZ;
186 
187     /**
188      * Standard deviation of x-coordinate of measurements accumulated during
189      * last static period expressed in meters per squared second (m/s^2) for
190      * acceleration, radians per second (rad/s) for angular speed or Teslas
191      * (T) for magnetic flux density.
192      * This value is updated when switching from a static period to a dynamic
193      * one or after completing initialization.
194      */
195     private double accumulatedStdX;
196 
197     /**
198      * Standard deviation of y-coordinate of measurements accumulated during
199      * last static period expressed in meters per squared second (m/s^2) for
200      * acceleration, radians per second (rad/s) for angular speed or Teslas
201      * (T) for magnetic flux density.
202      * This value is updated when switching from a static period to a dynamic
203      * one or after completing initialization.
204      */
205     private double accumulatedStdY;
206 
207     /**
208      * Standard deviation of z-coordinate of measurements accumulated during
209      * last static period expressed in meters per squared second (m/s^2) for
210      * acceleration, radians per second (rad/s) for angular speed or Teslas
211      * (T) for magnetic flux density.
212      * This value is updated when switching from a static period to a dynamic
213      * one or after completing initialization.
214      */
215     private double accumulatedStdZ;
216 
217     /**
218      * Windowed average x-coordinate of measurements for each processed triad
219      * expressed in meters per squared second (m/s^2) for acceleration,
220      * radians per second (rad/s) for angular speed or Teslas (T) for
221      * magnetic flux density.
222      * This value is updated for each processed sample containing an average
223      * value for the samples within the window.
224      */
225     private double instantaneousAvgX;
226 
227     /**
228      * Windowed average y-coordinate of measurements for each processed triad
229      * expressed in meters per squared second (m/s^2) for acceleration,
230      * radians per second (rad/s) for angular speed or Teslas (T) for
231      * magnetic flux density.
232      * This value is updated for each processed sample containing an average
233      * value for the samples within the window.
234      */
235     private double instantaneousAvgY;
236 
237     /**
238      * Windowed average z-coordinate of measurements for each processed triad
239      * expressed in meters per squared second (m/s^2) for acceleration,
240      * radians per second (rad/s) for angular speed or Teslas (T) for
241      * magnetic flux density.
242      * This value is updated for each processed sample containing an average
243      * value for the samples within the window.
244      */
245     private double instantaneousAvgZ;
246 
247     /**
248      * Windowed standard deviation of x-coordinate of measurements for each
249      * processed triad expressed in meters per squared second (m/s^2) for
250      * acceleration, radians per second (rad/s) for angular speed or Teslas (T)
251      * for magnetic flux density.
252      * This value is updated for each processed sample containing measured standard
253      * deviation for the samples within the window.
254      */
255     private double instantaneousStdX;
256 
257     /**
258      * Windowed standard deviation of y-coordinate of measurements for each
259      * processed triad expressed in meters per squared second (m/s^2) for
260      * acceleration, radians per second (rad/s) for angular speed or Teslas (T)
261      * for magnetic flux density.
262      * This value is updated for each processed sample containing measured standard
263      * deviation for the samples within the window.
264      */
265     private double instantaneousStdY;
266 
267     /**
268      * Windowed standard deviation of z-coordinate of measurements for each
269      * processed triad expressed in meters per squared second (m/s^2) for
270      * acceleration, radians per second (rad/s) for angular speed or Teslas (T)
271      * for magnetic flux density.
272      * This value is updated for each processed sample containing measured standard
273      * deviation for the samples within the window.
274      */
275     private double instantaneousStdZ;
276 
277     /**
278      * Estimator to find instantaneous measurement noise level averaged for a certain window of samples.
279      */
280     private final WindowedTriadNoiseEstimator<U, M, T, ?, ?> windowedNoiseEstimator;
281 
282     /**
283      * Estimator to find accumulated accelerometer noise level.
284      */
285     private final AccumulatedTriadNoiseEstimator<U, M, T, ?, ?> accumulatedNoiseEstimator;
286 
287     /**
288      * Constructor.
289      *
290      * @param windowedNoiseEstimator    windowed noise estimator to estimate noise within a window of measures.
291      * @param accumulatedNoiseEstimator accumulated noise estimator to estimate accumulated noise and average.
292      */
293     protected TriadStaticIntervalDetector(
294             final WindowedTriadNoiseEstimator<U, M, T, ?, ?> windowedNoiseEstimator,
295             final AccumulatedTriadNoiseEstimator<U, M, T, ?, ?> accumulatedNoiseEstimator) {
296         this.windowedNoiseEstimator = windowedNoiseEstimator;
297         this.accumulatedNoiseEstimator = accumulatedNoiseEstimator;
298     }
299 
300     /**
301      * Constructor.
302      *
303      * @param windowedNoiseEstimator    windowed noise estimator to estimate noise within a window of measures.
304      * @param accumulatedNoiseEstimator accumulated noise estimator to estimate accumulated noise and average.
305      * @param listener                  listener to handle events generated by this detector.
306      */
307     protected TriadStaticIntervalDetector(
308             final WindowedTriadNoiseEstimator<U, M, T, ?, ?> windowedNoiseEstimator,
309             final AccumulatedTriadNoiseEstimator<U, M, T, ?, ?> accumulatedNoiseEstimator, final L listener) {
310         this(windowedNoiseEstimator, accumulatedNoiseEstimator);
311         this.listener = listener;
312     }
313 
314     /**
315      * Gets length of number of samples to keep within the window being processed
316      * to determine instantaneous accelerometer noise level.
317      *
318      * @return length of number of samples to keep within the window.
319      */
320     public int getWindowSize() {
321         return windowSize;
322     }
323 
324     /**
325      * Sets length of number of samples to keep within the window being processed
326      * to determine instantaneous accelerometer noise level.
327      * Window size must always be larger than allowed minimum value, which is 2 and
328      * must have and odd value.
329      *
330      * @param windowSize length of number of samples to keep within the window.
331      * @throws LockedException          if detector is busy processing a previous sample.
332      * @throws IllegalArgumentException if provided value is not valid.
333      */
334     public void setWindowSize(final int windowSize) throws LockedException {
335         if (running) {
336             throw new LockedException();
337         }
338 
339         windowedNoiseEstimator.setWindowSize(windowSize);
340         this.windowSize = windowSize;
341     }
342 
343     /**
344      * Gets number of samples to be processed initially while keeping the sensor static in order
345      * to find the base noise level when device is static.
346      *
347      * @return number of samples to be processed initially.
348      */
349     public int getInitialStaticSamples() {
350         return initialStaticSamples;
351     }
352 
353     /**
354      * Sets number of samples to be processed initially while keeping the sensor static in order
355      * to find the base noise level when device is static.
356      *
357      * @param initialStaticSamples number of samples to be processed initially.
358      * @throws LockedException          if detector is busy.
359      * @throws IllegalArgumentException if provided value is less than {@link #MINIMUM_INITIAL_STATIC_SAMPLES}
360      */
361     public void setInitialStaticSamples(final int initialStaticSamples) throws LockedException {
362         if (running) {
363             throw new LockedException();
364         }
365 
366         if (initialStaticSamples < MINIMUM_INITIAL_STATIC_SAMPLES) {
367             throw new IllegalArgumentException();
368         }
369 
370         this.initialStaticSamples = initialStaticSamples;
371     }
372 
373     /**
374      * Gets factor to be applied to detected base noise level in order to
375      * determine threshold for static/dynamic period changes. This factor is
376      * unit-less.
377      *
378      * @return factor to be applied to detected base noise level.
379      */
380     public double getThresholdFactor() {
381         return thresholdFactor;
382     }
383 
384     /**
385      * Sets factor to be applied to detected base noise level in order to
386      * determine threshold for static/dynamic period changes. This factor is
387      * unit-less.
388      *
389      * @param thresholdFactor factor to be applied to detected base noise level.
390      * @throws LockedException          if detector is busy.
391      * @throws IllegalArgumentException if provided value is zero or negative.
392      */
393     public void setThresholdFactor(final double thresholdFactor) throws LockedException {
394         if (running) {
395             throw new LockedException();
396         }
397         if (thresholdFactor <= 0.0) {
398             throw new IllegalArgumentException();
399         }
400 
401         this.thresholdFactor = thresholdFactor;
402     }
403 
404     /**
405      * Gets factor to determine that a sudden movement has occurred during
406      * initialization if instantaneous noise level exceeds accumulated noise
407      * level by this factor amount.
408      * This factor is unit-less.
409      *
410      * @return factor to determine that a sudden movement has occurred.
411      */
412     public double getInstantaneousNoiseLevelFactor() {
413         return instantaneousNoiseLevelFactor;
414     }
415 
416     /**
417      * Sets factor to determine that a sudden movement has occurred during
418      * initialization if instantaneous noise level exceeds accumulated noise
419      * level by this factor amount.
420      * This factor is unit-less.
421      *
422      * @param instantaneousNoiseLevelFactor factor to determine that a sudden
423      *                                      movement has occurred during
424      *                                      initialization.
425      * @throws LockedException          if detector is busy.
426      * @throws IllegalArgumentException if provided value is zero or negative.
427      */
428     public void setInstantaneousNoiseLevelFactor(
429             final double instantaneousNoiseLevelFactor) throws LockedException {
430         if (running) {
431             throw new LockedException();
432         }
433         if (instantaneousNoiseLevelFactor <= 0.0) {
434             throw new IllegalArgumentException();
435         }
436 
437         this.instantaneousNoiseLevelFactor = instantaneousNoiseLevelFactor;
438     }
439 
440     /**
441      * Gets overall absolute threshold to determine whether there has been
442      * excessive motion during the whole initialization phase.
443      * Failure will be detected if estimated base noise level exceeds this
444      * threshold when initialization completes.
445      * This threshold is expressed in meters per squared second (m/s^2) for
446      * acceleration, radians per second (rad/s) for angular speed or Teslas
447      * (T) for magnetic flux density.
448      *
449      * @return overall absolute threshold to determine whether there has
450      * been excessive motion.
451      */
452     public double getBaseNoiseLevelAbsoluteThreshold() {
453         return baseNoiseLevelAbsoluteThreshold;
454     }
455 
456     /**
457      * Sets overall absolute threshold to determine whether there has been
458      * excessive motion during the whole initialization phase.
459      * Failure will be detected if estimated base noise level exceeds this
460      * threshold when initialization completes.
461      * This threshold is expressed in meters per squared second (m/s^2) for
462      * acceleration, radians per second (rad/s) for angular speed or Teslas
463      * (T) for magnetic flux density.
464      *
465      * @param baseNoiseLevelAbsoluteThreshold overall absolute threshold to
466      *                                        determine whether there has been
467      *                                        excessive motion.
468      * @throws LockedException          if detector is busy.
469      * @throws IllegalArgumentException if provided value is zero or negative.
470      */
471     public void setBaseNoiseLevelAbsoluteThreshold(
472             final double baseNoiseLevelAbsoluteThreshold) throws LockedException {
473         if (running) {
474             throw new LockedException();
475         }
476         if (baseNoiseLevelAbsoluteThreshold <= 0.0) {
477             throw new IllegalArgumentException();
478         }
479 
480         this.baseNoiseLevelAbsoluteThreshold = baseNoiseLevelAbsoluteThreshold;
481     }
482 
483     /**
484      * Gets overall absolute threshold to determine whether there has been
485      * excessive motion during the whole initialization phase.
486      * Failure will be detected if estimated base noise level exceeds this
487      * threshold when initialization completes.
488      *
489      * @return overall absolute threshold to determine whether there has been
490      * excessive motion.
491      */
492     public M getBaseNoiseLevelAbsoluteThresholdAsMeasurement() {
493         return createMeasurement(baseNoiseLevelAbsoluteThreshold, getDefaultUnit());
494     }
495 
496     /**
497      * Gets overall absolute threshold to determine whether there has been
498      * excessive motion during the whole initialization phase.
499      * Failure will be detected if estimated base noise level exceeds this
500      * threshold when initialization completes.
501      *
502      * @param result instance where result will be stored.
503      */
504     public void getBaseNoiseLevelAbsoluteThresholdAsMeasurement(final M result) {
505         result.setValue(baseNoiseLevelAbsoluteThreshold);
506         result.setUnit(getDefaultUnit());
507     }
508 
509     /**
510      * Sets overall absolute threshold to determine whether there has been
511      * excessive motion during the whole initialization phase.
512      * Failure will be detected if estimated base noise level exceeds this
513      * threshold when initialization completes.
514      *
515      * @param baseNoiseLevelAbsoluteThreshold overall absolute threshold to
516      *                                        determine whether there has been
517      *                                        excessive motion.
518      * @throws LockedException          if detector is busy.
519      * @throws IllegalArgumentException if provided value is zero or negative.
520      */
521     public void setBaseNoiseLevelAbsoluteThreshold(
522             final M baseNoiseLevelAbsoluteThreshold) throws LockedException {
523         if (running) {
524             throw new LockedException();
525         }
526 
527         setBaseNoiseLevelAbsoluteThreshold(convertMeasurement(baseNoiseLevelAbsoluteThreshold));
528     }
529 
530     /**
531      * Gets listener to handle events generated by this detector.
532      *
533      * @return listener to handle events.
534      */
535     public L getListener() {
536         return listener;
537     }
538 
539     /**
540      * Sets listener to handle events generated by this detector.
541      *
542      * @param listener listener to handle events.
543      * @throws LockedException if detector is busy.
544      */
545     public void setListener(final L listener) throws LockedException {
546         if (running) {
547             throw new LockedException();
548         }
549 
550         this.listener = listener;
551     }
552 
553     /**
554      * Gets time interval between triad samples expressed in seconds (s).
555      *
556      * @return time interval between triad samples.
557      */
558     public double getTimeInterval() {
559         return windowedNoiseEstimator.getTimeInterval();
560     }
561 
562     /**
563      * Sets time interval between triad samples expressed in
564      * seconds (s).
565      *
566      * @param timeInterval time interval between triad samples.
567      * @throws IllegalArgumentException if provided value is negative.
568      * @throws LockedException          if estimator is currently running.
569      */
570     public void setTimeInterval(final double timeInterval) throws LockedException {
571         if (running) {
572             throw new LockedException();
573         }
574 
575         if (timeInterval < 0.0) {
576             throw new IllegalArgumentException();
577         }
578 
579         windowedNoiseEstimator.setTimeInterval(timeInterval);
580         accumulatedNoiseEstimator.setTimeInterval(timeInterval);
581     }
582 
583     /**
584      * Gets time interval between triad samples.
585      *
586      * @return time interval between triad samples.
587      */
588     public Time getTimeIntervalAsTime() {
589         return new Time(getTimeInterval(), TimeUnit.SECOND);
590     }
591 
592     /**
593      * Gets time interval between triad samples.
594      *
595      * @param result instance where time interval will be stored.
596      */
597     public void getTimeIntervalAsTime(final Time result) {
598         result.setValue(getTimeInterval());
599         result.setUnit(TimeUnit.SECOND);
600     }
601 
602     /**
603      * Sets time interval between triad samples.
604      *
605      * @param timeInterval time interval between triad samples.
606      * @throws IllegalArgumentException if provided value is negative.
607      * @throws LockedException          if estimator is currently running.
608      */
609     public void setTimeInterval(final Time timeInterval) throws LockedException {
610         setTimeInterval(TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(),
611                 TimeUnit.SECOND));
612     }
613 
614     /**
615      * Gets current status of this detector.
616      *
617      * @return current status of this detector.
618      */
619     public Status getStatus() {
620         return status;
621     }
622 
623     /**
624      * Gets measurement base noise level that has been detected during
625      * initialization expressed in meters per squared second (m/s^2) for
626      * acceleration, radians per second (rad/s) for angular speed or
627      * Teslas (T) for magnetic flux density.
628      *
629      * @return base noise level.
630      */
631     public double getBaseNoiseLevel() {
632         return baseNoiseLevel;
633     }
634 
635     /**
636      * Gets measurement base noise level that has been detected during
637      * initialization.
638      *
639      * @return measurement base noise level.
640      */
641     public M getBaseNoiseLevelAsMeasurement() {
642         return createMeasurement(baseNoiseLevel, getDefaultUnit());
643     }
644 
645     /**
646      * Gets measurement base noise level that has been detected during
647      * initialization.
648      *
649      * @param result instance where result will be stored.
650      */
651     public void getBaseNoiseLevelAsMeasurement(final M result) {
652         result.setValue(baseNoiseLevel);
653         result.setUnit(getDefaultUnit());
654     }
655 
656     /**
657      * Gets measurement base noise level PSD (Power Spectral Density) expressed in
658      * (m^2 * s^-3) for accelerometer, (rad^2/s) for gyroscope or (T^2 * s) for
659      * magnetometer.
660      *
661      * @return measurement base noise level PSD.
662      */
663     public double getBaseNoiseLevelPsd() {
664         return baseNoiseLevel * baseNoiseLevel * getTimeInterval();
665     }
666 
667     /**
668      * Gets measurement base noise level root PSD (Power SpectralDensity) expressed
669      * in (m * s^-1.5) for accelerometer, (rad * s^-0.5) for gyroscope or (T * s^0.5)
670      * for magnetometer.
671      *
672      * @return measurement base noise level root PSD.
673      */
674     public double getBaseNoiseLevelRootPsd() {
675         return baseNoiseLevel * Math.sqrt(getTimeInterval());
676     }
677 
678     /**
679      * Gets threshold to determine static/dynamic period changes expressed in
680      * meters per squared second (m/s^2) for acceleration, radians per second
681      * (rad/s) for angular speed or Teslas (T) for magnetic flux density.
682      *
683      * @return threshold to determine static/dynamic period changes.
684      */
685     public double getThreshold() {
686         return threshold;
687     }
688 
689     /**
690      * Gets threshold to determine static/dynamic period changes.
691      *
692      * @return threshold to determine static/dynamic period changes.
693      */
694     public M getThresholdAsMeasurement() {
695         return createMeasurement(threshold, getDefaultUnit());
696     }
697 
698     /**
699      * Gets threshold to determine static/dynamic period changes.
700      *
701      * @param result instance where result will be stored.
702      */
703     public void getThresholdAsMeasurement(final M result) {
704         result.setValue(threshold);
705         result.setUnit(getDefaultUnit());
706     }
707 
708     /**
709      * Indicates whether this detector is busy processing last provided sample.
710      *
711      * @return true if this detector is busy, false otherwise.
712      */
713     public boolean isRunning() {
714         return running;
715     }
716 
717     /**
718      * Gets number of samples that have been processed so far.
719      *
720      * @return number of samples that have been processed so far.
721      */
722     public int getProcessedSamples() {
723         return processedSamples;
724     }
725 
726     /**
727      * Gets average x-coordinate of measurements accumulated during last static
728      * period expressed in meters per squared second (m/s^2) for acceleration,
729      * radians per second (rad/s) for angular speed or Teslas (T) for
730      * magnetic flux density.
731      * This value is updated when switching from a static period to a dynamic
732      * one or after completing initialization.
733      *
734      * @return accumulated average x-coordinate of measurement during last static
735      * period.
736      */
737     public double getAccumulatedAvgX() {
738         return accumulatedAvgX;
739     }
740 
741     /**
742      * Gets average x-coordinate of measurements accumulated during last static
743      * period.
744      * This value is updated when switching from a static period to a dynamic
745      * one or after completing initialization.
746      *
747      * @return accumulated average x-coordinate of measurement during last static
748      * period.
749      */
750     public M getAccumulatedAvgXAsMeasurement() {
751         return createMeasurement(accumulatedAvgX, getDefaultUnit());
752     }
753 
754     /**
755      * Gets average x-coordinate of measurements accumulated during last static
756      * period.
757      * This value is updated when switching from a static period to a dynamic
758      * one or after completing initialization.
759      *
760      * @param result instance where result will be stored.
761      */
762     public void getAccumulatedAvgXAsMeasurement(final M result) {
763         result.setValue(accumulatedAvgX);
764         result.setUnit(getDefaultUnit());
765     }
766 
767     /**
768      * Gets average y-coordinate of measurements accumulated during last static
769      * period expressed in meters per squared second (m/s^2) for acceleration,
770      * radians per second (rad/s) for angular speed or Teslas (T) for
771      * magnetic flux density.
772      * This value is updated when switching from a static period to a dynamic
773      * one or after completing initialization.
774      *
775      * @return accumulated average y-coordinate of measurement during last static
776      * period.
777      */
778     public double getAccumulatedAvgY() {
779         return accumulatedAvgY;
780     }
781 
782     /**
783      * Gets average y-coordinate of measurements accumulated during last static
784      * period.
785      * This value is updated when switching from a static period to a dynamic
786      * one or after completing initialization.
787      *
788      * @return accumulated average y-coordinate of measurement during last static
789      * period.
790      */
791     public M getAccumulatedAvgYAsMeasurement() {
792         return createMeasurement(accumulatedAvgY, getDefaultUnit());
793     }
794 
795     /**
796      * Gets average y-coordinate of measurements accumulated during last static
797      * period.
798      * This value is updated when switching from a static period to a dynamic
799      * one or after completing initialization.
800      *
801      * @param result instance where result will be stored.
802      */
803     public void getAccumulatedAvgYAsMeasurement(final M result) {
804         result.setValue(accumulatedAvgY);
805         result.setUnit(getDefaultUnit());
806     }
807 
808     /**
809      * Gets average z-coordinate of measurements accumulated during last static
810      * period expressed in meters per squared second (m/s^2) for acceleration,
811      * radians per second (rad/s) for angular speed or Teslas (T) for
812      * magnetic flux density.
813      * This value is updated when switching from a static period to a dynamic
814      * one or after completing initialization.
815      *
816      * @return accumulated average y-coordinate of measurement during last static
817      * period.
818      */
819     public double getAccumulatedAvgZ() {
820         return accumulatedAvgZ;
821     }
822 
823     /**
824      * Gets average z-coordinate of measurements accumulated during last static
825      * period.
826      * This value is updated when switching from a static period to a dynamic
827      * one or after completing initialization.
828      *
829      * @return accumulated average z-coordinate of measurement during last static
830      * period.
831      */
832     public M getAccumulatedAvgZAsMeasurement() {
833         return createMeasurement(accumulatedAvgZ, getDefaultUnit());
834     }
835 
836     /**
837      * Gets average z-coordinate of measurements accumulated during last static
838      * period.
839      * This value is updated when switching from a static period to a dynamic
840      * one or after completing initialization.
841      *
842      * @param result instance where result will be stored.
843      */
844     public void getAccumulatedAvgZAsMeasurement(final M result) {
845         result.setValue(accumulatedAvgZ);
846         result.setUnit(getDefaultUnit());
847     }
848 
849     /**
850      * Gets average measurements triad accumulated during last static period.
851      * This value is updated when switching from a static period to a dynamic
852      * one or after completing initialization.
853      *
854      * @return accumulated average measurements triad during last static period.
855      */
856     public T getAccumulatedAvgTriad() {
857         return createTriad(accumulatedAvgX, accumulatedAvgY, accumulatedAvgZ, getDefaultUnit());
858     }
859 
860     /**
861      * Gets average measurements triad accumulated during last static period.
862      * This value is updated when switching from a static period to a dynamic
863      * one or after completing initialization.
864      *
865      * @param result instance where result will be stored.
866      */
867     public void getAccumulatedAvgTriad(final T result) {
868         result.setValueCoordinatesAndUnit(accumulatedAvgX, accumulatedAvgY, accumulatedAvgZ, getDefaultUnit());
869     }
870 
871     /**
872      * Gets standard deviation of x-coordinate of measurements accumulated during
873      * last static period expressed in meters per squared second (m/s^2) for
874      * acceleration, radians per second (rad/s) for angular speed or Teslas
875      * (T) for magnetic flux density.
876      * This value is updated when switching from a static period to a dynamic
877      * one or after completing initialization.
878      *
879      * @return standard deviation of x-coordinate of measurements accumulated
880      * during last static period.
881      */
882     public double getAccumulatedStdX() {
883         return accumulatedStdX;
884     }
885 
886     /**
887      * Gets standard deviation of x-coordinate of measurements accumulated during
888      * last static period.
889      * This value is updated when switching from a static period to a dynamic
890      * one or after completing initialization.
891      *
892      * @return standard deviation of x-coordinate of measurements accumulated
893      * during last static period.
894      */
895     public M getAccumulatedStdXAsMeasurement() {
896         return createMeasurement(accumulatedStdX, getDefaultUnit());
897     }
898 
899     /**
900      * Gets standard deviation of x-coordinate of measurements accumulated during
901      * last static period.
902      * This value is updated when switching from a static period to a dynamic
903      * one or after completing initialization.
904      *
905      * @param result instance where result will be stored.
906      */
907     public void getAccumulatedStdXAsMeasurement(final M result) {
908         result.setValue(accumulatedStdX);
909         result.setUnit(getDefaultUnit());
910     }
911 
912     /**
913      * Gets standard deviation of y-coordinate of measurements accumulated during
914      * last static period expressed in meters per squared second (m/s^2) for
915      * acceleration, radians per second (rad/s) for angular speed or Teslas
916      * (T) for magnetic flux density.
917      * This value is updated when switching from a static period to a dynamic
918      * one or after completing initialization.
919      *
920      * @return standard deviation of y-coordinate of measurements accumulated
921      * during last static period.
922      */
923     public double getAccumulatedStdY() {
924         return accumulatedStdY;
925     }
926 
927     /**
928      * Gets standard deviation of y-coordinate of measurements accumulated during
929      * last static period.
930      * This value is updated when switching from a static period to a dynamic
931      * one or after completing initialization.
932      *
933      * @return standard deviation of y-coordinate of measurements accumulated
934      * during last static period.
935      */
936     public M getAccumulatedStdYAsMeasurement() {
937         return createMeasurement(accumulatedStdY, getDefaultUnit());
938     }
939 
940     /**
941      * Gets standard deviation of y-coordinate of measurements accumulated during
942      * last static period.
943      * This value is updated when switching from a static period to a dynamic
944      * one or after completing initialization.
945      *
946      * @param result instance where result will be stored.
947      */
948     public void getAccumulatedStdYAsMeasurement(final M result) {
949         result.setValue(accumulatedStdY);
950         result.setUnit(getDefaultUnit());
951     }
952 
953     /**
954      * Gets standard deviation of z-coordinate of measurements accumulated during
955      * last static period expressed in meters per squared second (m/s^2) for
956      * acceleration, radians per second (rad/s) for angular speed or Teslas
957      * (T) for magnetic flux density.
958      * This value is updated when switching from a static period to a dynamic
959      * one or after completing initialization.
960      *
961      * @return standard deviation of z-coordinate of measurements accumulated
962      * during last static period.
963      */
964     public double getAccumulatedStdZ() {
965         return accumulatedStdZ;
966     }
967 
968     /**
969      * Gets standard deviation of z-coordinate of measurements accumulated during
970      * last static period.
971      * This value is updated when switching from a static period to a dynamic
972      * one or after completing initialization.
973      *
974      * @return standard deviation of z-coordinate of measurements accumulated
975      * during last static period.
976      */
977     public M getAccumulatedStdZAsMeasurement() {
978         return createMeasurement(accumulatedStdZ, getDefaultUnit());
979     }
980 
981     /**
982      * Gets standard deviation of z-coordinate of measurements accumulated during
983      * last static period.
984      * This value is updated when switching from a static period to a dynamic
985      * one or after completing initialization.
986      *
987      * @param result instance where result will be stored.
988      */
989     public void getAccumulatedStdZAsMeasurement(final M result) {
990         result.setValue(accumulatedStdZ);
991         result.setUnit(getDefaultUnit());
992     }
993 
994     /**
995      * Gets standard deviation of measurements accumulated during last static
996      * period.
997      * This value is updated when switching from a static period to a dynamic
998      * one or after completing initialization.
999      *
1000      * @return standard deviation of measurements accumulated during last static
1001      * period.
1002      */
1003     public T getAccumulatedStdTriad() {
1004         return createTriad(accumulatedStdX, accumulatedStdY, accumulatedStdZ, getDefaultUnit());
1005     }
1006 
1007     /**
1008      * Gets standard deviation of measurements accumulated during last static
1009      * period.
1010      * This value is updated when switching from a static period to a dynamic
1011      * one or after completing initialization.
1012      *
1013      * @param result instance where result will be stored.
1014      */
1015     public void getAccumulatedStdTriad(final T result) {
1016         result.setValueCoordinatesAndUnit(accumulatedStdX, accumulatedStdY, accumulatedStdZ, getDefaultUnit());
1017     }
1018 
1019     /**
1020      * Gets windowed average x-coordinate of measurements for each processed triad
1021      * expressed in meters per squared second (m/s^2) for acceleration,
1022      * radians per second (rad/s) for angular speed or Teslas (T) for
1023      * magnetic flux density.
1024      * This value is updated for each processed sample containing an average
1025      * value for the samples within the window.
1026      *
1027      * @return windowed average x-coordinate of measurements for each processed
1028      * triad.
1029      */
1030     public double getInstantaneousAvgX() {
1031         return instantaneousAvgX;
1032     }
1033 
1034     /**
1035      * Gets windowed average x-coordinate of measurements for each processed triad.
1036      * This value is updated for each processed sample containing an average
1037      * value for the samples within the window.
1038      *
1039      * @return windowed average x-coordinate of measurements for each processed
1040      * triad.
1041      */
1042     public M getInstantaneousAvgXAsMeasurement() {
1043         return createMeasurement(instantaneousAvgX, getDefaultUnit());
1044     }
1045 
1046     /**
1047      * Gets windowed average x-coordinate of measurements for each processed triad.
1048      * This value is updated for each processed sample containing an average
1049      * value for the samples within the window.
1050      *
1051      * @param result instance where result will be stored.
1052      */
1053     public void getInstantaneousAvgXAsMeasurement(final M result) {
1054         result.setValue(instantaneousAvgX);
1055         result.setUnit(getDefaultUnit());
1056     }
1057 
1058     /**
1059      * Gets windowed average y-coordinate of measurements for each processed triad
1060      * expressed in meters per squared second (m/s^2) for acceleration,
1061      * radians per second (rad/s) for angular speed or Teslas (T) for
1062      * magnetic flux density.
1063      * This value is updated for each processed sample containing an average
1064      * value for the samples within the window.
1065      *
1066      * @return windowed average y-coordinate of measurements for each processed
1067      * triad.
1068      */
1069     public double getInstantaneousAvgY() {
1070         return instantaneousAvgY;
1071     }
1072 
1073     /**
1074      * Gets windowed average y-coordinate of measurements for each processed triad.
1075      * This value is updated for each processed sample containing an average
1076      * value for the samples within the window.
1077      *
1078      * @return windowed average y-coordinate of measurements for each processed
1079      * triad.
1080      */
1081     public M getInstantaneousAvgYAsMeasurement() {
1082         return createMeasurement(instantaneousAvgY, getDefaultUnit());
1083     }
1084 
1085     /**
1086      * Gets windowed average y-coordinate of measurements for each processed triad.
1087      * This value is updated for each processed sample containing an average
1088      * value for the samples within the window.
1089      *
1090      * @param result instance where result will be stored.
1091      */
1092     public void getInstantaneousAvgYAsMeasurement(final M result) {
1093         result.setValue(instantaneousAvgY);
1094         result.setUnit(getDefaultUnit());
1095     }
1096 
1097     /**
1098      * Gets windowed average z-coordinate of measurements for each processed triad
1099      * expressed in meters per squared second (m/s^2) for acceleration,
1100      * radians per second (rad/s) for angular speed or Teslas (T) for
1101      * magnetic flux density.
1102      * This value is updated for each processed sample containing an average
1103      * value for the samples within the window.
1104      *
1105      * @return windowed average z-coordinate of measurements for each processed
1106      * triad.
1107      */
1108     public double getInstantaneousAvgZ() {
1109         return instantaneousAvgZ;
1110     }
1111 
1112     /**
1113      * Gets windowed average z-coordinate of measurements for each processed triad.
1114      * This value is updated for each processed sample containing an average
1115      * value for the samples within the window.
1116      *
1117      * @return windowed average z-coordinate of measurements for each processed
1118      * triad.
1119      */
1120     public M getInstantaneousAvgZAsMeasurement() {
1121         return createMeasurement(instantaneousAvgZ, getDefaultUnit());
1122     }
1123 
1124     /**
1125      * Gets windowed average z-coordinate of measurements for each processed triad.
1126      * This value is updated for each processed sample containing an average
1127      * value for the samples within the window.
1128      *
1129      * @param result instance where result will be stored.
1130      */
1131     public void getInstantaneousAvgZAsMeasurement(final M result) {
1132         result.setValue(instantaneousAvgZ);
1133         result.setUnit(getDefaultUnit());
1134     }
1135 
1136     /**
1137      * Gets windowed average of measurements for each processed triad.
1138      * This value is updated for each processed sample containing an average
1139      * value for the samples within the window.
1140      *
1141      * @return windowed average of measurements for each processed triad.
1142      */
1143     public T getInstantaneousAvgTriad() {
1144         return createTriad(instantaneousAvgX, instantaneousAvgY, instantaneousAvgZ, getDefaultUnit());
1145     }
1146 
1147     /**
1148      * Gets windowed average of measurements for each processed triad.
1149      * This value is updated for each processed sample containing an average
1150      * value for the samples within the window.
1151      *
1152      * @param result instance where result will be stored.
1153      */
1154     public void getInstantaneousAvgTriad(final T result) {
1155         result.setValueCoordinatesAndUnit(instantaneousAvgX, instantaneousAvgY, instantaneousAvgZ, getDefaultUnit());
1156     }
1157 
1158     /**
1159      * Gets windowed standard deviation of x-coordinate of measurements for each
1160      * processed triad expressed in meters per squared second (m/s^2) for
1161      * acceleration, radians per second (rad/s) for angular speed or Teslas
1162      * (T) for magnetic flux density.
1163      * This value is updated for each processed sample containing measured standard
1164      * deviation for the samples within the window.
1165      *
1166      * @return windowed standard deviation of x-coordinate of measurements for
1167      * each processed triad.
1168      */
1169     public double getInstantaneousStdX() {
1170         return instantaneousStdX;
1171     }
1172 
1173     /**
1174      * Gets windowed standard deviation of x-coordinate of measurements for each
1175      * processed triad.
1176      * This value is updated for each processed sample containing measured standard
1177      * deviation for the samples within the window.
1178      *
1179      * @return windowed standard deviation of x-coordinate of measurements for
1180      * each processed triad.
1181      */
1182     public M getInstantaneousStdXAsMeasurement() {
1183         return createMeasurement(instantaneousStdX, getDefaultUnit());
1184     }
1185 
1186     /**
1187      * Gets windowed standard deviation of x-coordinate of measurements for each
1188      * processed triad.
1189      * This value is updated for each processed sample containing measured standard
1190      * deviation for the samples within the window.
1191      *
1192      * @param result instance where result will be stored.
1193      */
1194     public void getInstantaneousStdXAsMeasurement(final M result) {
1195         result.setValue(instantaneousStdX);
1196         result.setUnit(getDefaultUnit());
1197     }
1198 
1199     /**
1200      * Gets windowed standard deviation of y-coordinate of measurements for each
1201      * processed triad expressed in meters per squared second (m/s^2) for
1202      * acceleration, radians per second (rad/s) for angular speed or Teslas
1203      * (T) for magnetic flux density.
1204      * This value is updated for each processed sample containing measured standard
1205      * deviation for the samples within the window.
1206      *
1207      * @return windowed standard deviation of y-coordinate of measurements for
1208      * each processed triad.
1209      */
1210     public double getInstantaneousStdY() {
1211         return instantaneousStdY;
1212     }
1213 
1214     /**
1215      * Gets windowed standard deviation of y-coordinate of measurements for each
1216      * processed triad.
1217      * This value is updated for each processed sample containing measured standard
1218      * deviation of the samples within the window.
1219      *
1220      * @return windowed standard deviation of y-coordinate of measurements for
1221      * each processed triad.
1222      */
1223     public M getInstantaneousStdYAsMeasurement() {
1224         return createMeasurement(instantaneousStdY, getDefaultUnit());
1225     }
1226 
1227     /**
1228      * Gets windowed standard deviation of y-coordinate of measurements for each
1229      * processed triad.
1230      * This value is updated for each processed sample containing measured standard
1231      * deviation of the samples within the window.
1232      *
1233      * @param result instance where result will be stored.
1234      */
1235     public void getInstantaneousStdYAsMeasurement(final M result) {
1236         result.setValue(instantaneousStdY);
1237         result.setUnit(getDefaultUnit());
1238     }
1239 
1240     /**
1241      * Gets windowed standard deviation of z-coordinate of measurements for each
1242      * processed triad expressed in meters per squared second (m/s^2) for
1243      * acceleration, radians per second (rad/s) for angular speed or Teslas (T)
1244      * for magnetic flux density.
1245      * This value is updated for each processed sample containing measured standard
1246      * deviation for the samples within the window.
1247      *
1248      * @return windowed standard deviation of z-coordinate of measurements for
1249      * each processed triad.
1250      */
1251     public double getInstantaneousStdZ() {
1252         return instantaneousStdZ;
1253     }
1254 
1255     /**
1256      * Gets windowed standard deviation of z-coordinate of measurements for each
1257      * processed triad.
1258      * This value is updated for each processed sample containing measured standard
1259      * deviation for the samples within the window.
1260      *
1261      * @return windowed standard deviation of z-coordinate of measurements for
1262      * each processed triad.
1263      */
1264     public M getInstantaneousStdZAsMeasurement() {
1265         return createMeasurement(instantaneousStdZ, getDefaultUnit());
1266     }
1267 
1268     /**
1269      * Gets windowed standard deviation of z-coordinate of measurements for each
1270      * processed triad.
1271      * This value is updated for each processed sample containing measured standard
1272      * deviation for the samples within the window.
1273      *
1274      * @param result instance where result will be stored.
1275      */
1276     public void getInstantaneousStdZAsMeasurement(final M result) {
1277         result.setValue(instantaneousStdZ);
1278         result.setUnit(getDefaultUnit());
1279     }
1280 
1281     /**
1282      * Gets windowed standard deviation of measurements for each processed triad.
1283      * This value is updated for each processed sample containing measured standard
1284      * deviation for the samples within the window.
1285      *
1286      * @return windowed standard deviation of measurements for each processed
1287      * triad.
1288      */
1289     public T getInstantaneousStdTriad() {
1290         return createTriad(instantaneousStdX, instantaneousStdY, instantaneousStdZ, getDefaultUnit());
1291     }
1292 
1293     /**
1294      * Gets windowed standard deviation of measurements for each processed triad.
1295      * This value is updated for each processed sample containing measured standard
1296      * deviation for the samples within the window.
1297      *
1298      * @param result instance where result will be stored.
1299      */
1300     public void getInstantaneousStdTriad(final T result) {
1301         result.setValueCoordinatesAndUnit(instantaneousStdX, instantaneousStdY, instantaneousStdZ, getDefaultUnit());
1302     }
1303 
1304     /**
1305      * Processes a new measurement triad sample.
1306      *
1307      * @param triad a new measurement triad to be processed.
1308      * @return true if provided triad has been processed, false if provided triad has been skipped because detector
1309      * previously failed. If detector previously failed, it will need to be reset before processing additional
1310      * samples.
1311      * @throws LockedException if detector is busy processing a previous sample.
1312      */
1313     public boolean process(final T triad) throws LockedException {
1314         return process(convertMeasurement(triad.getValueX(), triad.getUnit()),
1315                 convertMeasurement(triad.getValueY(), triad.getUnit()),
1316                 convertMeasurement(triad.getValueZ(), triad.getUnit()));
1317     }
1318 
1319     /**
1320      * Processes a new measurement triad sample.
1321      *
1322      * @param valueX x-coordinate of sensed measurement.
1323      * @param valueY y-coordinate of sensed measurement.
1324      * @param valueZ z-coordinate of sensed measurement.
1325      * @return true if provided triad has been processed, false if provided triad has been skipped because detector
1326      * previously failed. If detector previously failed, it will need to be reset before processing additional
1327      * samples.
1328      * @throws LockedException if detector is busy processing a previous sample.
1329      */
1330     public boolean process(final M valueX, final M valueY, final M valueZ) throws LockedException {
1331         return process(convertMeasurement(valueX), convertMeasurement(valueY), convertMeasurement(valueZ));
1332     }
1333 
1334     /**
1335      * Processes a new measurement triad sample.
1336      * Provided measurement coordinates are expressed in meters per squared second (m/s^2) for acceleration,
1337      * radians per second (rad/s) for angular speed or Teslas (T) for magnetic flux density.
1338      *
1339      * @param valueX x-coordinate of sensed measurement.
1340      * @param valueY y-coordinate of sensed measurement.
1341      * @param valueZ z-coordinate of sensed measurement.
1342      * @return true if provided triad has been processed, false if provided triad has been skipped because detector
1343      * previously failed. If detector previously failed, it will need to be reset before processing additional
1344      * samples.
1345      * @throws LockedException if detector is busy processing a previous sample.
1346      */
1347     public boolean process(final double valueX, final double valueY, final double valueZ) throws LockedException {
1348         if (running) {
1349             throw new LockedException();
1350         }
1351 
1352         if (status == Status.FAILED) {
1353             return false;
1354         }
1355 
1356         running = true;
1357 
1358         if (status == Status.IDLE) {
1359             // start initialization
1360             status = Status.INITIALIZING;
1361 
1362             if (listener != null) {
1363                 //noinspection unchecked
1364                 listener.onInitializationStarted((D) this);
1365             }
1366         }
1367 
1368         processedSamples++;
1369 
1370         windowedNoiseEstimator.addTriadAndProcess(valueX, valueY, valueZ);
1371 
1372         instantaneousAvgX = windowedNoiseEstimator.getAvgX();
1373         instantaneousAvgY = windowedNoiseEstimator.getAvgY();
1374         instantaneousAvgZ = windowedNoiseEstimator.getAvgZ();
1375 
1376         instantaneousStdX = windowedNoiseEstimator.getStandardDeviationX();
1377         instantaneousStdY = windowedNoiseEstimator.getStandardDeviationY();
1378         instantaneousStdZ = windowedNoiseEstimator.getStandardDeviationZ();
1379 
1380         final var windowedStdNorm = windowedNoiseEstimator.getStandardDeviationNorm();
1381 
1382         final var filledWindow = windowedNoiseEstimator.isWindowFilled();
1383 
1384         if (status == Status.INITIALIZING) {
1385             // process sample during initialization
1386             accumulatedNoiseEstimator.addTriad(valueX, valueY, valueZ);
1387             final var accumulatedStdNorm = accumulatedNoiseEstimator.getStandardDeviationNorm();
1388 
1389             if (processedSamples < initialStaticSamples) {
1390                 if (filledWindow && (windowedStdNorm / accumulatedStdNorm > instantaneousNoiseLevelFactor)) {
1391                     // sudden motion detected
1392                     status = Status.FAILED;
1393 
1394                     // notify error
1395                     if (listener != null) {
1396                         //noinspection unchecked
1397                         listener.onError((D) this, accumulatedStdNorm, windowedStdNorm,
1398                                 ErrorReason.SUDDEN_EXCESSIVE_MOVEMENT_DETECTED);
1399                     }
1400                 }
1401 
1402             } else if (filledWindow) {
1403                 // initialization completed
1404                 // set base noise level and threshold
1405                 baseNoiseLevel = accumulatedStdNorm;
1406                 threshold = baseNoiseLevel * thresholdFactor;
1407 
1408                 // keep average/std measurements triad in case we want to obtain
1409                 // its value since initial period must be static
1410                 accumulatedAvgX = accumulatedNoiseEstimator.getAvgX();
1411                 accumulatedAvgY = accumulatedNoiseEstimator.getAvgY();
1412                 accumulatedAvgZ = accumulatedNoiseEstimator.getAvgZ();
1413 
1414                 accumulatedStdX = accumulatedNoiseEstimator.getStandardDeviationX();
1415                 accumulatedStdY = accumulatedNoiseEstimator.getStandardDeviationY();
1416                 accumulatedStdZ = accumulatedNoiseEstimator.getStandardDeviationZ();
1417 
1418                 // reset accumulated estimator so that we can estimate
1419                 // average specific force in static periods
1420                 accumulatedNoiseEstimator.reset();
1421 
1422                 if (baseNoiseLevel > baseNoiseLevelAbsoluteThreshold) {
1423                     // base noise level exceeds allowed value
1424                     status = Status.FAILED;
1425 
1426                     // notify error
1427                     if (listener != null) {
1428                         //noinspection unchecked
1429                         listener.onError((D) this, accumulatedStdNorm, windowedStdNorm,
1430                                 ErrorReason.OVERALL_EXCESSIVE_MOVEMENT_DETECTED);
1431                     }
1432 
1433                 } else {
1434                     // initialization has been successfully completed
1435                     status = Status.INITIALIZATION_COMPLETED;
1436 
1437                     if (listener != null) {
1438                         //noinspection unchecked
1439                         listener.onInitializationCompleted((D) this, baseNoiseLevel);
1440                     }
1441                 }
1442 
1443             }
1444 
1445             running = false;
1446             return true;
1447         } else {
1448             // detect static or dynamic period
1449             final var previousStatus = status;
1450 
1451             if (windowedStdNorm < threshold) {
1452                 status = Status.STATIC_INTERVAL;
1453             } else {
1454                 status = Status.DYNAMIC_INTERVAL;
1455             }
1456 
1457             if (status == Status.STATIC_INTERVAL) {
1458                 // while we are in static interval, keep adding samples to estimate
1459                 // accumulated average measurement triad
1460                 accumulatedNoiseEstimator.addTriad(valueX, valueY, valueZ);
1461             }
1462 
1463             if (previousStatus != status) {
1464                 // static/dynamic period change detected
1465                 if (status == Status.STATIC_INTERVAL && listener != null) {
1466                     //noinspection unchecked
1467                     listener.onStaticIntervalDetected((D) this,
1468                             instantaneousAvgX, instantaneousAvgY, instantaneousAvgZ,
1469                             instantaneousStdX, instantaneousStdY, instantaneousStdZ);
1470                 } else if (status == Status.DYNAMIC_INTERVAL) {
1471                     // when switching from static to dynamic interval,
1472                     // pick accumulated average and standard deviation measurement triads
1473                     accumulatedAvgX = accumulatedNoiseEstimator.getAvgX();
1474                     accumulatedAvgY = accumulatedNoiseEstimator.getAvgY();
1475                     accumulatedAvgZ = accumulatedNoiseEstimator.getAvgZ();
1476 
1477                     accumulatedStdX = accumulatedNoiseEstimator.getStandardDeviationX();
1478                     accumulatedStdY = accumulatedNoiseEstimator.getStandardDeviationY();
1479                     accumulatedStdZ = accumulatedNoiseEstimator.getStandardDeviationZ();
1480 
1481                     // reset accumulated estimator when switching to dynamic period
1482                     accumulatedNoiseEstimator.reset();
1483 
1484                     if (listener != null) {
1485                         //noinspection unchecked
1486                         listener.onDynamicIntervalDetected((D) this,
1487                                 instantaneousAvgX, instantaneousAvgY, instantaneousAvgZ,
1488                                 instantaneousStdX, instantaneousStdY, instantaneousStdZ,
1489                                 accumulatedAvgX, accumulatedAvgY, accumulatedAvgZ,
1490                                 accumulatedStdX, accumulatedStdY, accumulatedStdZ);
1491                     }
1492                 }
1493             }
1494         }
1495 
1496         running = false;
1497         return true;
1498     }
1499 
1500     /**
1501      * Resets this detector so that it is initialized again when new samples are added.
1502      *
1503      * @throws LockedException if detector is busy.
1504      */
1505     public void reset() throws LockedException {
1506         if (running) {
1507             throw new LockedException();
1508         }
1509 
1510         running = true;
1511 
1512         status = Status.IDLE;
1513         processedSamples = 0;
1514         baseNoiseLevel = 0.0;
1515         threshold = 0.0;
1516 
1517         windowedNoiseEstimator.reset();
1518         accumulatedNoiseEstimator.reset();
1519 
1520         if (listener != null) {
1521             //noinspection unchecked
1522             listener.onReset((D) this);
1523         }
1524 
1525         running = false;
1526     }
1527 
1528     /**
1529      * Converts provided measurement instance to its default unit, which is
1530      * meters per squared second (m/s^2) for acceleration, radians per second (rad/s) for
1531      * angular speed or Teslas (T) for magnetic flux density.
1532      *
1533      * @param measurement measurement to be converted.
1534      * @return converted value.
1535      */
1536     protected double convertMeasurement(M measurement) {
1537         return convertMeasurement(measurement.getValue().doubleValue(), measurement.getUnit());
1538     }
1539 
1540     /**
1541      * Converts provided measurement value expressed in provided unit to the
1542      * default measurement value, which is meters per squared second (m/s^2) for acceleration,
1543      * radians per second (rad/s) for angular speed or Teslas (t) for magnetic flux density.
1544      *
1545      * @param value value to be converted.
1546      * @param unit  unit of value to be converted.
1547      * @return converted value.
1548      */
1549     protected abstract double convertMeasurement(final double value, final U unit);
1550 
1551     /**
1552      * Creates a measurement instance using provided value and unit.
1553      *
1554      * @param value value of measurement.
1555      * @param unit  unit of value.
1556      * @return created measurement
1557      */
1558     protected abstract M createMeasurement(final double value, final U unit);
1559 
1560     /**
1561      * Gets default unit for measurements this implementation works with.
1562      *
1563      * @return default measurement unit.
1564      */
1565     protected abstract U getDefaultUnit();
1566 
1567     /**
1568      * Creates a triad.
1569      *
1570      * @param valueX x-coordinate value.
1571      * @param valueY y-coordinate value.
1572      * @param valueZ z-coordinate value.
1573      * @param unit   unit of values.
1574      * @return created triad.
1575      */
1576     protected abstract T createTriad(final double valueX, final double valueY, final double valueZ, final U unit);
1577 
1578     /**
1579      * Possible detector status values.
1580      */
1581     public enum Status {
1582         /**
1583          * Detector is in idle status when it hasn't processed any sample yet.
1584          */
1585         IDLE,
1586 
1587         /**
1588          * Detector is processing samples in the initial static process to determine base noise level.
1589          */
1590         INITIALIZING,
1591 
1592         /**
1593          * Detector has successfully completed processing samples on the initial
1594          * static period.
1595          */
1596         INITIALIZATION_COMPLETED,
1597 
1598         /**
1599          * A static interval has been detected, where accelerometer is considered to be subject to no substantial
1600          * movement forces.
1601          */
1602         STATIC_INTERVAL,
1603 
1604         /**
1605          * A dynamic interval has been detected, where accelerometer is considered to be subject to substantial
1606          * movement forces.
1607          */
1608         DYNAMIC_INTERVAL,
1609 
1610         /**
1611          * Detector has failed. This happens if accelerometer is subject to sudden movement forces while detector
1612          * is initializing during the initial static period.
1613          * When detector has failed, no new samples will be allowed to be processed until detector is reset.
1614          */
1615         FAILED
1616     }
1617 
1618     /**
1619      * Reason why this detector has failed during initialization.
1620      */
1621     public enum ErrorReason {
1622         /**
1623          * If a sudden movement is detected during initialization.
1624          */
1625         SUDDEN_EXCESSIVE_MOVEMENT_DETECTED,
1626 
1627         /**
1628          * If overall noise level is excessive during initialization.
1629          */
1630         OVERALL_EXCESSIVE_MOVEMENT_DETECTED
1631     }
1632 }