IntervalDetectorThresholdFactorOptimizer.java

/*
 * Copyright (C) 2021 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.irurueta.navigation.inertial.calibration.intervals.thresholdfactor;

import com.irurueta.navigation.LockedException;
import com.irurueta.navigation.NotReadyException;

/**
 * Optimizes the threshold factor for interval detection of accelerometer data based
 * on results of accelerometers, gyroscopes or magnetometers calibration.
 *
 * @param <T> type of data to be used as input for this optimizer.
 * @param <S> type of data source for this optimizer.
 */
public abstract class IntervalDetectorThresholdFactorOptimizer<T,
        S extends IntervalDetectorThresholdFactorOptimizerDataSource<T>> {

    /**
     * Default amount of progress variation before notifying a change in optimization progress.
     * By default, this is set to 5%.
     */
    public static final float DEFAULT_PROGRESS_DELTA = 0.05f;

    /**
     * Minimum allowed value for progress delta.
     */
    public static final float MIN_PROGRESS_DELTA = 0.0f;

    /**
     * Maximum allowed value for progress delta.
     */
    public static final float MAX_PROGRESS_DELTA = 1.0f;

    /**
     * Retrieves data for this optimizer.
     */
    protected S dataSource;

    /**
     * Indicates whether this optimizer is running or not.
     */
    protected boolean running;

    /**
     * Minimum Mean Square Error that has been found.
     */
    protected double minMse;

    /**
     * Optimal threshold factor that has been found.
     */
    protected double optimalThresholdFactor;

    /**
     * Listener that notifies events generated by this optimizer.
     */
    protected IntervalDetectorThresholdFactorOptimizerListener<T, S> listener;

    /**
     * Amount of progress variation before notifying a progress change during optimization.
     */
    protected float progressDelta = DEFAULT_PROGRESS_DELTA;

    /**
     * Current progress of optimization.
     */
    protected float progress;

    /**
     * Previously notified progress of optimization.
     */
    protected float previousProgress;

    /**
     * Constructor.
     */
    protected IntervalDetectorThresholdFactorOptimizer() {
    }

    /**
     * Constructor.
     *
     * @param dataSource instance in charge of retrieving data for this optimizer.
     */
    protected IntervalDetectorThresholdFactorOptimizer(final S dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * Gets instance in charge of retrieving data for this optimizer.
     *
     * @return instance in charge of retrieving data for this optimizer.
     */
    public S getDataSource() {
        return dataSource;
    }

    /**
     * Sets an instance in charge of retrieving data for this optimizer.
     *
     * @param dataSource instance in charge of retrieving data for this optimizer.
     * @throws LockedException if optimizer is already running.
     */
    public void setDataSource(final S dataSource) throws LockedException {
        if (running) {
            throw new LockedException();
        }

        this.dataSource = dataSource;
    }

    /**
     * Gets a listener that notifies events generated by this optimizer.
     *
     * @return listener that notifies events generated by this optimizer.
     */
    public IntervalDetectorThresholdFactorOptimizerListener<T, S> getListener() {
        return listener;
    }

    /**
     * Sets a listener that notifies events generated by this optimizer.
     *
     * @param listener listener that notifies events generated by this optimizer.
     * @throws LockedException if optimizer is already running.
     */
    public void setListener(final IntervalDetectorThresholdFactorOptimizerListener<T, S> listener)
            throws LockedException {
        if (running) {
            throw new LockedException();
        }

        this.listener = listener;
    }

    /**
     * Returns the amount of progress variation before notifying a progress change during
     * optimization.
     *
     * @return amount of progress variation before notifying a progress change during
     * optimization.
     */
    public float getProgressDelta() {
        return progressDelta;
    }

    /**
     * Sets the amount of progress variation before notifying a progress change during
     * optimization.
     *
     * @param progressDelta amount of progress variation before notifying a progress
     *                      change during optimization.
     * @throws IllegalArgumentException if the progress delta is less than zero or greater than 1.
     * @throws LockedException          if optimizer is currently running.
     */
    public void setProgressDelta(final float progressDelta) throws LockedException {
        if (running) {
            throw new LockedException();
        }
        if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
            throw new IllegalArgumentException();
        }
        this.progressDelta = progressDelta;
    }

    /**
     * Indicates whether this optimizer is busy optimizing a threshold factor.
     *
     * @return true if optimizer is busy, false otherwise.
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * Indicates whether this optimizer is ready to start optimization.
     *
     * @return true if this optimizer is ready, false otherwise.
     */
    public boolean isReady() {
        return dataSource != null;
    }

    /**
     * Gets minimum Mean Square Error that has been found for calibration.
     *
     * @return minimum Mean Square Error that has been found.
     */
    public double getMinMse() {
        return minMse;
    }

    /**
     * Gets the optimal threshold factor that has been found.
     *
     * @return optimal threshold factor that has been found.
     */
    public double getOptimalThresholdFactor() {
        return optimalThresholdFactor;
    }

    /**
     * Optimizes the threshold factor for a static interval detector or measurement
     * generator to minimize MSE (Minimum Squared Error) of estimated
     * calibration parameters.
     *
     * @return optimized threshold factor.
     * @throws NotReadyException                                 if this optimizer is not
     *                                                           ready to start optimization.
     * @throws LockedException                                   if optimizer is already
     *                                                           running.
     * @throws IntervalDetectorThresholdFactorOptimizerException if optimization fails for
     *                                                           some reason.
     */
    public abstract double optimize() throws NotReadyException, LockedException,
            IntervalDetectorThresholdFactorOptimizerException;

    /**
     * Checks current progress and notifies if progress has changed significantly.
     */
    protected void checkAndNotifyProgress() {
        if (listener != null && (progress - previousProgress > progressDelta)) {
            previousProgress = progress;
            listener.onOptimizeProgressChange(this, Math.min(progress, 1.0f));
        }
    }

    /**
     * Initializes progress values.
     */
    protected void initProgress() {
        previousProgress = 0.0f;
        progress = 0.0f;
    }
}