FileDocCategorySizeDatePackage
GpsMeasurement.javaAPI DocAndroid 5.1 API45848Thu Mar 12 22:22:30 GMT 2015android.location

GpsMeasurement.java

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 android.location;

import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

/**
 * A class representing a GPS satellite measurement, containing raw and computed information.
 *
 * @hide
 */
@SystemApi
public class GpsMeasurement implements Parcelable {
    private static final String TAG = "GpsMeasurement";

    private int mFlags;
    private byte mPrn;
    private double mTimeOffsetInNs;
    private short mState;
    private long mReceivedGpsTowInNs;
    private long mReceivedGpsTowUncertaintyInNs;
    private double mCn0InDbHz;
    private double mPseudorangeRateInMetersPerSec;
    private double mPseudorangeRateUncertaintyInMetersPerSec;
    private short mAccumulatedDeltaRangeState;
    private double mAccumulatedDeltaRangeInMeters;
    private double mAccumulatedDeltaRangeUncertaintyInMeters;
    private double mPseudorangeInMeters;
    private double mPseudorangeUncertaintyInMeters;
    private double mCodePhaseInChips;
    private double mCodePhaseUncertaintyInChips;
    private float mCarrierFrequencyInHz;
    private long mCarrierCycles;
    private double mCarrierPhase;
    private double mCarrierPhaseUncertainty;
    private byte mLossOfLock;
    private int mBitNumber;
    private short mTimeFromLastBitInMs;
    private double mDopplerShiftInHz;
    private double mDopplerShiftUncertaintyInHz;
    private byte mMultipathIndicator;
    private double mSnrInDb;
    private double mElevationInDeg;
    private double mElevationUncertaintyInDeg;
    private double mAzimuthInDeg;
    private double mAzimuthUncertaintyInDeg;
    private boolean mUsedInFix;

    // The following enumerations must be in sync with the values declared in gps.h

    private static final int HAS_NO_FLAGS = 0;
    private static final int HAS_SNR = (1<<0);
    private static final int HAS_ELEVATION = (1<<1);
    private static final int HAS_ELEVATION_UNCERTAINTY = (1<<2);
    private static final int HAS_AZIMUTH = (1<<3);
    private static final int HAS_AZIMUTH_UNCERTAINTY = (1<<4);
    private static final int HAS_PSEUDORANGE = (1<<5);
    private static final int HAS_PSEUDORANGE_UNCERTAINTY = (1<<6);
    private static final int HAS_CODE_PHASE = (1<<7);
    private static final int HAS_CODE_PHASE_UNCERTAINTY = (1<<8);
    private static final int HAS_CARRIER_FREQUENCY = (1<<9);
    private static final int HAS_CARRIER_CYCLES = (1<<10);
    private static final int HAS_CARRIER_PHASE = (1<<11);
    private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
    private static final int HAS_BIT_NUMBER = (1<<13);
    private static final int HAS_TIME_FROM_LAST_BIT = (1<<14);
    private static final int HAS_DOPPLER_SHIFT = (1<<15);
    private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16);

    /**
     * The indicator is not available or it is unknown.
     */
    public static final byte LOSS_OF_LOCK_UNKNOWN = 0;

    /**
     * The measurement does not present any indication of 'loss of lock'.
     */
    public static final byte LOSS_OF_LOCK_OK = 1;

    /**
     * 'Loss of lock' detected between the previous and current observation: cycle slip possible.
     */
    public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2;

    /**
     * The indicator is not available or it is unknown.
     */
    public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0;

    /**
     * The measurement has been indicated to use multi-path.
     */
    public static final byte MULTIPATH_INDICATOR_DETECTED = 1;

    /**
     * The measurement has been indicated not tu use multi-path.
     */
    public static final byte MULTIPATH_INDICATOR_NOT_USED = 2;

    /**
     * The state of GPS receiver the measurement is invalid or unknown.
     */
    public static final short STATE_UNKNOWN = 0;

    /**
     * The state of the GPS receiver is ranging code lock.
     */
    public static final short STATE_CODE_LOCK = (1<<0);

    /**
     * The state of the GPS receiver is in bit sync.
     */
    public static final short STATE_BIT_SYNC = (1<<1);

    /**
     *The state of the GPS receiver is in sub-frame sync.
     */
    public static final short STATE_SUBFRAME_SYNC = (1<<2);

    /**
     * The state of the GPS receiver has TOW decoded.
     */
    public static final short STATE_TOW_DECODED = (1<<3);

    /**
     * The state of the 'Accumulated Delta Range' is invalid or unknown.
     */
    public static final short ADR_STATE_UNKNOWN = 0;

    /**
     * The state of the 'Accumulated Delta Range' is valid.
     */
    public static final short ADR_STATE_VALID = (1<<0);

    /**
     * The state of the 'Accumulated Delta Range' has detected a reset.
     */
    public static final short ADR_STATE_RESET = (1<<1);

    /**
     * The state of the 'Accumulated Delta Range' has a cycle slip detected.
     */
    public static final short ADR_STATE_CYCLE_SLIP = (1<<2);

    // End enumerations in sync with gps.h

    GpsMeasurement() {
        initialize();
    }

    /**
     * Sets all contents to the values stored in the provided object.
     */
    public void set(GpsMeasurement measurement) {
        mFlags = measurement.mFlags;
        mPrn = measurement.mPrn;
        mTimeOffsetInNs = measurement.mTimeOffsetInNs;
        mState = measurement.mState;
        mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
        mReceivedGpsTowUncertaintyInNs = measurement.mReceivedGpsTowUncertaintyInNs;
        mCn0InDbHz = measurement.mCn0InDbHz;
        mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec;
        mPseudorangeRateUncertaintyInMetersPerSec =
                measurement.mPseudorangeRateUncertaintyInMetersPerSec;
        mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState;
        mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters;
        mAccumulatedDeltaRangeUncertaintyInMeters =
                measurement.mAccumulatedDeltaRangeUncertaintyInMeters;
        mPseudorangeInMeters = measurement.mPseudorangeInMeters;
        mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters;
        mCodePhaseInChips = measurement.mCodePhaseInChips;
        mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips;
        mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz;
        mCarrierCycles = measurement.mCarrierCycles;
        mCarrierPhase = measurement.mCarrierPhase;
        mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
        mLossOfLock = measurement.mLossOfLock;
        mBitNumber = measurement.mBitNumber;
        mTimeFromLastBitInMs = measurement.mTimeFromLastBitInMs;
        mDopplerShiftInHz = measurement.mDopplerShiftInHz;
        mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz;
        mMultipathIndicator = measurement.mMultipathIndicator;
        mSnrInDb = measurement.mSnrInDb;
        mElevationInDeg = measurement.mElevationInDeg;
        mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg;
        mAzimuthInDeg = measurement.mAzimuthInDeg;
        mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg;
        mUsedInFix = measurement.mUsedInFix;
    }

    /**
     * Resets all the contents to its original state.
     */
    public void reset() {
        initialize();
    }

    /**
     * Gets the Pseudo-random number (PRN).
     * Range: [1, 32]
     */
    public byte getPrn() {
        return mPrn;
    }

    /**
     * Sets the Pseud-random number (PRN).
     */
    public void setPrn(byte value) {
        mPrn = value;
    }

    /**
     * Gets the time offset at which the measurement was taken in nanoseconds.
     * The reference receiver's time is specified by {@link GpsClock#getTimeInNs()} and should be
     * interpreted in the same way as indicated by {@link GpsClock#getType()}.
     *
     * The sign of this value is given by the following equation:
     *      measurement time = time_ns + time_offset_ns
     *
     * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
     * accuracy.
     */
    public double getTimeOffsetInNs() {
        return mTimeOffsetInNs;
    }

    /**
     * Sets the time offset at which the measurement was taken in nanoseconds.
     */
    public void setTimeOffsetInNs(double value) {
        mTimeOffsetInNs = value;
    }

    /**
     * Gets per-satellite sync state.
     * It represents the current sync state for the associated satellite.
     *
     * This value helps interpret {@link #getReceivedGpsTowInNs()}.
     */
    public short getState() {
        return mState;
    }

    /**
     * Sets the sync state.
     */
    public void setState(short value) {
        switch (value) {
            case STATE_UNKNOWN:
            case STATE_BIT_SYNC:
            case STATE_CODE_LOCK:
            case STATE_SUBFRAME_SYNC:
            case STATE_TOW_DECODED:
                mState = value;
                break;
            default:
                Log.d(TAG, "Sanitizing invalid 'sync state': " + value);
                mState = STATE_UNKNOWN;
                break;
        }
    }

    /**
     * Gets a string representation of the 'sync state'.
     * For internal and logging use only.
     */
    private String getStateString() {
        switch (mState) {
            case STATE_UNKNOWN:
                return "Unknown";
            case STATE_BIT_SYNC:
                return "BitSync";
            case STATE_CODE_LOCK:
                return "CodeLock";
            case STATE_SUBFRAME_SYNC:
                return "SubframeSync";
            case STATE_TOW_DECODED:
                return "TowDecoded";
            default:
                return "<Invalid>";
        }
    }

    /**
     * Gets the received GPS Time-of-Week at the measurement time, in nanoseconds.
     * The value is relative to the beginning of the current GPS week.
     *
     * Given {@link #getState()} of the GPS receiver, the range of this field can be:
     *      Searching           : [ 0           ]   : {@link #STATE_UNKNOWN} is set
     *      Ranging code lock   : [ 0    1 ms   ]   : {@link #STATE_CODE_LOCK} is set
     *      Bit sync            : [ 0   20 ms   ]   : {@link #STATE_BIT_SYNC} is set
     *      Subframe sync       : [ 0    6 ms   ]   : {@link #STATE_SUBFRAME_SYNC} is set
     *      TOW decoded         : [ 0    1 week ]   : {@link #STATE_TOW_DECODED} is set
     */
    public long getReceivedGpsTowInNs() {
        return mReceivedGpsTowInNs;
    }

    /**
     * Sets the received GPS time-of-week in nanoseconds.
     */
    public void setReceivedGpsTowInNs(long value) {
        mReceivedGpsTowInNs = value;
    }

    /**
     * Gets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
     */
    public long getReceivedGpsTowUncertaintyInNs() {
        return mReceivedGpsTowUncertaintyInNs;
    }

    /**
     * Sets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
     */
    public void setReceivedGpsTowUncertaintyInNs(long value) {
        mReceivedGpsTowUncertaintyInNs = value;
    }

    /**
     * Gets the Carrier-to-noise density in dB-Hz.
     * Range: [0, 63].
     *
     * The value contains the measured C/N0 for the signal at the antenna input.
     */
    public double getCn0InDbHz() {
        return mCn0InDbHz;
    }

    /**
     * Sets the carrier-to-noise density in dB-Hz.
     */
    public void setCn0InDbHz(double value) {
        mCn0InDbHz = value;
    }

    /**
     * Gets the Pseudorange rate at the timestamp in m/s.
     * The reported value includes {@link #getPseudorangeRateUncertaintyInMetersPerSec()}.
     */
    public double getPseudorangeRateInMetersPerSec() {
        return mPseudorangeRateInMetersPerSec;
    }

    /**
     * Sets the pseudorange rate at the timestamp in m/s.
     */
    public void setPseudorangeRateInMetersPerSec(double value) {
        mPseudorangeRateInMetersPerSec = value;
    }

    /**
     * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
     * The uncertainty is represented as an absolute (single sided) value.
     */
    public double getPseudorangeRateUncertaintyInMetersPerSec() {
        return mPseudorangeRateUncertaintyInMetersPerSec;
    }

    /**
     * Sets the pseudorange's rate uncertainty (1-Sigma) in m/s.
     */
    public void setPseudorangeRateUncertaintyInMetersPerSec(double value) {
        mPseudorangeRateUncertaintyInMetersPerSec = value;
    }

    /**
     * Gets 'Accumulated Delta Range' state.
     * It indicates whether {@link #getAccumulatedDeltaRangeInMeters()} is reset or there is a
     * cycle slip (indicating 'loss of lock').
     */
    public short getAccumulatedDeltaRangeState() {
        return mAccumulatedDeltaRangeState;
    }

    /**
     * Sets the 'Accumulated Delta Range' state.
     */
    public void setAccumulatedDeltaRangeState(short value) {
        switch (value) {
            case ADR_STATE_UNKNOWN:
            case ADR_STATE_VALID:
            case ADR_STATE_RESET:
            case ADR_STATE_CYCLE_SLIP:
                mAccumulatedDeltaRangeState = value;
                break;
            default:
                Log.d(TAG, "Sanitizing invalid 'Accumulated Delta Range state': " + value);
                mAccumulatedDeltaRangeState = ADR_STATE_UNKNOWN;
                break;
        }
    }

    /**
     * Gets a string representation of the 'Accumulated Delta Range state'.
     * For internal and logging use only.
     */
    private String getAccumulatedDeltaRangeStateString() {
        switch (mAccumulatedDeltaRangeState) {
            case ADR_STATE_UNKNOWN:
                return "Unknown";
            case ADR_STATE_VALID:
                return "Valid";
            case ADR_STATE_RESET:
                return "Reset";
            case ADR_STATE_CYCLE_SLIP:
                return "CycleSlip";
            default:
                return "<Invalid>";
        }
    }

    /**
     * Gets the accumulated delta range since the last channel reset, in meters.
     * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}.
     *
     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
     */
    public double getAccumulatedDeltaRangeInMeters() {
        return mAccumulatedDeltaRangeInMeters;
    }

    /**
     * Sets the accumulated delta range in meters.
     */
    public void setAccumulatedDeltaRangeInMeters(double value) {
        mAccumulatedDeltaRangeInMeters = value;
    }

    /**
     * Gets the accumulated delta range's uncertainty (1-Sigma) in meters.
     * The uncertainty is represented as an absolute (single sided) value.
     */
    public double getAccumulatedDeltaRangeUncertaintyInMeters() {
        return mAccumulatedDeltaRangeUncertaintyInMeters;
    }

    /**
     * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
     *
     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
     */
    public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) {
        mAccumulatedDeltaRangeUncertaintyInMeters = value;
    }

    /**
     * Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise.
     */
    public boolean hasPseudorangeInMeters() {
        return isFlagSet(HAS_PSEUDORANGE);
    }

    /**
     * Gets the best derived pseudorange by the chipset, in meters.
     * The reported pseudorange includes {@link #getPseudorangeUncertaintyInMeters()}.
     *
     * The value is only available if {@link #hasPseudorangeInMeters()} is true.
     */
    public double getPseudorangeInMeters() {
        return mPseudorangeInMeters;
    }

    /**
     * Sets the Pseudo-range in meters.
     */
    public void setPseudorangeInMeters(double value) {
        setFlag(HAS_PSEUDORANGE);
        mPseudorangeInMeters = value;
    }

    /**
     * Resets the Pseudo-range in meters.
     */
    public void resetPseudorangeInMeters() {
        resetFlag(HAS_PSEUDORANGE);
        mPseudorangeInMeters = Double.NaN;
    }

    /**
     * Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise.
     */
    public boolean hasPseudorangeUncertaintyInMeters() {
        return isFlagSet(HAS_PSEUDORANGE_UNCERTAINTY);
    }

    /**
     * Gets the pseudorange's uncertainty (1-Sigma) in meters.
     * The value contains the 'pseudorange' and 'clock' uncertainty in it.
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasPseudorangeUncertaintyInMeters()} is true.
     */
    public double getPseudorangeUncertaintyInMeters() {
        return mPseudorangeUncertaintyInMeters;
    }

    /**
     * Sets the pseudo-range's uncertainty (1-Sigma) in meters.
     */
    public void setPseudorangeUncertaintyInMeters(double value) {
        setFlag(HAS_PSEUDORANGE_UNCERTAINTY);
        mPseudorangeUncertaintyInMeters = value;
    }

    /**
     * Resets the pseudo-range's uncertainty (1-Sigma) in meters.
     */
    public void resetPseudorangeUncertaintyInMeters() {
        resetFlag(HAS_PSEUDORANGE_UNCERTAINTY);
        mPseudorangeUncertaintyInMeters = Double.NaN;
    }

    /**
     * Returns true if {@link #getCodePhaseInChips()} is available, false otherwise.
     */
    public boolean hasCodePhaseInChips() {
        return isFlagSet(HAS_CODE_PHASE);
    }

    /**
     * Gets the fraction of the current C/A code cycle.
     * Range: [0, 1023]
     * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
     * The reported code-phase includes {@link #getCodePhaseUncertaintyInChips()}.
     *
     * The value is only available if {@link #hasCodePhaseInChips()} is true.
     */
    public double getCodePhaseInChips() {
        return mCodePhaseInChips;
    }

    /**
     * Sets the Code-phase in chips.
     */
    public void setCodePhaseInChips(double value) {
        setFlag(HAS_CODE_PHASE);
        mCodePhaseInChips = value;
    }

    /**
     * Resets the Code-phase in chips.
     */
    public void resetCodePhaseInChips() {
        resetFlag(HAS_CODE_PHASE);
        mCodePhaseInChips = Double.NaN;
    }

    /**
     * Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise.
     */
    public boolean hasCodePhaseUncertaintyInChips() {
        return isFlagSet(HAS_CODE_PHASE_UNCERTAINTY);
    }

    /**
     * Gets the code-phase's uncertainty (1-Sigma) as a fraction of chips.
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasCodePhaseUncertaintyInChips()} is true.
     */
    public double getCodePhaseUncertaintyInChips() {
        return mCodePhaseUncertaintyInChips;
    }

    /**
     * Sets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
     */
    public void setCodePhaseUncertaintyInChips(double value) {
        setFlag(HAS_CODE_PHASE_UNCERTAINTY);
        mCodePhaseUncertaintyInChips = value;
    }

    /**
     * Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
     */
    public void resetCodePhaseUncertaintyInChips() {
        resetFlag(HAS_CODE_PHASE_UNCERTAINTY);
        mCodePhaseUncertaintyInChips = Double.NaN;
    }

    /**
     * Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise.
     */
    public boolean hasCarrierFrequencyInHz() {
        return isFlagSet(HAS_CARRIER_FREQUENCY);
    }

    /**
     * Gets the carrier frequency at which codes and messages are modulated, it can be L1 or L2.
     * If the field is not set, the carrier frequency corresponds to L1.
     *
     * The value is only available if {@link #hasCarrierFrequencyInHz()} is true.
     */
    public float getCarrierFrequencyInHz() {
        return mCarrierFrequencyInHz;
    }

    /**
     * Sets the Carrier frequency (L1 or L2) in Hz.
     */
    public void setCarrierFrequencyInHz(float carrierFrequencyInHz) {
        setFlag(HAS_CARRIER_FREQUENCY);
        mCarrierFrequencyInHz = carrierFrequencyInHz;
    }

    /**
     * Resets the Carrier frequency (L1 or L2) in Hz.
     */
    public void resetCarrierFrequencyInHz() {
        resetFlag(HAS_CARRIER_FREQUENCY);
        mCarrierFrequencyInHz = Float.NaN;
    }

    /**
     * Returns true if {@link #getCarrierCycles()} is available, false otherwise.
     */
    public boolean hasCarrierCycles() {
        return isFlagSet(HAS_CARRIER_CYCLES);
    }

    /**
     * The number of full carrier cycles between the satellite and the receiver.
     * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
     *
     * The value is only available if {@link #hasCarrierCycles()} is true.
     */
    public long getCarrierCycles() {
        return mCarrierCycles;
    }

    /**
     * Sets the number of full carrier cycles between the satellite and the receiver.
     */
    public void setCarrierCycles(long value) {
        setFlag(HAS_CARRIER_CYCLES);
        mCarrierCycles = value;
    }

    /**
     * Resets the number of full carrier cycles between the satellite and the receiver.
     */
    public void resetCarrierCycles() {
        resetFlag(HAS_CARRIER_CYCLES);
        mCarrierCycles = Long.MIN_VALUE;
    }

    /**
     * Returns true if {@link #getCarrierPhase()} is available, false otherwise.
     */
    public boolean hasCarrierPhase() {
        return isFlagSet(HAS_CARRIER_PHASE);
    }

    /**
     * Gets the RF phase detected by the receiver.
     * Range: [0.0, 1.0].
     * This is usually the fractional part of the complete carrier phase measurement.
     *
     * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
     * The reported carrier-phase includes {@link #getCarrierPhaseUncertainty()}.
     *
     * The value is only available if {@link #hasCarrierPhase()} is true.
     */
    public double getCarrierPhase() {
        return mCarrierPhase;
    }

    /**
     * Sets the RF phase detected by the receiver.
     */
    public void setCarrierPhase(double value) {
        setFlag(HAS_CARRIER_PHASE);
        mCarrierPhase = value;
    }

    /**
     * Resets the RF phase detected by the receiver.
     */
    public void resetCarrierPhase() {
        resetFlag(HAS_CARRIER_PHASE);
        mCarrierPhase = Double.NaN;
    }

    /**
     * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise.
     */
    public boolean hasCarrierPhaseUncertainty() {
        return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
    }

    /**
     * Gets the carrier-phase's uncertainty (1-Sigma).
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasCarrierPhaseUncertainty()} is true.
     */
    public double getCarrierPhaseUncertainty() {
        return mCarrierPhaseUncertainty;
    }

    /**
     * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
     */
    public void setCarrierPhaseUncertainty(double value) {
        setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
        mCarrierPhaseUncertainty = value;
    }

    /**
     * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
     */
    public void resetCarrierPhaseUncertainty() {
        resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
        mCarrierPhaseUncertainty = Double.NaN;
    }

    /**
     * Gets a value indicating the 'loss of lock' state of the event.
     */
    public byte getLossOfLock() {
        return mLossOfLock;
    }

    /**
     * Sets the 'loss of lock' status.
     */
    public void setLossOfLock(byte value) {
        switch (value) {
            case LOSS_OF_LOCK_UNKNOWN:
            case LOSS_OF_LOCK_OK:
            case LOSS_OF_LOCK_CYCLE_SLIP:
                mLossOfLock = value;
                break;
            default:
                Log.d(TAG, "Sanitizing invalid 'loss of lock': " + value);
                mLossOfLock = LOSS_OF_LOCK_UNKNOWN;
                break;
        }
    }

    /**
     * Gets a string representation of the 'loss of lock'.
     * For internal and logging use only.
     */
    private String getLossOfLockString() {
        switch (mLossOfLock) {
            case LOSS_OF_LOCK_UNKNOWN:
                return "Unknown";
            case LOSS_OF_LOCK_OK:
                return "Ok";
            case LOSS_OF_LOCK_CYCLE_SLIP:
                return "CycleSlip";
            default:
                return "<Invalid>";
        }
    }

    /**
     * Returns true if {@link #getBitNumber()} is available, false otherwise.
     */
    public boolean hasBitNumber() {
        return isFlagSet(HAS_BIT_NUMBER);
    }

    /**
     * Gets the number of GPS bits transmitted since Sat-Sun midnight (GPS week).
     *
     * The value is only available if {@link #hasBitNumber()} is true.
     */
    public int getBitNumber() {
        return mBitNumber;
    }

    /**
     * Sets the bit number within the broadcast frame.
     */
    public void setBitNumber(int bitNumber) {
        setFlag(HAS_BIT_NUMBER);
        mBitNumber = bitNumber;
    }

    /**
     * Resets the bit number within the broadcast frame.
     */
    public void resetBitNumber() {
        resetFlag(HAS_BIT_NUMBER);
        mBitNumber = Integer.MIN_VALUE;
    }

    /**
     * Returns true if {@link #getTimeFromLastBitInMs()} is available, false otherwise.
     */
    public boolean hasTimeFromLastBitInMs() {
        return isFlagSet(HAS_TIME_FROM_LAST_BIT);
    }

    /**
     * Gets the elapsed time since the last received bit in milliseconds.
     * Range: [0, 20].
     *
     * The value is only available if {@link #hasTimeFromLastBitInMs()} is true.
     */
    public short getTimeFromLastBitInMs() {
        return mTimeFromLastBitInMs;
    }

    /**
     * Sets the elapsed time since the last received bit in milliseconds.
     */
    public void setTimeFromLastBitInMs(short value) {
        setFlag(HAS_TIME_FROM_LAST_BIT);
        mTimeFromLastBitInMs = value;
    }

    /**
     * Resets the elapsed time since the last received bit in milliseconds.
     */
    public void resetTimeFromLastBitInMs() {
        resetFlag(HAS_TIME_FROM_LAST_BIT);
        mTimeFromLastBitInMs = Short.MIN_VALUE;
    }

    /**
     * Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise.
     */
    public boolean hasDopplerShiftInHz() {
        return isFlagSet(HAS_DOPPLER_SHIFT);
    }

    /**
     * Gets the Doppler Shift in Hz.
     * A positive value indicates that the SV is moving toward the receiver.
     *
     * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
     * The reported doppler shift includes {@link #getDopplerShiftUncertaintyInHz()}.
     *
     * The value is only available if {@link #hasDopplerShiftInHz()} is true.
     */
    public double getDopplerShiftInHz() {
        return mDopplerShiftInHz;
    }

    /**
     * Sets the Doppler shift in Hz.
     */
    public void setDopplerShiftInHz(double value) {
        setFlag(HAS_DOPPLER_SHIFT);
        mDopplerShiftInHz = value;
    }

    /**
     * Resets the Doppler shift in Hz.
     */
    public void resetDopplerShiftInHz() {
        resetFlag(HAS_DOPPLER_SHIFT);
        mDopplerShiftInHz = Double.NaN;
    }

    /**
     * Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise.
     */
    public boolean hasDopplerShiftUncertaintyInHz() {
        return isFlagSet(HAS_DOPPLER_SHIFT_UNCERTAINTY);
    }

    /**
     * Gets the Doppler's Shift uncertainty (1-Sigma) in Hz.
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasDopplerShiftUncertaintyInHz()} is true.
     */
    public double getDopplerShiftUncertaintyInHz() {
        return mDopplerShiftUncertaintyInHz;
    }

    /**
     * Sets the Doppler's shift uncertainty (1-Sigma) in Hz.
     */
    public void setDopplerShiftUncertaintyInHz(double value) {
        setFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
        mDopplerShiftUncertaintyInHz = value;
    }

    /**
     * Resets the Doppler's shift uncertainty (1-Sigma) in Hz.
     */
    public void resetDopplerShiftUncertaintyInHz() {
        resetFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
        mDopplerShiftUncertaintyInHz = Double.NaN;
    }

    /**
     * Gets a value indicating the 'multipath' state of the event.
     */
    public byte getMultipathIndicator() {
        return mMultipathIndicator;
    }

    /**
     * Sets the 'multi-path' indicator.
     */
    public void setMultipathIndicator(byte value) {
        switch (value) {
            case MULTIPATH_INDICATOR_UNKNOWN:
            case MULTIPATH_INDICATOR_DETECTED:
            case MULTIPATH_INDICATOR_NOT_USED:
                mMultipathIndicator = value;
                break;
            default:
                Log.d(TAG, "Sanitizing invalid 'muti-path indicator': " + value);
                mMultipathIndicator = MULTIPATH_INDICATOR_UNKNOWN;
                break;
        }
    }

    /**
     * Gets a string representation of the 'multi-path indicator'.
     * For internal and logging use only.
     */
    private String getMultipathIndicatorString() {
        switch(mMultipathIndicator) {
            case MULTIPATH_INDICATOR_UNKNOWN:
                return "Unknown";
            case MULTIPATH_INDICATOR_DETECTED:
                return "Detected";
            case MULTIPATH_INDICATOR_NOT_USED:
                return "NotUsed";
            default:
                return "<Invalid>";
        }
    }

    /**
     * Returns true if {@link #getSnrInDb()} is available, false otherwise.
     */
    public boolean hasSnrInDb() {
        return isFlagSet(HAS_SNR);
    }

    /**
     * Gets the Signal-to-Noise ratio (SNR) in dB.
     *
     * The value is only available if {@link #hasSnrInDb()} is true.
     */
    public double getSnrInDb() {
        return mSnrInDb;
    }

    /**
     * Sets the Signal-to-noise ratio (SNR) in dB.
     */
    public void setSnrInDb(double snrInDb) {
        setFlag(HAS_SNR);
        mSnrInDb = snrInDb;
    }

    /**
     * Resets the Signal-to-noise ratio (SNR) in dB.
     */
    public void resetSnrInDb() {
        resetFlag(HAS_SNR);
        mSnrInDb = Double.NaN;
    }

    /**
     * Returns true if {@link #getElevationInDeg()} is available, false otherwise.
     */
    public boolean hasElevationInDeg() {
        return isFlagSet(HAS_ELEVATION);
    }

    /**
     * Gets the Elevation in degrees.
     * Range: [-90, 90]
     * The reported elevation includes {@link #getElevationUncertaintyInDeg()}.
     *
     * The value is only available if {@link #hasElevationInDeg()} is true.
     */
    public double getElevationInDeg() {
        return mElevationInDeg;
    }

    /**
     * Sets the Elevation in degrees.
     */
    public void setElevationInDeg(double elevationInDeg) {
        setFlag(HAS_ELEVATION);
        mElevationInDeg = elevationInDeg;
    }

    /**
     * Resets the Elevation in degrees.
     */
    public void resetElevationInDeg() {
        resetFlag(HAS_ELEVATION);
        mElevationInDeg = Double.NaN;
    }

    /**
     * Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise.
     */
    public boolean hasElevationUncertaintyInDeg() {
        return isFlagSet(HAS_ELEVATION_UNCERTAINTY);
    }

    /**
     * Gets the elevation's uncertainty (1-Sigma) in degrees.
     * Range: [0, 90]
     *
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasElevationUncertaintyInDeg()} is true.
     */
    public double getElevationUncertaintyInDeg() {
        return mElevationUncertaintyInDeg;
    }

    /**
     * Sets the elevation's uncertainty (1-Sigma) in degrees.
     */
    public void setElevationUncertaintyInDeg(double value) {
        setFlag(HAS_ELEVATION_UNCERTAINTY);
        mElevationUncertaintyInDeg = value;
    }

    /**
     * Resets the elevation's uncertainty (1-Sigma) in degrees.
     */
    public void resetElevationUncertaintyInDeg() {
        resetFlag(HAS_ELEVATION_UNCERTAINTY);
        mElevationUncertaintyInDeg = Double.NaN;
    }

    /**
     * Returns true if {@link #getAzimuthInDeg()} is available, false otherwise.
     */
    public boolean hasAzimuthInDeg() {
        return isFlagSet(HAS_AZIMUTH);
    }

    /**
     * Gets the azimuth in degrees.
     * Range: [0, 360).
     *
     * The reported azimuth includes {@link #getAzimuthUncertaintyInDeg()}.
     *
     * The value is only available if {@link #hasAzimuthInDeg()} is true.
     */
    public double getAzimuthInDeg() {
        return mAzimuthInDeg;
    }

    /**
     * Sets the Azimuth in degrees.
     */
    public void setAzimuthInDeg(double value) {
        setFlag(HAS_AZIMUTH);
        mAzimuthInDeg = value;
    }

    /**
     * Resets the Azimuth in degrees.
     */
    public void resetAzimuthInDeg() {
        resetFlag(HAS_AZIMUTH);
        mAzimuthInDeg = Double.NaN;
    }

    /**
     * Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise.
     */
    public boolean hasAzimuthUncertaintyInDeg() {
        return isFlagSet(HAS_AZIMUTH_UNCERTAINTY);
    }

    /**
     * Gets the azimuth's uncertainty (1-Sigma) in degrees.
     * Range: [0, 180].
     *
     * The uncertainty is represented as an absolute (single sided) value.
     *
     * The value is only available if {@link #hasAzimuthUncertaintyInDeg()} is true.
     */
    public double getAzimuthUncertaintyInDeg() {
        return mAzimuthUncertaintyInDeg;
    }

    /**
     * Sets the Azimuth's uncertainty (1-Sigma) in degrees.
     */
    public void setAzimuthUncertaintyInDeg(double value) {
        setFlag(HAS_AZIMUTH_UNCERTAINTY);
        mAzimuthUncertaintyInDeg = value;
    }

    /**
     * Resets the Azimuth's uncertainty (1-Sigma) in degrees.
     */
    public void resetAzimuthUncertaintyInDeg() {
        resetFlag(HAS_AZIMUTH_UNCERTAINTY);
        mAzimuthUncertaintyInDeg = Double.NaN;
    }

    /**
     * Gets a flag indicating whether the GPS represented by the measurement was used for computing
     * the most recent fix.
     *
     * @return A non-null value if the data is available, null otherwise.
     */
    public boolean isUsedInFix() {
        return mUsedInFix;
    }

    /**
     * Sets the Used-in-Fix flag.
     */
    public void setUsedInFix(boolean value) {
        mUsedInFix = value;
    }

    public static final Creator<GpsMeasurement> CREATOR = new Creator<GpsMeasurement>() {
        @Override
        public GpsMeasurement createFromParcel(Parcel parcel) {
            GpsMeasurement gpsMeasurement = new GpsMeasurement();

            gpsMeasurement.mFlags = parcel.readInt();
            gpsMeasurement.mPrn = parcel.readByte();
            gpsMeasurement.mTimeOffsetInNs = parcel.readDouble();
            gpsMeasurement.mState = (short) parcel.readInt();
            gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong();
            gpsMeasurement.mReceivedGpsTowUncertaintyInNs = parcel.readLong();
            gpsMeasurement.mCn0InDbHz = parcel.readDouble();
            gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble();
            gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble();
            gpsMeasurement.mAccumulatedDeltaRangeState = (short) parcel.readInt();
            gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble();
            gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble();
            gpsMeasurement.mPseudorangeInMeters = parcel.readDouble();
            gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble();
            gpsMeasurement.mCodePhaseInChips = parcel.readDouble();
            gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble();
            gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat();
            gpsMeasurement.mCarrierCycles = parcel.readLong();
            gpsMeasurement.mCarrierPhase = parcel.readDouble();
            gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
            gpsMeasurement.mLossOfLock = parcel.readByte();
            gpsMeasurement.mBitNumber = parcel.readInt();
            gpsMeasurement.mTimeFromLastBitInMs = (short) parcel.readInt();
            gpsMeasurement.mDopplerShiftInHz = parcel.readDouble();
            gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble();
            gpsMeasurement.mMultipathIndicator = parcel.readByte();
            gpsMeasurement.mSnrInDb = parcel.readDouble();
            gpsMeasurement.mElevationInDeg = parcel.readDouble();
            gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble();
            gpsMeasurement.mAzimuthInDeg = parcel.readDouble();
            gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble();
            gpsMeasurement.mUsedInFix = parcel.readInt() != 0;

            return gpsMeasurement;
        }

        @Override
        public GpsMeasurement[] newArray(int i) {
            return new GpsMeasurement[i];
        }
    };

    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeInt(mFlags);
        parcel.writeByte(mPrn);
        parcel.writeDouble(mTimeOffsetInNs);
        parcel.writeInt(mState);
        parcel.writeLong(mReceivedGpsTowInNs);
        parcel.writeLong(mReceivedGpsTowUncertaintyInNs);
        parcel.writeDouble(mCn0InDbHz);
        parcel.writeDouble(mPseudorangeRateInMetersPerSec);
        parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec);
        parcel.writeInt(mAccumulatedDeltaRangeState);
        parcel.writeDouble(mAccumulatedDeltaRangeInMeters);
        parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters);
        parcel.writeDouble(mPseudorangeInMeters);
        parcel.writeDouble(mPseudorangeUncertaintyInMeters);
        parcel.writeDouble(mCodePhaseInChips);
        parcel.writeDouble(mCodePhaseUncertaintyInChips);
        parcel.writeFloat(mCarrierFrequencyInHz);
        parcel.writeLong(mCarrierCycles);
        parcel.writeDouble(mCarrierPhase);
        parcel.writeDouble(mCarrierPhaseUncertainty);
        parcel.writeByte(mLossOfLock);
        parcel.writeInt(mBitNumber);
        parcel.writeInt(mTimeFromLastBitInMs);
        parcel.writeDouble(mDopplerShiftInHz);
        parcel.writeDouble(mDopplerShiftUncertaintyInHz);
        parcel.writeByte(mMultipathIndicator);
        parcel.writeDouble(mSnrInDb);
        parcel.writeDouble(mElevationInDeg);
        parcel.writeDouble(mElevationUncertaintyInDeg);
        parcel.writeDouble(mAzimuthInDeg);
        parcel.writeDouble(mAzimuthUncertaintyInDeg);
        parcel.writeInt(mUsedInFix ? 1 : 0);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public String toString() {
        final String format = "   %-29s = %s\n";
        final String formatWithUncertainty = "   %-29s = %-25s   %-40s = %s\n";
        StringBuilder builder = new StringBuilder("GpsMeasurement:\n");

        builder.append(String.format(format, "Prn", mPrn));

        builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));

        builder.append(String.format(format, "State", getStateString()));

        builder.append(String.format(
                formatWithUncertainty,
                "ReceivedGpsTowInNs",
                mReceivedGpsTowInNs,
                "ReceivedGpsTowUncertaintyInNs",
                mReceivedGpsTowUncertaintyInNs));

        builder.append(String.format(format, "Cn0InDbHz", mCn0InDbHz));

        builder.append(String.format(
                formatWithUncertainty,
                "PseudorangeRateInMetersPerSec",
                mPseudorangeRateInMetersPerSec,
                "PseudorangeRateUncertaintyInMetersPerSec",
                mPseudorangeRateUncertaintyInMetersPerSec));

        builder.append(String.format(
                format,
                "AccumulatedDeltaRangeState",
                getAccumulatedDeltaRangeStateString()));

        builder.append(String.format(
                formatWithUncertainty,
                "AccumulatedDeltaRangeInMeters",
                mAccumulatedDeltaRangeInMeters,
                "AccumulatedDeltaRangeUncertaintyInMeters",
                mAccumulatedDeltaRangeUncertaintyInMeters));

        builder.append(String.format(
                formatWithUncertainty,
                "PseudorangeInMeters",
                hasPseudorangeInMeters() ? mPseudorangeInMeters : null,
                "PseudorangeUncertaintyInMeters",
                hasPseudorangeUncertaintyInMeters() ? mPseudorangeUncertaintyInMeters : null));

        builder.append(String.format(
                formatWithUncertainty,
                "CodePhaseInChips",
                hasCodePhaseInChips() ? mCodePhaseInChips : null,
                "CodePhaseUncertaintyInChips",
                hasCodePhaseUncertaintyInChips() ? mCodePhaseUncertaintyInChips : null));

        builder.append(String.format(
                format,
                "CarrierFrequencyInHz",
                hasCarrierFrequencyInHz() ? mCarrierFrequencyInHz : null));

        builder.append(String.format(
                format,
                "CarrierCycles",
                hasCarrierCycles() ? mCarrierCycles : null));

        builder.append(String.format(
                formatWithUncertainty,
                "CarrierPhase",
                hasCarrierPhase() ? mCarrierPhase : null,
                "CarrierPhaseUncertainty",
                hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));

        builder.append(String.format(format, "LossOfLock", getLossOfLockString()));

        builder.append(String.format(
                format,
                "BitNumber",
                hasBitNumber() ? mBitNumber : null));

        builder.append(String.format(
                format,
                "TimeFromLastBitInMs",
                hasTimeFromLastBitInMs() ? mTimeFromLastBitInMs : null));

        builder.append(String.format(
                formatWithUncertainty,
                "DopplerShiftInHz",
                hasDopplerShiftInHz() ? mDopplerShiftInHz : null,
                "DopplerShiftUncertaintyInHz",
                hasDopplerShiftUncertaintyInHz() ? mDopplerShiftUncertaintyInHz : null));

        builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));

        builder.append(String.format(
                format,
                "SnrInDb",
                hasSnrInDb() ? mSnrInDb : null));

        builder.append(String.format(
                formatWithUncertainty,
                "ElevationInDeg",
                hasElevationInDeg() ? mElevationInDeg : null,
                "ElevationUncertaintyInDeg",
                hasElevationUncertaintyInDeg() ? mElevationUncertaintyInDeg : null));

        builder.append(String.format(
                formatWithUncertainty,
                "AzimuthInDeg",
                hasAzimuthInDeg() ? mAzimuthInDeg : null,
                "AzimuthUncertaintyInDeg",
                hasAzimuthUncertaintyInDeg() ? mAzimuthUncertaintyInDeg : null));

        builder.append(String.format(format, "UsedInFix", mUsedInFix));

        return builder.toString();
    }

    private void initialize() {
        mFlags = HAS_NO_FLAGS;
        setPrn(Byte.MIN_VALUE);
        setTimeOffsetInNs(Long.MIN_VALUE);
        setState(STATE_UNKNOWN);
        setReceivedGpsTowInNs(Long.MIN_VALUE);
        setReceivedGpsTowUncertaintyInNs(Long.MAX_VALUE);
        setCn0InDbHz(Double.MIN_VALUE);
        setPseudorangeRateInMetersPerSec(Double.MIN_VALUE);
        setPseudorangeRateUncertaintyInMetersPerSec(Double.MIN_VALUE);
        setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN);
        setAccumulatedDeltaRangeInMeters(Double.MIN_VALUE);
        setAccumulatedDeltaRangeUncertaintyInMeters(Double.MIN_VALUE);
        resetPseudorangeInMeters();
        resetPseudorangeUncertaintyInMeters();
        resetCodePhaseInChips();
        resetCodePhaseUncertaintyInChips();
        resetCarrierFrequencyInHz();
        resetCarrierCycles();
        resetCarrierPhase();
        resetCarrierPhaseUncertainty();
        setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
        resetBitNumber();
        resetTimeFromLastBitInMs();
        resetDopplerShiftInHz();
        resetDopplerShiftUncertaintyInHz();
        setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
        resetSnrInDb();
        resetElevationInDeg();
        resetElevationUncertaintyInDeg();
        resetAzimuthInDeg();
        resetAzimuthUncertaintyInDeg();
        setUsedInFix(false);
    }

    private void setFlag(int flag) {
        mFlags |= flag;
    }

    private void resetFlag(int flag) {
        mFlags &= ~flag;
    }

    private boolean isFlagSet(int flag) {
        return (mFlags & flag) == flag;
    }
}