RadioSourceEstimator.java

/*
 * Copyright (C) 2018 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.indoor.radiosource;

import com.irurueta.algebra.Matrix;
import com.irurueta.geometry.Point;
import com.irurueta.navigation.LockedException;
import com.irurueta.navigation.NotReadyException;
import com.irurueta.navigation.indoor.RadioSourceLocated;
import com.irurueta.navigation.indoor.ReadingLocated;

import java.util.List;

/**
 * Estimates a radio source. Usually this implies at least the estimation of
 * the radio source location, however, implementations of this class might
 * estimate additional parameters.
 *
 * @param <P> a {@link Point} type.
 * @param <R> a {@link ReadingLocated} type.
 * @param <L> a {@link RadioSourceEstimatorListener} type.
 */
public abstract class RadioSourceEstimator<P extends Point<?>, R extends ReadingLocated<P>,
        L extends RadioSourceEstimatorListener<? extends RadioSourceEstimator<?, ?, ?>>> {

    /**
     * Estimated position.
     */
    protected double[] estimatedPositionCoordinates;

    /**
     * Covariance of estimated parameters (position, transmitted power and path-loss exponent).
     * Size of this matrix will depend on which parameters estimation is enabled.
     */
    protected Matrix estimatedCovariance;

    /**
     * Covariance of estimated position.
     * Size of this matrix will depend on the number of dimensions
     * of estimated position (either 2 or 3).
     * This value will only be available when position estimation is enabled.
     */
    protected Matrix estimatedPositionCovariance;


    /**
     * Located signal readings belonging to the same radio source to be estimated.
     */
    protected List<? extends R> readings;

    /**
     * Indicates whether estimator is locked during estimation.
     */
    protected boolean locked;

    /**
     * Listener in charge of attending events raised by this instance.
     */
    protected L listener;

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

    /**
     * Constructor.
     * Sets located radio signal readings belonging to the same radio source.
     *
     * @param readings radio signal readings belonging to the same
     *                 radio source.
     * @throws IllegalArgumentException if readings are not valid.
     */
    protected RadioSourceEstimator(final List<? extends R> readings) {
        internalSetReadings(readings);
    }

    /**
     * Constructor.
     *
     * @param listener listener in charge of attending events raised by this instance.
     */
    protected RadioSourceEstimator(final L listener) {
        this.listener = listener;
    }

    /**
     * Constructor.
     * Sets radio signal readings belonging to the same radio source.
     *
     * @param readings radio signal readings belonging to the same radio source.
     * @param listener listener in charge of attending events raised by this instance.
     * @throws IllegalArgumentException if fingerprints are not valid.
     */
    protected RadioSourceEstimator(final List<? extends R> readings, final L listener) {
        this(readings);
        this.listener = listener;
    }

    /**
     * Indicates whether estimator is locked during estimation.
     *
     * @return true if estimator is locked, false otherwise.
     */
    public boolean isLocked() {
        return locked;
    }

    /**
     * Gets radio signal readings belonging to the same radio source to be estimated.
     *
     * @return radio signal readings belonging to the same radio source.
     */
    public List<R> getReadings() {
        //noinspection unchecked
        return (List<R>) readings;
    }

    /**
     * Sets radio signal readings belonging to the same radio source.
     *
     * @param readings Wi-Fi signal readings belonging to the same radio source.
     * @throws LockedException          if estimator is locked.
     * @throws IllegalArgumentException if readings are not valid.
     */
    public void setReadings(final List<? extends R> readings) throws LockedException {
        if (isLocked()) {
            throw new LockedException();
        }

        internalSetReadings(readings);
    }

    /**
     * Gets listener in charge of attending events raised by this instance.
     *
     * @return listener in charge of attending events raised by this instance.
     */
    public L getListener() {
        return listener;
    }

    /**
     * Sets listener in charge of attending events raised by this instance.
     *
     * @param listener listener in charge of attending events raised by this
     *                 instance.
     * @throws LockedException if estimator is locked.
     */
    public void setListener(final L listener) throws LockedException {
        if (isLocked()) {
            throw new LockedException();
        }

        this.listener = listener;
    }

    /**
     * Indicates whether readings are valid or not.
     * Readings are considered valid when there are enough readings.
     *
     * @param readings readings to be validated.
     * @return true if readings are valid, false otherwise.
     */
    public boolean areValidReadings(final List<? extends R> readings) {
        return readings != null && readings.size() >= getMinReadings();
    }

    /**
     * Gets estimated inhomogeneous position coordinates.
     *
     * @return estimated inhomogeneous position coordinates.
     */
    public double[] getEstimatedPositionCoordinates() {
        return estimatedPositionCoordinates;
    }

    /**
     * Gets estimated position and stores result into provided instance.
     *
     * @param estimatedPosition instance where estimated position will be stored.
     */
    public void getEstimatedPosition(final P estimatedPosition) {
        if (estimatedPositionCoordinates != null) {
            for (int i = 0; i < estimatedPositionCoordinates.length; i++) {
                estimatedPosition.setInhomogeneousCoordinate(i, estimatedPositionCoordinates[i]);
            }
        }
    }

    /**
     * Gets covariance for estimated position and power.
     * Matrix contains information in the following order:
     * Top-left sub-matrix contains covariance of position,
     * then follows transmitted power variance, and finally
     * the last element contains path-loss exponent variance.
     *
     * @return covariance for estimated parameters.
     */
    public Matrix getEstimatedCovariance() {
        return estimatedCovariance;
    }

    /**
     * Gets estimated position covariance.
     * Size of this matrix will depend on the number of dimensions
     * of estimated position (either 2 or 3).
     * This value will only be available when position estimation is enabled.
     *
     * @return estimated position covariance or null.
     */
    public Matrix getEstimatedPositionCovariance() {
        return estimatedPositionCovariance;
    }

    /**
     * Indicates whether this instance is ready to start the estimation.
     *
     * @return true if this instance is ready, false otherwise.
     */
    public abstract boolean isReady();

    /**
     * Gets minimum required number of readings to estimate
     * power, position and path-loss exponent.
     * This value depends on the number of parameters to
     * be estimated, but for position only, this is 3
     * readings for 2D, and 4 readings for 3D.
     *
     * @return minimum required number of readings.
     */
    public abstract int getMinReadings();

    /**
     * Gets number of dimensions of position points.
     *
     * @return number of dimensions of position points.
     */
    public abstract int getNumberOfDimensions();

    /**
     * Gets estimated radio source position.
     *
     * @return estimated radio source position.
     */
    public abstract P getEstimatedPosition();

    /**
     * Gets estimated located radio source.
     *
     * @param <S> type of located radio source.
     * @return estimated located radio source.
     */
    public abstract <S extends RadioSourceLocated<P>> S getEstimatedRadioSource();

    /**
     * Estimate radio source.
     *
     * @throws RadioSourceEstimationException if estimation fails.
     * @throws NotReadyException              if estimator is not ready.
     * @throws LockedException                if estimator is locked.
     */
    public abstract void estimate() throws RadioSourceEstimationException, NotReadyException, LockedException;

    /**
     * Internally sets radio signal readings belonging to the same radio source.
     *
     * @param readings radio signal readings belonging to the same radio source.
     * @throws IllegalArgumentException if readings are null or not enough readings
     *                                  are available.
     */
    protected void internalSetReadings(final List<? extends R> readings) {
        if (!areValidReadings(readings)) {
            throw new IllegalArgumentException();
        }

        this.readings = readings;
    }
}