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.NonNull;
20import android.annotation.SystemApi;
21import android.os.Parcel;
22import android.os.Parcelable;
23
24import java.security.InvalidParameterException;
25
26/**
27 * A class containing a GPS satellite Navigation Message.
28 *
29 * @hide
30 */
31@SystemApi
32public class GpsNavigationMessage implements Parcelable {
33
34    private static final byte[] EMPTY_ARRAY = new byte[0];
35
36    // The following enumerations must be in sync with the values declared in gps.h
37
38    /**
39     * The type of the navigation message is not available or unknown.
40     */
41    public static final byte TYPE_UNKNOWN = 0;
42
43    /**
44     * The Navigation Message is of type L1 C/A.
45     */
46    public static final byte TYPE_L1CA = 1;
47
48    /**
49     * The Navigation Message is of type L1-CNAV.
50     */
51    public static final byte TYPE_L2CNAV = 2;
52
53    /**
54     * The Navigation Message is of type L5-CNAV.
55     */
56    public static final byte TYPE_L5CNAV = 3;
57
58    /**
59     * The Navigation Message is of type CNAV-2.
60     */
61    public static final byte TYPE_CNAV2 = 4;
62
63    /**
64     * The Navigation Message Status is 'unknown'.
65     */
66    public static final short STATUS_UNKNOWN = 0;
67
68    /**
69     * The Navigation Message was received without any parity error in its navigation words.
70     */
71    public static final short STATUS_PARITY_PASSED = (1<<0);
72
73    /**
74     * The Navigation Message was received with words that failed parity check, but the receiver was
75     * able to correct those words.
76     */
77    public static final short STATUS_PARITY_REBUILT = (1<<1);
78
79    // End enumerations in sync with gps.h
80
81    private byte mType;
82    private byte mPrn;
83    private short mMessageId;
84    private short mSubmessageId;
85    private byte[] mData;
86    private short mStatus;
87
88    GpsNavigationMessage() {
89        initialize();
90    }
91
92    /**
93     * Sets all contents to the values stored in the provided object.
94     */
95    public void set(GpsNavigationMessage navigationMessage) {
96        mType = navigationMessage.mType;
97        mPrn = navigationMessage.mPrn;
98        mMessageId = navigationMessage.mMessageId;
99        mSubmessageId = navigationMessage.mSubmessageId;
100        mData = navigationMessage.mData;
101        mStatus = navigationMessage.mStatus;
102    }
103
104    /**
105     * Resets all the contents to its original state.
106     */
107    public void reset() {
108        initialize();
109    }
110
111    /**
112     * Gets the type of the navigation message contained in the object.
113     */
114    public byte getType() {
115        return mType;
116    }
117
118    /**
119     * Sets the type of the navigation message.
120     */
121    public void setType(byte value) {
122        mType = value;
123    }
124
125    /**
126     * Gets a string representation of the 'type'.
127     * For internal and logging use only.
128     */
129    private String getTypeString() {
130        switch (mType) {
131            case TYPE_UNKNOWN:
132                return "Unknown";
133            case TYPE_L1CA:
134                return "L1 C/A";
135            case TYPE_L2CNAV:
136                return "L2-CNAV";
137            case TYPE_L5CNAV:
138                return "L5-CNAV";
139            case TYPE_CNAV2:
140                return "CNAV-2";
141            default:
142                return "<Invalid:" + mType + ">";
143        }
144    }
145
146    /**
147     * Gets the Pseudo-random number.
148     * Range: [1, 32].
149     */
150    public byte getPrn() {
151        return mPrn;
152    }
153
154    /**
155     * Sets the Pseud-random number.
156     */
157    public void setPrn(byte value) {
158        mPrn = value;
159    }
160
161    /**
162     * Gets the Message Identifier.
163     * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A
164     * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message.
165     * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1.
166     */
167    public short getMessageId() {
168        return mMessageId;
169    }
170
171    /**
172     * Sets the Message Identifier.
173     */
174    public void setMessageId(short value) {
175        mMessageId = value;
176    }
177
178    /**
179     * Gets the Sub-message Identifier.
180     * If required by {@link #getType()}, this value contains a sub-index within the current message
181     * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds
182     * to the sub-frame Id of the navigation message.
183     */
184    public short getSubmessageId() {
185        return mSubmessageId;
186    }
187
188    /**
189     * Sets the Sub-message identifier.
190     */
191    public void setSubmessageId(short value) {
192        mSubmessageId = value;
193    }
194
195    /**
196     * Gets the data associated with the Navigation Message.
197     * The bytes (or words) specified using big endian format (MSB first).
198     */
199    @NonNull
200    public byte[] getData() {
201        return mData;
202    }
203
204    /**
205     * Sets the data associated with the Navigation Message.
206     */
207    public void setData(byte[] value) {
208        if (value == null) {
209            throw new InvalidParameterException("Data must be a non-null array");
210        }
211
212        mData = value;
213    }
214
215    /**
216     * Gets the Status of the navigation message contained in the object.
217     */
218    public short getStatus() {
219        return mStatus;
220    }
221
222    /**
223     * Sets the status of the navigation message.
224     */
225    public void setStatus(short value) {
226        mStatus = value;
227    }
228
229    /**
230     * Gets a string representation of the 'status'.
231     * For internal and logging use only.
232     */
233    private String getStatusString() {
234        switch (mStatus) {
235            case STATUS_UNKNOWN:
236                return "Unknown";
237            case STATUS_PARITY_PASSED:
238                return "ParityPassed";
239            case STATUS_PARITY_REBUILT:
240                return "ParityRebuilt";
241            default:
242                return "<Invalid:" + mStatus + ">";
243        }
244    }
245
246    public static final Creator<GpsNavigationMessage> CREATOR =
247            new Creator<GpsNavigationMessage>() {
248        @Override
249        public GpsNavigationMessage createFromParcel(Parcel parcel) {
250            GpsNavigationMessage navigationMessage = new GpsNavigationMessage();
251
252            navigationMessage.setType(parcel.readByte());
253            navigationMessage.setPrn(parcel.readByte());
254            navigationMessage.setMessageId((short) parcel.readInt());
255            navigationMessage.setSubmessageId((short) parcel.readInt());
256
257            int dataLength = parcel.readInt();
258            byte[] data = new byte[dataLength];
259            parcel.readByteArray(data);
260            navigationMessage.setData(data);
261
262            if (parcel.dataAvail() >= Integer.SIZE) {
263                int status = parcel.readInt();
264                navigationMessage.setStatus((short) status);
265            } else {
266                navigationMessage.setStatus(STATUS_UNKNOWN);
267            }
268
269            return navigationMessage;
270        }
271
272        @Override
273        public GpsNavigationMessage[] newArray(int size) {
274            return new GpsNavigationMessage[size];
275        }
276    };
277
278    public void writeToParcel(Parcel parcel, int flags) {
279        parcel.writeByte(mType);
280        parcel.writeByte(mPrn);
281        parcel.writeInt(mMessageId);
282        parcel.writeInt(mSubmessageId);
283        parcel.writeInt(mData.length);
284        parcel.writeByteArray(mData);
285        parcel.writeInt(mStatus);
286    }
287
288    @Override
289    public int describeContents() {
290        return 0;
291    }
292
293    @Override
294    public String toString() {
295        final String format = "   %-15s = %s\n";
296        StringBuilder builder = new StringBuilder("GpsNavigationMessage:\n");
297
298        builder.append(String.format(format, "Type", getTypeString()));
299        builder.append(String.format(format, "Prn", mPrn));
300        builder.append(String.format(format, "Status", getStatusString()));
301        builder.append(String.format(format, "MessageId", mMessageId));
302        builder.append(String.format(format, "SubmessageId", mSubmessageId));
303
304        builder.append(String.format(format, "Data", "{"));
305        String prefix = "        ";
306        for(byte value : mData) {
307            builder.append(prefix);
308            builder.append(value);
309            prefix = ", ";
310        }
311        builder.append(" }");
312
313        return builder.toString();
314    }
315
316    private void initialize() {
317        mType = TYPE_UNKNOWN;
318        mPrn = 0;
319        mMessageId = -1;
320        mSubmessageId = -1;
321        mData = EMPTY_ARRAY;
322        mStatus = STATUS_UNKNOWN;
323    }
324}
325