1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package android.location;
18
19import android.annotation.SystemApi;
20import android.os.Parcel;
21import android.os.Parcelable;
22
23/**
24 * A class containing a GPS clock timestamp.
25 * It represents a measurement of the GPS receiver's clock.
26 *
27 * @hide
28 */
29@SystemApi
30public class GpsClock implements Parcelable {
31
32    // The following enumerations must be in sync with the values declared in gps.h
33
34    /**
35     * The type of the time stored is not available or it is unknown.
36     */
37    public static final byte TYPE_UNKNOWN = 0;
38
39    /**
40     * The source of the time value reported by this class is the 'Local Hardware Clock'.
41     */
42    public static final byte TYPE_LOCAL_HW_TIME = 1;
43
44    /**
45     * The source of the time value reported by this class is the 'GPS time' derived from
46     * satellites (epoch = Jan 6, 1980).
47     */
48    public static final byte TYPE_GPS_TIME = 2;
49
50    private static final short HAS_NO_FLAGS = 0;
51    private static final short HAS_LEAP_SECOND = (1<<0);
52    private static final short HAS_TIME_UNCERTAINTY = (1<<1);
53    private static final short HAS_FULL_BIAS = (1<<2);
54    private static final short HAS_BIAS = (1<<3);
55    private static final short HAS_BIAS_UNCERTAINTY = (1<<4);
56    private static final short HAS_DRIFT = (1<<5);
57    private static final short HAS_DRIFT_UNCERTAINTY = (1<<6);
58
59    // End enumerations in sync with gps.h
60
61    private short mFlags;
62    private short mLeapSecond;
63    private byte mType;
64    private long mTimeInNs;
65    private double mTimeUncertaintyInNs;
66    private long mFullBiasInNs;
67    private double mBiasInNs;
68    private double mBiasUncertaintyInNs;
69    private double mDriftInNsPerSec;
70    private double mDriftUncertaintyInNsPerSec;
71
72    GpsClock() {
73        initialize();
74    }
75
76    /**
77     * Sets all contents to the values stored in the provided object.
78     */
79    public void set(GpsClock clock) {
80        mFlags = clock.mFlags;
81        mLeapSecond = clock.mLeapSecond;
82        mType = clock.mType;
83        mTimeInNs = clock.mTimeInNs;
84        mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
85        mFullBiasInNs = clock.mFullBiasInNs;
86        mBiasInNs = clock.mBiasInNs;
87        mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
88        mDriftInNsPerSec = clock.mDriftInNsPerSec;
89        mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
90    }
91
92    /**
93     * Resets all the contents to its original state.
94     */
95    public void reset() {
96        initialize();
97    }
98
99    /**
100     * Gets the type of time reported by {@link #getTimeInNs()}.
101     */
102    public byte getType() {
103        return mType;
104    }
105
106    /**
107     * Sets the type of time reported.
108     */
109    public void setType(byte value) {
110        mType = value;
111    }
112
113    /**
114     * Gets a string representation of the 'type'.
115     * For internal and logging use only.
116     */
117    private String getTypeString() {
118        switch (mType) {
119            case TYPE_UNKNOWN:
120                return "Unknown";
121            case TYPE_GPS_TIME:
122                return "GpsTime";
123            case TYPE_LOCAL_HW_TIME:
124                return "LocalHwClock";
125            default:
126                return "<Invalid:" + mType + ">";
127        }
128    }
129
130    /**
131     * Returns true if {@link #getLeapSecond()} is available, false otherwise.
132     */
133    public boolean hasLeapSecond() {
134        return isFlagSet(HAS_LEAP_SECOND);
135    }
136
137    /**
138     * Gets the leap second associated with the clock's time.
139     * The sign of the value is defined by the following equation:
140     *      utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
141     *
142     * The value is only available if {@link #hasLeapSecond()} is true.
143     */
144    public short getLeapSecond() {
145        return mLeapSecond;
146    }
147
148    /**
149     * Sets the leap second associated with the clock's time.
150     */
151    public void setLeapSecond(short leapSecond) {
152        setFlag(HAS_LEAP_SECOND);
153        mLeapSecond = leapSecond;
154    }
155
156    /**
157     * Resets the leap second associated with the clock's time.
158     */
159    public void resetLeapSecond() {
160        resetFlag(HAS_LEAP_SECOND);
161        mLeapSecond = Short.MIN_VALUE;
162    }
163
164    /**
165     * Gets the GPS receiver internal clock value in nanoseconds.
166     * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
167     * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
168     * {@link #getType()} defines the time reported.
169     *
170     * For 'local hardware clock' this value is expected to be monotonically increasing during the
171     * reporting session. The real GPS time can be derived by compensating
172     * {@link #getFullBiasInNs()} (when it is available) from this value.
173     *
174     * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
175     * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is
176     * specified.
177     *
178     * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}.
179     * The reported time includes {@link #getTimeUncertaintyInNs()}.
180     */
181    public long getTimeInNs() {
182        return mTimeInNs;
183    }
184
185    /**
186     * Sets the GPS receiver internal clock in nanoseconds.
187     */
188    public void setTimeInNs(long timeInNs) {
189        mTimeInNs = timeInNs;
190    }
191
192    /**
193     * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise.
194     */
195    public boolean hasTimeUncertaintyInNs() {
196        return isFlagSet(HAS_TIME_UNCERTAINTY);
197    }
198
199    /**
200     * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
201     * The uncertainty is represented as an absolute (single sided) value.
202     *
203     * The value is only available if {@link #hasTimeUncertaintyInNs()} is true.
204     */
205    public double getTimeUncertaintyInNs() {
206        return mTimeUncertaintyInNs;
207    }
208
209    /**
210     * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
211     */
212    public void setTimeUncertaintyInNs(double timeUncertaintyInNs) {
213        setFlag(HAS_TIME_UNCERTAINTY);
214        mTimeUncertaintyInNs = timeUncertaintyInNs;
215    }
216
217    /**
218     * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
219     */
220    public void resetTimeUncertaintyInNs() {
221        resetFlag(HAS_TIME_UNCERTAINTY);
222        mTimeUncertaintyInNs = Double.NaN;
223    }
224
225    /**
226     * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
227     */
228    public boolean hasFullBiasInNs() {
229        return isFlagSet(HAS_FULL_BIAS);
230    }
231
232    /**
233     * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
234     * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
235     *
236     * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
237     * the clock for GPS time.
238     * {@link #getBiasUncertaintyInNs()} should be used for quality check.
239     *
240     * The sign of the value is defined by the following equation:
241     *      true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
242     *
243     * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
244     * The value is onl available if {@link #hasFullBiasInNs()} is true.
245     */
246    public long getFullBiasInNs() {
247        return mFullBiasInNs;
248    }
249
250    /**
251     * Sets the full bias in nanoseconds.
252     */
253    public void setFullBiasInNs(long value) {
254        setFlag(HAS_FULL_BIAS);
255        mFullBiasInNs = value;
256    }
257
258    /**
259     * Resets the full bias in nanoseconds.
260     */
261    public void resetFullBiasInNs() {
262        resetFlag(HAS_FULL_BIAS);
263        mFullBiasInNs = Long.MIN_VALUE;
264    }
265
266    /**
267     * Returns true if {@link #getBiasInNs()} is available, false otherwise.
268     */
269    public boolean hasBiasInNs() {
270        return isFlagSet(HAS_BIAS);
271    }
272
273    /**
274     * Gets the clock's sub-nanosecond bias.
275     * The reported bias includes {@link #getBiasUncertaintyInNs()}.
276     *
277     * The value is only available if {@link #hasBiasInNs()} is true.
278     */
279    public double getBiasInNs() {
280        return mBiasInNs;
281    }
282
283    /**
284     * Sets the sub-nanosecond bias.
285     */
286    public void setBiasInNs(double biasInNs) {
287        setFlag(HAS_BIAS);
288        mBiasInNs = biasInNs;
289    }
290
291    /**
292     * Resets the clock's Bias in nanoseconds.
293     */
294    public void resetBiasInNs() {
295        resetFlag(HAS_BIAS);
296        mBiasInNs = Double.NaN;
297    }
298
299    /**
300     * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise.
301     */
302    public boolean hasBiasUncertaintyInNs() {
303        return isFlagSet(HAS_BIAS_UNCERTAINTY);
304    }
305
306    /**
307     * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
308     *
309     * The value is only available if {@link #hasBiasUncertaintyInNs()} is true.
310     */
311    public double getBiasUncertaintyInNs() {
312        return mBiasUncertaintyInNs;
313    }
314
315    /**
316     * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
317     */
318    public void setBiasUncertaintyInNs(double biasUncertaintyInNs) {
319        setFlag(HAS_BIAS_UNCERTAINTY);
320        mBiasUncertaintyInNs = biasUncertaintyInNs;
321    }
322
323    /**
324     * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
325     */
326    public void resetBiasUncertaintyInNs() {
327        resetFlag(HAS_BIAS_UNCERTAINTY);
328        mBiasUncertaintyInNs = Double.NaN;
329    }
330
331    /**
332     * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise.
333     */
334    public boolean hasDriftInNsPerSec() {
335        return isFlagSet(HAS_DRIFT);
336    }
337
338    /**
339     * Gets the clock's Drift in nanoseconds per second.
340     * A positive value indicates that the frequency is higher than the nominal frequency.
341     * The reported drift includes {@link #getDriftUncertaintyInNsPerSec()}.
342     *
343     * The value is only available if {@link #hasDriftInNsPerSec()} is true.
344     */
345    public double getDriftInNsPerSec() {
346        return mDriftInNsPerSec;
347    }
348
349    /**
350     * Sets the clock's Drift in nanoseconds per second.
351     */
352    public void setDriftInNsPerSec(double driftInNsPerSec) {
353        setFlag(HAS_DRIFT);
354        mDriftInNsPerSec = driftInNsPerSec;
355    }
356
357    /**
358     * Resets the clock's Drift in nanoseconds per second.
359     */
360    public void resetDriftInNsPerSec() {
361        resetFlag(HAS_DRIFT);
362        mDriftInNsPerSec = Double.NaN;
363    }
364
365    /**
366     * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise.
367     */
368    public boolean hasDriftUncertaintyInNsPerSec() {
369        return isFlagSet(HAS_DRIFT_UNCERTAINTY);
370    }
371
372    /**
373     * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
374     *
375     * The value is only available if {@link #hasDriftUncertaintyInNsPerSec()} is true.
376     */
377    public double getDriftUncertaintyInNsPerSec() {
378        return mDriftUncertaintyInNsPerSec;
379    }
380
381    /**
382     * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
383     */
384    public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) {
385        setFlag(HAS_DRIFT_UNCERTAINTY);
386        mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec;
387    }
388
389    /**
390     * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
391     */
392    public void resetDriftUncertaintyInNsPerSec() {
393        resetFlag(HAS_DRIFT_UNCERTAINTY);
394        mDriftUncertaintyInNsPerSec = Double.NaN;
395    }
396
397    public static final Creator<GpsClock> CREATOR = new Creator<GpsClock>() {
398        @Override
399        public GpsClock createFromParcel(Parcel parcel) {
400            GpsClock gpsClock = new GpsClock();
401
402            gpsClock.mFlags = (short) parcel.readInt();
403            gpsClock.mLeapSecond = (short) parcel.readInt();
404            gpsClock.mType = parcel.readByte();
405            gpsClock.mTimeInNs = parcel.readLong();
406            gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
407            gpsClock.mFullBiasInNs = parcel.readLong();
408            gpsClock.mBiasInNs = parcel.readDouble();
409            gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
410            gpsClock.mDriftInNsPerSec = parcel.readDouble();
411            gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
412
413            return gpsClock;
414        }
415
416        @Override
417        public GpsClock[] newArray(int size) {
418            return new GpsClock[size];
419        }
420    };
421
422    public void writeToParcel(Parcel parcel, int flags) {
423        parcel.writeInt(mFlags);
424        parcel.writeInt(mLeapSecond);
425        parcel.writeByte(mType);
426        parcel.writeLong(mTimeInNs);
427        parcel.writeDouble(mTimeUncertaintyInNs);
428        parcel.writeLong(mFullBiasInNs);
429        parcel.writeDouble(mBiasInNs);
430        parcel.writeDouble(mBiasUncertaintyInNs);
431        parcel.writeDouble(mDriftInNsPerSec);
432        parcel.writeDouble(mDriftUncertaintyInNsPerSec);
433    }
434
435    @Override
436    public int describeContents() {
437        return 0;
438    }
439
440    @Override
441    public String toString() {
442        final String format = "   %-15s = %s\n";
443        final String formatWithUncertainty = "   %-15s = %-25s   %-26s = %s\n";
444        StringBuilder builder = new StringBuilder("GpsClock:\n");
445
446        builder.append(String.format(format, "Type", getTypeString()));
447
448        builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
449
450        builder.append(String.format(
451                formatWithUncertainty,
452                "TimeInNs",
453                mTimeInNs,
454                "TimeUncertaintyInNs",
455                hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null));
456
457        builder.append(String.format(
458                format,
459                "FullBiasInNs",
460                hasFullBiasInNs() ? mFullBiasInNs : null));
461
462        builder.append(String.format(
463                formatWithUncertainty,
464                "BiasInNs",
465                hasBiasInNs() ? mBiasInNs : null,
466                "BiasUncertaintyInNs",
467                hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null));
468
469        builder.append(String.format(
470                formatWithUncertainty,
471                "DriftInNsPerSec",
472                hasDriftInNsPerSec() ? mDriftInNsPerSec : null,
473                "DriftUncertaintyInNsPerSec",
474                hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
475
476        return builder.toString();
477    }
478
479    private void initialize() {
480        mFlags = HAS_NO_FLAGS;
481        resetLeapSecond();
482        setType(TYPE_UNKNOWN);
483        setTimeInNs(Long.MIN_VALUE);
484        resetTimeUncertaintyInNs();
485        resetFullBiasInNs();
486        resetBiasInNs();
487        resetBiasUncertaintyInNs();
488        resetDriftInNsPerSec();
489        resetDriftUncertaintyInNsPerSec();
490    }
491
492    private void setFlag(short flag) {
493        mFlags |= flag;
494    }
495
496    private void resetFlag(short flag) {
497        mFlags &= ~flag;
498    }
499
500    private boolean isFlagSet(short flag) {
501        return (mFlags & flag) == flag;
502    }
503}
504