AltBeaconUtils.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;

/**
 * Utility class to handle Android's AltBeacon library coefficients used te determine distance for calibrated devices.
 * AltBeacon uses the following formula to estimate Beacon's distance:
 * d = coefficient1 * ratio^coefficient2 + coefficient3
 * <p>
 * Considered that radio signal propagation follows equation:
 * Pr = Pt * k / d^n,
 * where:
 * - Pr is received signal strength (expressed in mW),
 * - Pt is transmitted signal strength (expressed in mW),
 * - k is equal to k = c / (4 * pi * f), where:
 * - c is speed of light (which is approximately 9*10^8 m/s)
 * - f is the radio link frequency used by the beacon.
 * <p>
 * Hence, considering:
 * ratio = Pr / Pt
 * <p>
 * which is equal to:
 * ratio = k / d^n
 * <p>
 * And isolating d, whe obtain that:
 * d^n = k / ratio
 * <p>
 * d = (k / ratio)^(1/n)
 * d = k^(1/n)*ratio^(-1/n)
 * <p>
 * And thus we can see from this last expression that:
 * coefficient1 = k^(1/n) = (c / (4 * pi * f))^(1/n)
 * coefficient2 = -1/n, where n is the path loss exponent and depends on the
 * environment where the device is used (typically on free space n is equal to
 * 2.0, but can usually range from 1.6 to 1.8 on indoor environments)
 * coefficient3 is a bias term calibrated for each device.
 */
public class AltBeaconUtils {

    /**
     * Speed of light expressed in meters per second (m/s).
     */
    public static final double SPEED_OF_LIGHT = 299792458.0;

    /**
     * Constructor to prevent instantiation of utility class.
     */
    private AltBeaconUtils() {
    }

    /**
     * Gets k value using provided frequency.
     * K is defined as k = c / (4 * pi * f), where:
     * - c is the speed of light expressed in m/s.
     * - f is the frequency expressed in Hz.
     *
     * @param frequency a frequency (expressed in Hz).
     * @return k value.
     */
    public static double getK(final double frequency) {
        //k = c / (4 * pi * f)
        return SPEED_OF_LIGHT / (4.0 * Math.PI * frequency);
    }

    /**
     * Gets k value using AltBeacon's coefficients.
     *
     * @param coefficient1 1st coefficient.
     * @param coefficient2 2nd coefficient.
     * @return k value.
     */
    public static double getK(final double coefficient1, final double coefficient2) {
        //coefficient1 = k^(1/n), hence
        //k = coefficient1^n
        final var n = -1.0 / coefficient2;
        return Math.pow(coefficient1, n);
    }

    /**
     * Gets frequency from k value.
     *
     * @param k k value.
     * @return frequency expressed in Hz.
     */
    public static double getFrequency(final double k) {
        //k = c / (4*pi*f) --> f = c / (4*pi*k)
        return SPEED_OF_LIGHT / (4.0 * Math.PI * k);
    }

    /**
     * Gets frequency using AltBeacon's coefficients.
     *
     * @param coefficient1 1st coefficient.
     * @param coefficient2 2nd coefficient.
     * @return frequency expressed in Hz.
     */
    public static double getFrequency(final double coefficient1, final double coefficient2) {
        return getFrequency(getK(coefficient1, coefficient2));
    }

    /**
     * Gets path loss exponent using AltBeacon's coefficient.
     *
     * @param coefficient2 2nd coefficient.
     * @return path loss exponent.
     */
    public static double getPathLossExponent(final double coefficient2) {
        //coefficient2 = -1/n
        return -1.0 / coefficient2;
    }

    /**
     * Gets distance using AltBeacon's coefficients.
     *
     * @param coefficient1 1st coefficient.
     * @param coefficient2 2nd coefficient.
     * @param coefficient3 3rd coefficient.
     * @param ratio        ratio between received power and transmitted power.
     * @return distance expressed in meters.
     */
    public static double getDistance(
            final double coefficient1, final double coefficient2, final double coefficient3, final double ratio) {
        //d = coefficient1 * ratio^coefficient2 + coefficient3
        return coefficient1 * Math.pow(ratio, coefficient2) + coefficient3;
    }

    /**
     * Gets distance using AltBeacon's coefficients.
     *
     * @param coefficient1     1st coefficient.
     * @param coefficient2     2nd coefficient.
     * @param coefficient3     3rd coefficient.
     * @param receivedPower    received power (expressed in mW).
     * @param transmittedPower transmitted power (expressed in mW).
     * @return distance expressed in meters.
     */
    public static double getDistance(
            final double coefficient1, final double coefficient2, final double coefficient3, final double receivedPower,
            final double transmittedPower) {
        return getDistance(coefficient1, coefficient2, coefficient3, receivedPower / transmittedPower);
    }

    /**
     * Gets power ratio between received power and transmitted power
     * using AltBeacon's coefficients.
     *
     * @param coefficient1 1st coefficient.
     * @param coefficient2 2nd coefficient.
     * @param coefficient3 3rd coefficient.
     * @param distance     distance expressed in meters.
     * @return power ratio.
     */
    public static double getRatio(
            final double coefficient1, final double coefficient2, final double coefficient3, final double distance) {
        //d = coefficient1 * ratio^coefficient2 + coefficient3
        //d - coefficient3 = coefficient1 * ratio^coefficient2
        //(d - coefficient3) / coefficient1 = ratio^coefficient2
        //ratio = ((d - coefficient3) / coefficient1)^(1/coefficient2)
        return Math.pow((distance - coefficient3) / coefficient1, 1.0 / coefficient2);
    }

    /**
     * Gets power ratio between received power and transmitted power.
     *
     * @param receivedPower    received power (expressed in mW).
     * @param transmittedPower transmitted power (expressed in mW).
     * @return power ratio.
     */
    public static double getRatio(final double receivedPower, final double transmittedPower) {
        return receivedPower / transmittedPower;
    }

    /**
     * Gets received power.
     *
     * @param ratio            ratio between received power and transmitted power.
     * @param transmittedPower transmitted power (expressed in mW).
     * @return received power (expressed in mW).
     */
    public static double getReceivedPower(final double ratio, final double transmittedPower) {
        //ratio = Pr / Pt
        return ratio * transmittedPower;
    }

    /**
     * Gets received power using AltBeacon's coefficients.
     *
     * @param coefficient1     1st coefficient.
     * @param coefficient2     2nd coefficient.
     * @param coefficient3     3rd coefficient.
     * @param distance         distance expressed in meters.
     * @param transmittedPower transmitted power expressed in mW.
     * @return received power expressed in mW.
     */
    public static double getReceivedPower(
            final double coefficient1, final double coefficient2, final double coefficient3, final double distance,
            final double transmittedPower) {
        return getReceivedPower(getRatio(coefficient1, coefficient2, coefficient3, distance), transmittedPower);
    }

    /**
     * Gets transmitted power.
     *
     * @param ratio         ratio between received power and transmitted power.
     * @param receivedPower received power expressed in mW.
     * @return transmitted power expressed in mW.
     */
    public static double getTransmittedPower(final double ratio, final double receivedPower) {
        //ratio = Pr / Pt
        return receivedPower / ratio;
    }

    /**
     * Gets transmitted power using AltBeacon's coefficients.
     *
     * @param coefficient1  1st coefficient.
     * @param coefficient2  2nd coefficient.
     * @param coefficient3  3rd coefficient.
     * @param distance      distance expressed in meters.
     * @param receivedPower received power expressed in mW.
     * @return transmitted power expressed in mW.
     */
    public static double getTransmittedPower(
            final double coefficient1, final double coefficient2, final double coefficient3, final double distance,
            final double receivedPower) {
        return getTransmittedPower(getRatio(coefficient1, coefficient2, coefficient3, distance), receivedPower);
    }

    /**
     * Gets AltBeacon's 1st coefficient.
     *
     * @param k                k value.
     * @param pathLossExponent path loss exponent.
     * @return AltBeacon's 1st coefficient.
     */
    public static double getCoefficient1(final double k, final double pathLossExponent) {
        //coefficient1 = k^(1/n)
        return Math.pow(k, 1.0 / pathLossExponent);
    }

    /**
     * Gets AltBeacon's 1st coefficient.
     *
     * @param frequency        frequency expressed in Hz.
     * @param pathLossExponent path loss exponent.
     * @return AltBeacon's 1st coefficient.
     */
    public static double getCoefficient1WithFrequency(final double frequency, final double pathLossExponent) {
        return getCoefficient1(getK(frequency), pathLossExponent);
    }

    /**
     * Gets AltBeacon's 2nd coefficient.
     *
     * @param pathLossExponent path loss exponent.
     * @return AltBeacon's 2nd coefficient.
     */
    public static double getCoefficient2(final double pathLossExponent) {
        //coefficient2 = -1/n
        return -1.0 / pathLossExponent;
    }
}