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.SystemApi;
  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.
   *
   * @hide
   */
  @SystemApi
  public class GpsClock implements Parcelable {
  
      // The following enumerations must be in sync with the values declared in gps.h
  
      /**
       * The type of the time stored is not available or it is unknown.
       */
      public static final byte TYPE_UNKNOWN = 0;
  
      /**
       * The source of the time value reported by this class is the 'Local Hardware Clock'.
       */
      public static final byte TYPE_LOCAL_HW_TIME = 1;
  
      /**
       * The source of the time value reported by this class is the 'GPS time' derived from
       * satellites (epoch = Jan 6, 1980).
       */
      public static final byte TYPE_GPS_TIME = 2;
  
      private static final short HAS_NO_FLAGS = 0;
      private static final short HAS_LEAP_SECOND = (1<<0);
      private static final short HAS_TIME_UNCERTAINTY = (1<<1);
      private static final short HAS_FULL_BIAS = (1<<2);
      private static final short HAS_BIAS = (1<<3);
      private static final short HAS_BIAS_UNCERTAINTY = (1<<4);
      private static final short HAS_DRIFT = (1<<5);
      private static final short HAS_DRIFT_UNCERTAINTY = (1<<6);
  
      // End enumerations in sync with gps.h
  
      private short mFlags;
      private short mLeapSecond;
      private byte mType;
      private long mTimeInNs;
      private double mTimeUncertaintyInNs;
      private long mFullBiasInNs;
      private double mBiasInNs;
      private double mBiasUncertaintyInNs;
      private double mDriftInNsPerSec;
      private double mDriftUncertaintyInNsPerSec;
  
      GpsClock() {
          initialize();
      }
  
      /**
       * Sets all contents to the values stored in the provided object.
       */
      public void set(GpsClock clock) {
          mFlags = clock.mFlags;
          mLeapSecond = clock.mLeapSecond;
          mType = clock.mType;
          mTimeInNs = clock.mTimeInNs;
          mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
          mFullBiasInNs = clock.mFullBiasInNs;
          mBiasInNs = clock.mBiasInNs;
          mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
          mDriftInNsPerSec = clock.mDriftInNsPerSec;
          mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
      }
  
      /**
       * Resets all the contents to its original state.
       */
      public void reset() {
          initialize();
      }
  
      /**
       * Gets the type of time reported by {@link #getTimeInNs()}.
       */
      public byte getType() {
          return mType;
      }
  
      /**
       * Sets the type of time reported.
       */
      public void setType(byte value) {
          mType = value;
      }
  
      /**
       * Gets a string representation of the 'type'.
       * For internal and logging use only.
       */
      private String getTypeString() {
          switch (mType) {
              case TYPE_UNKNOWN:
                  return "Unknown";
              case TYPE_GPS_TIME:
                  return "GpsTime";
              case TYPE_LOCAL_HW_TIME:
                  return "LocalHwClock";
              default:
                  return "<Invalid:" + mType + ">";
          }
      }
  
      /**
       * Returns true if {@link #getLeapSecond()} is available, 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:
       *      utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
       *
       * The value is only available if {@link #hasLeapSecond()} is true.
       */
      public short getLeapSecond() {
          return mLeapSecond;
      }
  
      /**
       * Sets the leap second associated with the clock's time.
       */
      public void setLeapSecond(short leapSecond) {
          setFlag(HAS_LEAP_SECOND);
          mLeapSecond = leapSecond;
      }
  
      /**
       * Resets the leap second associated with the clock's time.
       */
      public void resetLeapSecond() {
          resetFlag(HAS_LEAP_SECOND);
          mLeapSecond = Short.MIN_VALUE;
      }
  
      /**
       * Gets the GPS receiver internal clock value in nanoseconds.
       * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
       * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
       * {@link #getType()} defines the time reported.
       *
       * For 'local hardware clock' this value is expected to be monotonically increasing during the
       * reporting session. The real GPS time can be derived by compensating
       * {@link #getFullBiasInNs()} (when it is available) from this value.
       *
       * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
       * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is
       * specified.
       *
       * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}.
       * The reported time includes {@link #getTimeUncertaintyInNs()}.
       */
      public long getTimeInNs() {
          return mTimeInNs;
      }
  
      /**
       * Sets the GPS receiver internal clock in nanoseconds.
       */
      public void setTimeInNs(long timeInNs) {
          mTimeInNs = timeInNs;
      }
  
      /**
       * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise.
       */
      public boolean hasTimeUncertaintyInNs() {
          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 #hasTimeUncertaintyInNs()} is true.
       */
      public double getTimeUncertaintyInNs() {
          return mTimeUncertaintyInNs;
      }
  
      /**
       * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
       */
      public void setTimeUncertaintyInNs(double timeUncertaintyInNs) {
          setFlag(HAS_TIME_UNCERTAINTY);
          mTimeUncertaintyInNs = timeUncertaintyInNs;
      }
  
      /**
       * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
       */
      public void resetTimeUncertaintyInNs() {
          resetFlag(HAS_TIME_UNCERTAINTY);
          mTimeUncertaintyInNs = Double.NaN;
      }
  
      /**
       * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
       */
      public boolean hasFullBiasInNs() {
          return isFlagSet(HAS_FULL_BIAS);
      }
  
      /**
       * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
       * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
       *
       * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
       * the clock for GPS time.
       * {@link #getBiasUncertaintyInNs()} should be used for quality check.
       *
       * The sign of the value is defined by the following equation:
       *      true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
       *
       * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
       * The value is onl available if {@link #hasFullBiasInNs()} is true.
       */
      public long getFullBiasInNs() {
          return mFullBiasInNs;
      }
  
      /**
       * Sets the full bias in nanoseconds.
       */
      public void setFullBiasInNs(long value) {
          setFlag(HAS_FULL_BIAS);
          mFullBiasInNs = value;
      }
  
      /**
       * Resets the full bias in nanoseconds.
       */
      public void resetFullBiasInNs() {
          resetFlag(HAS_FULL_BIAS);
          mFullBiasInNs = Long.MIN_VALUE;
      }
  
      /**
       * Returns true if {@link #getBiasInNs()} is available, false otherwise.
       */
      public boolean hasBiasInNs() {
          return isFlagSet(HAS_BIAS);
      }
  
      /**
       * Gets the clock's sub-nanosecond bias.
       * The reported bias includes {@link #getBiasUncertaintyInNs()}.
       *
       * The value is only available if {@link #hasBiasInNs()} is true.
       */
      public double getBiasInNs() {
          return mBiasInNs;
      }
  
      /**
       * Sets the sub-nanosecond bias.
       */
      public void setBiasInNs(double biasInNs) {
          setFlag(HAS_BIAS);
          mBiasInNs = biasInNs;
      }
  
      /**
       * Resets the clock's Bias in nanoseconds.
       */
      public void resetBiasInNs() {
          resetFlag(HAS_BIAS);
          mBiasInNs = Double.NaN;
      }
  
      /**
       * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise.
       */
      public boolean hasBiasUncertaintyInNs() {
          return isFlagSet(HAS_BIAS_UNCERTAINTY);
      }
  
      /**
       * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
       *
       * The value is only available if {@link #hasBiasUncertaintyInNs()} is true.
       */
      public double getBiasUncertaintyInNs() {
          return mBiasUncertaintyInNs;
      }
  
      /**
       * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
       */
      public void setBiasUncertaintyInNs(double biasUncertaintyInNs) {
          setFlag(HAS_BIAS_UNCERTAINTY);
          mBiasUncertaintyInNs = biasUncertaintyInNs;
      }
  
      /**
       * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
       */
      public void resetBiasUncertaintyInNs() {
          resetFlag(HAS_BIAS_UNCERTAINTY);
          mBiasUncertaintyInNs = Double.NaN;
      }
  
      /**
       * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise.
       */
      public boolean hasDriftInNsPerSec() {
          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 frequency.
       * The reported drift includes {@link #getDriftUncertaintyInNsPerSec()}.
       *
       * The value is only available if {@link #hasDriftInNsPerSec()} is true.
       */
      public double getDriftInNsPerSec() {
          return mDriftInNsPerSec;
      }
  
      /**
       * Sets the clock's Drift in nanoseconds per second.
       */
      public void setDriftInNsPerSec(double driftInNsPerSec) {
          setFlag(HAS_DRIFT);
          mDriftInNsPerSec = driftInNsPerSec;
      }
  
      /**
       * Resets the clock's Drift in nanoseconds per second.
       */
      public void resetDriftInNsPerSec() {
          resetFlag(HAS_DRIFT);
          mDriftInNsPerSec = Double.NaN;
      }
  
      /**
       * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise.
       */
      public boolean hasDriftUncertaintyInNsPerSec() {
          return isFlagSet(HAS_DRIFT_UNCERTAINTY);
      }
  
      /**
       * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
       *
       * The value is only available if {@link #hasDriftUncertaintyInNsPerSec()} is true.
       */
      public double getDriftUncertaintyInNsPerSec() {
          return mDriftUncertaintyInNsPerSec;
      }
  
      /**
       * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
       */
      public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) {
          setFlag(HAS_DRIFT_UNCERTAINTY);
          mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec;
      }
  
      /**
       * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
       */
      public void resetDriftUncertaintyInNsPerSec() {
          resetFlag(HAS_DRIFT_UNCERTAINTY);
          mDriftUncertaintyInNsPerSec = Double.NaN;
      }
  
      public static final @android.annotation.NonNull Creator<GpsClock> CREATOR = new Creator<GpsClock>() {
          @Override
          public GpsClock createFromParcel(Parcel parcel) {
              GpsClock gpsClock = new GpsClock();
  
              gpsClock.mFlags = (short) parcel.readInt();
              gpsClock.mLeapSecond = (short) parcel.readInt();
              gpsClock.mType = parcel.readByte();
              gpsClock.mTimeInNs = parcel.readLong();
              gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
              gpsClock.mFullBiasInNs = parcel.readLong();
              gpsClock.mBiasInNs = parcel.readDouble();
              gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
              gpsClock.mDriftInNsPerSec = parcel.readDouble();
              gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
  
              return gpsClock;
          }
  
          @Override
          public GpsClock[] newArray(int size) {
              return new GpsClock[size];
          }
      };
  
      public void writeToParcel(Parcel parcel, int flags) {
          parcel.writeInt(mFlags);
          parcel.writeInt(mLeapSecond);
          parcel.writeByte(mType);
          parcel.writeLong(mTimeInNs);
          parcel.writeDouble(mTimeUncertaintyInNs);
          parcel.writeLong(mFullBiasInNs);
          parcel.writeDouble(mBiasInNs);
          parcel.writeDouble(mBiasUncertaintyInNs);
          parcel.writeDouble(mDriftInNsPerSec);
          parcel.writeDouble(mDriftUncertaintyInNsPerSec);
      }
  
      @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("GpsClock:\n");
  
          builder.append(String.format(format, "Type", getTypeString()));
  
          builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
  
          builder.append(String.format(
                  formatWithUncertainty,
                  "TimeInNs",
                  mTimeInNs,
                  "TimeUncertaintyInNs",
                  hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null));
  
          builder.append(String.format(
                  format,
                  "FullBiasInNs",
                  hasFullBiasInNs() ? mFullBiasInNs : null));
  
          builder.append(String.format(
                  formatWithUncertainty,
                  "BiasInNs",
                  hasBiasInNs() ? mBiasInNs : null,
                  "BiasUncertaintyInNs",
                  hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null));
  
          builder.append(String.format(
                  formatWithUncertainty,
                  "DriftInNsPerSec",
                  hasDriftInNsPerSec() ? mDriftInNsPerSec : null,
                  "DriftUncertaintyInNsPerSec",
                  hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
  
          return builder.toString();
      }
  
      private void initialize() {
          mFlags = HAS_NO_FLAGS;
          resetLeapSecond();
          setType(TYPE_UNKNOWN);
          setTimeInNs(Long.MIN_VALUE);
          resetTimeUncertaintyInNs();
          resetFullBiasInNs();
          resetBiasInNs();
          resetBiasUncertaintyInNs();
          resetDriftInNsPerSec();
          resetDriftUncertaintyInNsPerSec();
      }
  
      private void setFlag(short flag) {
          mFlags |= flag;
      }
  
      private void resetFlag(short flag) {
          mFlags &= ~flag;
      }
  
      private boolean isFlagSet(short flag) {
          return (mFlags & flag) == flag;
      }
  }
  