content: /*
   * 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.FloatRange;
  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);
      private static final int HAS_ELAPSED_REALTIME_NANOS = (1 << 7);
      private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS = (1 << 8);
  
      // 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;
      private long mElapsedRealtimeNanos;
      private double mElapsedRealtimeUncertaintyNanos;
  
      /**
       * @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;
          mElapsedRealtimeNanos = clock.mElapsedRealtimeNanos;
          mElapsedRealtimeUncertaintyNanos = clock.mElapsedRealtimeUncertaintyNanos;
      }
  
      /**
       * 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.
       */
      @FloatRange(from = 0.0f)
      public double getTimeUncertaintyNanos() {
          return mTimeUncertaintyNanos;
      }
  
      /**
       * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
       * @hide
       */
      @TestApi
      public void setTimeUncertaintyNanos(@FloatRange(from = 0.0f) 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}.
       */
      @FloatRange(from = 0.0f)
      public double getBiasUncertaintyNanos() {
          return mBiasUncertaintyNanos;
      }
  
      /**
       * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
       * @hide
       */
      @TestApi
      public void setBiasUncertaintyNanos(@FloatRange(from = 0.0f) 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>This value is the instantaneous time-derivative of the value provided by
       * {@link #getBiasNanos()}.
       *
       * <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}.
       */
      @FloatRange(from = 0.0f)
      public double getDriftUncertaintyNanosPerSecond() {
          return mDriftUncertaintyNanosPerSecond;
      }
  
      /**
       * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
       * @hide
       */
      @TestApi
      public void setDriftUncertaintyNanosPerSecond(
              @FloatRange(from = 0.0f) 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;
      }
  
      /**
       * Returns {@code true} if {@link #getElapsedRealtimeNanos()} is available, {@code false}
       * otherwise.
       */
      public boolean hasElapsedRealtimeNanos() {
          return isFlagSet(HAS_ELAPSED_REALTIME_NANOS);
      }
  
      /**
       * Returns the elapsed real-time of this clock since system boot, in nanoseconds.
       *
       * <p>The value is only available if {@link #hasElapsedRealtimeNanos()} is
       * {@code true}.
       */
      public long getElapsedRealtimeNanos() {
          return mElapsedRealtimeNanos;
      }
  
      /**
       * Sets the elapsed real-time of this clock since system boot, in nanoseconds.
       * @hide
       */
      @TestApi
      public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
          setFlag(HAS_ELAPSED_REALTIME_NANOS);
          mElapsedRealtimeNanos = elapsedRealtimeNanos;
      }
  
      /**
       * Resets the elapsed real-time of this clock since system boot, in nanoseconds.
       * @hide
       */
      @TestApi
      public void resetElapsedRealtimeNanos() {
          resetFlag(HAS_ELAPSED_REALTIME_NANOS);
          mElapsedRealtimeNanos = 0;
      }
  
      /**
       * Returns {@code true} if {@link #getElapsedRealtimeUncertaintyNanos()} is available, {@code
       * false} otherwise.
       */
      public boolean hasElapsedRealtimeUncertaintyNanos() {
          return isFlagSet(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
      }
  
      /**
       * Gets the estimate of the relative precision of the alignment of the
       * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
       * nanoseconds (68% confidence).
       *
       * <p>The value is only available if {@link #hasElapsedRealtimeUncertaintyNanos()} is
       * {@code true}.
       */
      @FloatRange(from = 0.0f)
      public double getElapsedRealtimeUncertaintyNanos() {
          return mElapsedRealtimeUncertaintyNanos;
      }
  
      /**
       * Sets the estimate of the relative precision of the alignment of the
       * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
       * nanoseconds (68% confidence).
       * @hide
       */
      @TestApi
      public void setElapsedRealtimeUncertaintyNanos(
              @FloatRange(from = 0.0f) double elapsedRealtimeUncertaintyNanos) {
          setFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
          mElapsedRealtimeUncertaintyNanos = elapsedRealtimeUncertaintyNanos;
      }
  
      /**
       * Resets the estimate of the relative precision of the alignment of the
       * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
       * nanoseconds (68% confidence).
       * @hide
       */
      @TestApi
      public void resetElapsedRealtimeUncertaintyNanos() {
          resetFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
          mElapsedRealtimeUncertaintyNanos = 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 @android.annotation.NonNull 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();
              gpsClock.mElapsedRealtimeNanos = parcel.readLong();
              gpsClock.mElapsedRealtimeUncertaintyNanos = parcel.readDouble();
  
              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);
          parcel.writeLong(mElapsedRealtimeNanos);
          parcel.writeDouble(mElapsedRealtimeUncertaintyNanos);
      }
  
      @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));
  
          builder.append(String.format(
                  format,
                  "ElapsedRealtimeNanos",
                  hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null));
  
          builder.append(String.format(
                  format,
                  "ElapsedRealtimeUncertaintyNanos",
                  hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos : null));
  
          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);
          resetElapsedRealtimeNanos();
          resetElapsedRealtimeUncertaintyNanos();
      }
  
      private void setFlag(int flag) {
          mFlags |= flag;
      }
  
      private void resetFlag(int flag) {
          mFlags &= ~flag;
      }
  
      private boolean isFlagSet(int flag) {
          return (mFlags & flag) == flag;
      }
  }
  