/* * 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.TestApi; import android.os.Parcel; import android.os.Parcelable; /** * A class containing a GPS clock timestamp. * *

It represents a measurement of the GPS receiver's clock. */ public final class GnssClock implements Parcelable { // 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_LEAP_SECOND = (1<<0); private static final int HAS_TIME_UNCERTAINTY = (1<<1); private static final int HAS_FULL_BIAS = (1<<2); private static final int HAS_BIAS = (1<<3); private static final int HAS_BIAS_UNCERTAINTY = (1<<4); private static final int HAS_DRIFT = (1<<5); private static final int HAS_DRIFT_UNCERTAINTY = (1<<6); // End enumerations in sync with gps.h private int mFlags; private int mLeapSecond; private long mTimeNanos; private double mTimeUncertaintyNanos; private long mFullBiasNanos; private double mBiasNanos; private double mBiasUncertaintyNanos; private double mDriftNanosPerSecond; private double mDriftUncertaintyNanosPerSecond; private int mHardwareClockDiscontinuityCount; /** * @hide */ @TestApi public GnssClock() { initialize(); } /** * Sets all contents to the values stored in the provided object. * @hide */ @TestApi public void set(GnssClock clock) { mFlags = clock.mFlags; mLeapSecond = clock.mLeapSecond; mTimeNanos = clock.mTimeNanos; mTimeUncertaintyNanos = clock.mTimeUncertaintyNanos; mFullBiasNanos = clock.mFullBiasNanos; mBiasNanos = clock.mBiasNanos; mBiasUncertaintyNanos = clock.mBiasUncertaintyNanos; mDriftNanosPerSecond = clock.mDriftNanosPerSecond; mDriftUncertaintyNanosPerSecond = clock.mDriftUncertaintyNanosPerSecond; mHardwareClockDiscontinuityCount = clock.mHardwareClockDiscontinuityCount; } /** * Resets all the contents to its original state. * @hide */ @TestApi public void reset() { initialize(); } /** * Returns {@code true} if {@link #getLeapSecond()} is available, {@code false} otherwise. */ public boolean hasLeapSecond() { return isFlagSet(HAS_LEAP_SECOND); } /** * Gets the leap second associated with the clock's time. * *

The sign of the value is defined by the following equation: *

     *     UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000
* *

The value is only available if {@link #hasLeapSecond()} is {@code true}. */ public int getLeapSecond() { return mLeapSecond; } /** * Sets the leap second associated with the clock's time. * @hide */ @TestApi public void setLeapSecond(int leapSecond) { setFlag(HAS_LEAP_SECOND); mLeapSecond = leapSecond; } /** * Resets the leap second associated with the clock's time. * @hide */ @TestApi public void resetLeapSecond() { resetFlag(HAS_LEAP_SECOND); mLeapSecond = Integer.MIN_VALUE; } /** * Gets the GNSS receiver internal hardware clock value in nanoseconds. * *

This value is expected to be monotonically increasing while the hardware clock remains * powered on. For the case of a hardware clock that is not continuously on, see the * {@link #getHardwareClockDiscontinuityCount} field. The GPS time can be derived by subtracting * the sum of {@link #getFullBiasNanos()} and {@link #getBiasNanos()} (when they are available) * from this value. Sub-nanosecond accuracy can be provided by means of {@link #getBiasNanos()}. * *

The error estimate for this value (if applicable) is {@link #getTimeUncertaintyNanos()}. */ public long getTimeNanos() { return mTimeNanos; } /** * Sets the GNSS receiver internal clock in nanoseconds. * @hide */ @TestApi public void setTimeNanos(long timeNanos) { mTimeNanos = timeNanos; } /** * Returns {@code true} if {@link #getTimeUncertaintyNanos()} is available, {@code false} * otherwise. */ public boolean hasTimeUncertaintyNanos() { return isFlagSet(HAS_TIME_UNCERTAINTY); } /** * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds. * *

The uncertainty is represented as an absolute (single sided) value. * *

The value is only available if {@link #hasTimeUncertaintyNanos()} is {@code true}. * *

This value is often effectively zero (it is the reference clock by which all other times * and time uncertainties are measured), and thus this field may often be 0, or not provided. */ public double getTimeUncertaintyNanos() { return mTimeUncertaintyNanos; } /** * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds. * @hide */ @TestApi public void setTimeUncertaintyNanos(double timeUncertaintyNanos) { setFlag(HAS_TIME_UNCERTAINTY); mTimeUncertaintyNanos = timeUncertaintyNanos; } /** * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds. * @hide */ @TestApi public void resetTimeUncertaintyNanos() { resetFlag(HAS_TIME_UNCERTAINTY); mTimeUncertaintyNanos = Double.NaN; } /** * Returns {@code true} if {@link #getFullBiasNanos()} is available, {@code false} otherwise. */ public boolean hasFullBiasNanos() { return isFlagSet(HAS_FULL_BIAS); } /** * Gets the difference between hardware clock ({@link #getTimeNanos()}) inside GPS receiver and * the true GPS time since 0000Z, January 6, 1980, in nanoseconds. * *

This value is available if the receiver has estimated GPS time. If the computed time is * for a non-GPS constellation, the time offset of that constellation to GPS has to be applied * to fill this value. The value is only available if {@link #hasFullBiasNanos()} is * {@code true}. * *

The error estimate for the sum of this field and {@link #getBiasNanos} is * {@link #getBiasUncertaintyNanos()}. * *

The sign of the value is defined by the following equation: * *

     *     local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)
*/ public long getFullBiasNanos() { return mFullBiasNanos; } /** * Sets the full bias in nanoseconds. * @hide */ @TestApi public void setFullBiasNanos(long value) { setFlag(HAS_FULL_BIAS); mFullBiasNanos = value; } /** * Resets the full bias in nanoseconds. * @hide */ @TestApi public void resetFullBiasNanos() { resetFlag(HAS_FULL_BIAS); mFullBiasNanos = Long.MIN_VALUE; } /** * Returns {@code true} if {@link #getBiasNanos()} is available, {@code false} otherwise. */ public boolean hasBiasNanos() { return isFlagSet(HAS_BIAS); } /** * Gets the clock's sub-nanosecond bias. * *

See the description of how this field is part of converting from hardware clock time, to * GPS time, in {@link #getFullBiasNanos()}. * *

The error estimate for the sum of this field and {@link #getFullBiasNanos} is * {@link #getBiasUncertaintyNanos()}. * *

The value is only available if {@link #hasBiasNanos()} is {@code true}. */ public double getBiasNanos() { return mBiasNanos; } /** * Sets the sub-nanosecond bias. * @hide */ @TestApi public void setBiasNanos(double biasNanos) { setFlag(HAS_BIAS); mBiasNanos = biasNanos; } /** * Resets the clock's Bias in nanoseconds. * @hide */ @TestApi public void resetBiasNanos() { resetFlag(HAS_BIAS); mBiasNanos = Double.NaN; } /** * Returns {@code true} if {@link #getBiasUncertaintyNanos()} is available, {@code false} * otherwise. */ public boolean hasBiasUncertaintyNanos() { return isFlagSet(HAS_BIAS_UNCERTAINTY); } /** * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. * *

See the description of how this field provides the error estimate in the conversion from * hardware clock time, to GPS time, in {@link #getFullBiasNanos()}. * *

The value is only available if {@link #hasBiasUncertaintyNanos()} is {@code true}. */ public double getBiasUncertaintyNanos() { return mBiasUncertaintyNanos; } /** * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. * @hide */ @TestApi public void setBiasUncertaintyNanos(double biasUncertaintyNanos) { setFlag(HAS_BIAS_UNCERTAINTY); mBiasUncertaintyNanos = biasUncertaintyNanos; } /** * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. * @hide */ @TestApi public void resetBiasUncertaintyNanos() { resetFlag(HAS_BIAS_UNCERTAINTY); mBiasUncertaintyNanos = Double.NaN; } /** * Returns {@code true} if {@link #getDriftNanosPerSecond()} is available, {@code false} * otherwise. */ public boolean hasDriftNanosPerSecond() { return isFlagSet(HAS_DRIFT); } /** * Gets the clock's Drift in nanoseconds per second. * *

A positive value indicates that the frequency is higher than the nominal (e.g. GPS master * clock) frequency. The error estimate for this reported drift is * {@link #getDriftUncertaintyNanosPerSecond()}. * *

The value is only available if {@link #hasDriftNanosPerSecond()} is {@code true}. */ public double getDriftNanosPerSecond() { return mDriftNanosPerSecond; } /** * Sets the clock's Drift in nanoseconds per second. * @hide */ @TestApi public void setDriftNanosPerSecond(double driftNanosPerSecond) { setFlag(HAS_DRIFT); mDriftNanosPerSecond = driftNanosPerSecond; } /** * Resets the clock's Drift in nanoseconds per second. * @hide */ @TestApi public void resetDriftNanosPerSecond() { resetFlag(HAS_DRIFT); mDriftNanosPerSecond = Double.NaN; } /** * Returns {@code true} if {@link #getDriftUncertaintyNanosPerSecond()} is available, * {@code false} otherwise. */ public boolean hasDriftUncertaintyNanosPerSecond() { return isFlagSet(HAS_DRIFT_UNCERTAINTY); } /** * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. * *

The value is only available if {@link #hasDriftUncertaintyNanosPerSecond()} is * {@code true}. */ public double getDriftUncertaintyNanosPerSecond() { return mDriftUncertaintyNanosPerSecond; } /** * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. * @hide */ @TestApi public void setDriftUncertaintyNanosPerSecond(double driftUncertaintyNanosPerSecond) { setFlag(HAS_DRIFT_UNCERTAINTY); mDriftUncertaintyNanosPerSecond = driftUncertaintyNanosPerSecond; } /** * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. * @hide */ @TestApi public void resetDriftUncertaintyNanosPerSecond() { resetFlag(HAS_DRIFT_UNCERTAINTY); mDriftUncertaintyNanosPerSecond = Double.NaN; } /** * Gets count of hardware clock discontinuities. * *

When this value stays the same, vs. a value in a previously reported {@link GnssClock}, it * can be safely assumed that the {@code TimeNanos} value has been derived from a clock that has * been running continuously - e.g. a single continuously powered crystal oscillator, and thus * the {@code (FullBiasNanos + BiasNanos)} offset can be modelled with traditional clock bias * & drift models. * *

Each time this value changes, vs. the value in a previously reported {@link GnssClock}, * that suggests the hardware clock may have experienced a discontinuity (e.g. a power cycle or * other anomaly), so that any assumptions about modelling a smoothly changing * {@code (FullBiasNanos + BiasNanos)} offset, and a smoothly growing {@code (TimeNanos)} * between this and the previously reported {@code GnssClock}, should be reset. */ public int getHardwareClockDiscontinuityCount() { return mHardwareClockDiscontinuityCount; } /** * Sets count of last hardware clock discontinuity. * @hide */ @TestApi public void setHardwareClockDiscontinuityCount(int value) { mHardwareClockDiscontinuityCount = value; } public static final Creator CREATOR = new Creator() { @Override public GnssClock createFromParcel(Parcel parcel) { GnssClock gpsClock = new GnssClock(); gpsClock.mFlags = parcel.readInt(); gpsClock.mLeapSecond = parcel.readInt(); gpsClock.mTimeNanos = parcel.readLong(); gpsClock.mTimeUncertaintyNanos = parcel.readDouble(); gpsClock.mFullBiasNanos = parcel.readLong(); gpsClock.mBiasNanos = parcel.readDouble(); gpsClock.mBiasUncertaintyNanos = parcel.readDouble(); gpsClock.mDriftNanosPerSecond = parcel.readDouble(); gpsClock.mDriftUncertaintyNanosPerSecond = parcel.readDouble(); gpsClock.mHardwareClockDiscontinuityCount = parcel.readInt(); return gpsClock; } @Override public GnssClock[] newArray(int size) { return new GnssClock[size]; } }; @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mFlags); parcel.writeInt(mLeapSecond); parcel.writeLong(mTimeNanos); parcel.writeDouble(mTimeUncertaintyNanos); parcel.writeLong(mFullBiasNanos); parcel.writeDouble(mBiasNanos); parcel.writeDouble(mBiasUncertaintyNanos); parcel.writeDouble(mDriftNanosPerSecond); parcel.writeDouble(mDriftUncertaintyNanosPerSecond); parcel.writeInt(mHardwareClockDiscontinuityCount); } @Override public int describeContents() { return 0; } @Override public String toString() { final String format = " %-15s = %s\n"; final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n"; StringBuilder builder = new StringBuilder("GnssClock:\n"); builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null)); builder.append(String.format( formatWithUncertainty, "TimeNanos", mTimeNanos, "TimeUncertaintyNanos", hasTimeUncertaintyNanos() ? mTimeUncertaintyNanos : null)); builder.append(String.format( format, "FullBiasNanos", hasFullBiasNanos() ? mFullBiasNanos : null)); builder.append(String.format( formatWithUncertainty, "BiasNanos", hasBiasNanos() ? mBiasNanos : null, "BiasUncertaintyNanos", hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null)); builder.append(String.format( formatWithUncertainty, "DriftNanosPerSecond", hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null, "DriftUncertaintyNanosPerSecond", hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null)); builder.append(String.format( format, "HardwareClockDiscontinuityCount", mHardwareClockDiscontinuityCount)); return builder.toString(); } private void initialize() { mFlags = HAS_NO_FLAGS; resetLeapSecond(); setTimeNanos(Long.MIN_VALUE); resetTimeUncertaintyNanos(); resetFullBiasNanos(); resetBiasNanos(); resetBiasUncertaintyNanos(); resetDriftNanosPerSecond(); resetDriftUncertaintyNanosPerSecond(); setHardwareClockDiscontinuityCount(Integer.MIN_VALUE); } private void setFlag(int flag) { mFlags |= flag; } private void resetFlag(int flag) { mFlags &= ~flag; } private boolean isFlagSet(int flag) { return (mFlags & flag) == flag; } }