blob: 25d247deff24d4f9dac0fd36b118a7a7e0635ab8 [file] [log] [blame]
/*
* 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.
*
* <p>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.
*
* <p>The sign of the value is defined by the following equation:
* <pre>
* UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000</pre>
*
* <p>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.
*
* <p>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()}.
*
* <p>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.
*
* <p>The uncertainty is represented as an absolute (single sided) value.
*
* <p>The value is only available if {@link #hasTimeUncertaintyNanos()} is {@code true}.
*
* <p>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.
*
* <p>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}.
*
* <p>The error estimate for the sum of this field and {@link #getBiasNanos} is
* {@link #getBiasUncertaintyNanos()}.
*
* <p>The sign of the value is defined by the following equation:
*
* <pre>
* local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)</pre>
*/
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.
*
* <p>See the description of how this field is part of converting from hardware clock time, to
* GPS time, in {@link #getFullBiasNanos()}.
*
* <p>The error estimate for the sum of this field and {@link #getFullBiasNanos} is
* {@link #getBiasUncertaintyNanos()}.
*
* <p>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.
*
* <p>See the description of how this field provides the error estimate in the conversion from
* hardware clock time, to GPS time, in {@link #getFullBiasNanos()}.
*
* <p>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.
*
* <p>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()}.
*
* <p>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.
*
* <p>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.
*
* <p>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
* &amp; drift models.
*
* <p>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<GnssClock> CREATOR = new Creator<GnssClock>() {
@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;
}
}