View Javadoc
1   /*
2    * Copyright (C) 2021 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.thresholdfactor;
17  
18  import com.irurueta.navigation.LockedException;
19  import com.irurueta.navigation.NotReadyException;
20  import com.irurueta.navigation.inertial.calibration.BodyKinematicsSequence;
21  import com.irurueta.navigation.inertial.calibration.gyroscope.GyroscopeCalibratorMeasurementOrSequenceType;
22  import com.irurueta.navigation.inertial.calibration.gyroscope.GyroscopeNonLinearCalibrator;
23  
24  /**
25   * Optimizes the threshold factor for interval detection of accelerometer and gyroscope data
26   * based on results of calibration.
27   * Only gyroscope calibrators based on unknown orientation are supported (in other terms,
28   * calibrators must be {@link GyroscopeNonLinearCalibrator} and must support
29   * {@link GyroscopeCalibratorMeasurementOrSequenceType#BODY_KINEMATICS_SEQUENCE}).
30   * This implementation makes an exhaustive search between minimum and maximum
31   * threshold factor values to find the threshold value that produces the
32   * minimum Mean Square Error (MSE) for calibration parameters.
33   */
34  public class ExhaustiveGyroscopeIntervalDetectorThresholdFactorOptimizer extends
35          GyroscopeIntervalDetectorThresholdFactorOptimizer {
36  
37      /**
38       * Default step value to make exhaustive search of threshold factor values.
39       */
40      public static final double DEFAULT_STEP = 1.0;
41  
42      /**
43       * Step to make exhaustive search of threshold factor values between
44       * {@link #getMaxThresholdFactor()} and {@link #getMaxThresholdFactor()}.
45       */
46      private double thresholdFactorStep = DEFAULT_STEP;
47  
48      /**
49       * Constructor.
50       */
51      public ExhaustiveGyroscopeIntervalDetectorThresholdFactorOptimizer() {
52          super();
53      }
54  
55      /**
56       * Constructor.
57       *
58       * @param dataSource instance in charge of retrieving data for this optimizer.
59       */
60      public ExhaustiveGyroscopeIntervalDetectorThresholdFactorOptimizer(
61              final GyroscopeIntervalDetectorThresholdFactorOptimizerDataSource dataSource) {
62          super(dataSource);
63      }
64  
65      /**
66       * Constructor.
67       *
68       * @param calibrator a gyroscope calibrator to be used to optimize its
69       *                   Mean Square Error (MSE).
70       * @throws IllegalArgumentException if gyroscope calibrator does not use
71       *                                  {@link BodyKinematicsSequence} sequences.
72       */
73      public ExhaustiveGyroscopeIntervalDetectorThresholdFactorOptimizer(final GyroscopeNonLinearCalibrator calibrator) {
74          super(calibrator);
75      }
76  
77      /**
78       * Constructor.
79       *
80       * @param dataSource instance in charge of retrieving data for this optimizer.
81       * @param calibrator a gyroscope calibrator to be used to optimize its
82       *                   Mean Square Error (MSE).
83       * @throws IllegalArgumentException if gyroscope calibrator does not use
84       *                                  {@link BodyKinematicsSequence} sequences.
85       */
86      public ExhaustiveGyroscopeIntervalDetectorThresholdFactorOptimizer(
87              final GyroscopeIntervalDetectorThresholdFactorOptimizerDataSource dataSource,
88              final GyroscopeNonLinearCalibrator calibrator) {
89          super(dataSource, calibrator);
90      }
91  
92      /**
93       * Gets the step to make exhaustive search of threshold values between
94       * {@link #getMaxThresholdFactor()} and {@link #getMaxThresholdFactor()}.
95       *
96       * @return step to make exhaustive search of threshold values.
97       */
98      public double getThresholdFactorStep() {
99          return thresholdFactorStep;
100     }
101 
102     /**
103      * Sets step to make exhaustive search of threshold values between
104      * {@link #getMaxThresholdFactor()} and {@link #getMaxThresholdFactor()}.
105      *
106      * @param thresholdStep step to make exhaustive search of threshold values.
107      * @throws LockedException          if optimizer is already running.
108      * @throws IllegalArgumentException if provided value is zero or negative.
109      */
110     public void setThresholdFactorStep(final double thresholdStep) throws LockedException {
111         if (running) {
112             throw new LockedException();
113         }
114         if (thresholdStep <= 0.0) {
115             throw new IllegalArgumentException();
116         }
117 
118         thresholdFactorStep = thresholdStep;
119     }
120 
121     /**
122      * Optimizes the threshold factor for a static interval detector or measurement
123      * generator to minimize MSE (Minimum Squared Error) of estimated
124      * calibration parameters.
125      *
126      * @return optimized threshold factor.
127      * @throws NotReadyException                                 if this optimizer is not ready to start optimization.
128      * @throws LockedException                                   if optimizer is already running.
129      * @throws IntervalDetectorThresholdFactorOptimizerException if optimization fails for
130      *                                                           some reason.
131      */
132     @Override
133     public double optimize() throws NotReadyException, LockedException,
134             IntervalDetectorThresholdFactorOptimizerException {
135         if (running) {
136             throw new LockedException();
137         }
138 
139         if (!isReady()) {
140             throw new NotReadyException();
141         }
142 
143         var hasResult = false;
144         minMse = Double.MAX_VALUE;
145         try {
146             running = true;
147 
148             initProgress();
149             final var progressStep = (float) (thresholdFactorStep
150                     / (thresholdFactorStep + maxThresholdFactor - minThresholdFactor));
151 
152             if (listener != null) {
153                 listener.onOptimizeStart(this);
154             }
155 
156             for (var thresholdFactor = minThresholdFactor;
157                  thresholdFactor <= maxThresholdFactor;
158                  thresholdFactor += thresholdFactorStep) {
159                 try {
160                     evaluateForThresholdFactor(thresholdFactor);
161                     hasResult = true;
162                 } catch (final Exception ignore) {
163                     // when an error occurs, skip to the next iteration
164                 }
165 
166                 progress += progressStep;
167                 checkAndNotifyProgress();
168             }
169 
170             if (!hasResult) {
171                 throw new IntervalDetectorThresholdFactorOptimizerException();
172             }
173 
174             if (listener != null) {
175                 listener.onOptimizeEnd(this);
176             }
177 
178         } finally {
179             running = false;
180         }
181 
182         return optimalThresholdFactor;
183     }
184 }