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