1 /*
2  * Copyright (C) 2022 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 
17 package android.location;
18 
19 import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_ATTENUATION;
20 import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH;
21 import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC;
22 import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_REFLECTING_PLANE;
23 
24 import android.annotation.FloatRange;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.SystemApi;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 
31 import com.android.internal.util.Preconditions;
32 
33 import java.util.Objects;
34 
35 /**
36  * Contains the info of an excess path signal caused by reflection
37  *
38  * @hide
39  */
40 @SystemApi
41 public final class GnssExcessPathInfo implements Parcelable {
42 
43     private static final int HAS_EXCESS_PATH_LENGTH_MASK = EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH;
44     private static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK =
45             EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC;
46     private static final int HAS_REFLECTING_PLANE_MASK = EXCESS_PATH_INFO_HAS_REFLECTING_PLANE;
47     private static final int HAS_ATTENUATION_MASK = EXCESS_PATH_INFO_HAS_ATTENUATION;
48 
49     /* A bitmask of fields present in this object (see HAS_* constants defined above) */
50     private final int mFlags;
51     private final float mExcessPathLengthMeters;
52     private final float mExcessPathLengthUncertaintyMeters;
53     @Nullable
54     private final GnssReflectingPlane mReflectingPlane;
55     private final float mAttenuationDb;
56 
GnssExcessPathInfo( int flags, float excessPathLengthMeters, float excessPathLengthUncertaintyMeters, @Nullable GnssReflectingPlane reflectingPlane, float attenuationDb)57     private GnssExcessPathInfo(
58             int flags,
59             float excessPathLengthMeters,
60             float excessPathLengthUncertaintyMeters,
61             @Nullable GnssReflectingPlane reflectingPlane,
62             float attenuationDb) {
63         mFlags = flags;
64         mExcessPathLengthMeters = excessPathLengthMeters;
65         mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
66         mReflectingPlane = reflectingPlane;
67         mAttenuationDb = attenuationDb;
68     }
69 
70     /**
71      * Gets a bitmask of fields present in this object.
72      *
73      * <p>This API exists for JNI since it is easier for JNI to get one integer flag than looking up
74      * several has* methods.
75      * @hide
76      */
getFlags()77     public int getFlags() {
78         return mFlags;
79     }
80 
81     /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
hasExcessPathLength()82     public boolean hasExcessPathLength() {
83         return (mFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
84     }
85 
86     /**
87      * Returns the excess path length to be subtracted from pseudorange before using it in
88      * calculating location.
89      *
90      * <p>{@link #hasExcessPathLength()} must be true when calling this method. Otherwise, an
91      * {@link UnsupportedOperationException} will be thrown.
92      */
93     @FloatRange(from = 0.0f)
getExcessPathLengthMeters()94     public float getExcessPathLengthMeters() {
95         if (!hasExcessPathLength()) {
96             throw new UnsupportedOperationException(
97                     "getExcessPathLengthMeters() is not supported when hasExcessPathLength() is "
98                             + "false");
99         }
100         return mExcessPathLengthMeters;
101     }
102 
103     /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
hasExcessPathLengthUncertainty()104     public boolean hasExcessPathLengthUncertainty() {
105         return (mFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
106     }
107 
108     /**
109      * Returns the error estimate (1-sigma) for the excess path length estimate.
110      *
111      * <p>{@link #hasExcessPathLengthUncertainty()} must be true when calling this method.
112      * Otherwise, an {@link UnsupportedOperationException} will be thrown.
113      */
114     @FloatRange(from = 0.0f)
getExcessPathLengthUncertaintyMeters()115     public float getExcessPathLengthUncertaintyMeters() {
116         if (!hasExcessPathLengthUncertainty()) {
117             throw new UnsupportedOperationException(
118                     "getExcessPathLengthUncertaintyMeters() is not supported when "
119                             + "hasExcessPathLengthUncertainty() is false");
120         }
121         return mExcessPathLengthUncertaintyMeters;
122     }
123 
124     /**
125      * Returns {@code true} if {@link #getReflectingPlane()} is valid.
126      *
127      * <p>Returns false if the satellite signal goes through multiple reflections or if reflection
128      * plane serving is not supported.
129      */
hasReflectingPlane()130     public boolean hasReflectingPlane() {
131         return (mFlags & HAS_REFLECTING_PLANE_MASK) != 0;
132     }
133 
134     /**
135      * Returns the reflecting plane characteristics at which the signal has bounced.
136      *
137      * <p>{@link #hasReflectingPlane()} must be true when calling this method. Otherwise, an
138      * {@link UnsupportedOperationException} will be thrown.
139      */
140     @NonNull
getReflectingPlane()141     public GnssReflectingPlane getReflectingPlane() {
142         if (!hasReflectingPlane()) {
143             throw new UnsupportedOperationException(
144                     "getReflectingPlane() is not supported when hasReflectingPlane() is false");
145         }
146         return mReflectingPlane;
147     }
148 
149     /** Returns {@code true} if {@link #getAttenuationDb()} is valid. */
hasAttenuation()150     public boolean hasAttenuation() {
151         return (mFlags & HAS_ATTENUATION_MASK) != 0;
152     }
153 
154     /**
155      * Returns the expected reduction of signal strength of this path in non-negative dB.
156      *
157      * <p>{@link #hasAttenuation()} must be true when calling this method. Otherwise, an
158      * {@link UnsupportedOperationException} will be thrown.
159      */
160     @FloatRange(from = 0.0f)
getAttenuationDb()161     public float getAttenuationDb() {
162         if (!hasAttenuation()) {
163             throw new UnsupportedOperationException(
164                     "getAttenuationDb() is not supported when hasAttenuation() is false");
165         }
166         return mAttenuationDb;
167     }
168 
169     @Override
describeContents()170     public int describeContents() {
171         return 0;
172     }
173 
174     @Override
writeToParcel(@onNull Parcel parcel, int parcelFlags)175     public void writeToParcel(@NonNull Parcel parcel, int parcelFlags) {
176         parcel.writeInt(mFlags);
177         if (hasExcessPathLength()) {
178             parcel.writeFloat(mExcessPathLengthMeters);
179         }
180         if (hasExcessPathLengthUncertainty()) {
181             parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
182         }
183         if (hasReflectingPlane()) {
184             mReflectingPlane.writeToParcel(parcel, parcelFlags);
185         }
186         if (hasAttenuation()) {
187             parcel.writeFloat(mAttenuationDb);
188         }
189     }
190 
191     public static final @NonNull Creator<GnssExcessPathInfo> CREATOR =
192             new Creator<GnssExcessPathInfo>() {
193                 @Override
194                 @NonNull
195                 public GnssExcessPathInfo createFromParcel(@NonNull Parcel parcel) {
196                     int flags = parcel.readInt();
197                     float excessPathLengthMeters =
198                             (flags & HAS_EXCESS_PATH_LENGTH_MASK) != 0
199                                     ? parcel.readFloat() : 0;
200                     float excessPathLengthUncertaintyMeters =
201                             (flags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0
202                                     ? parcel.readFloat() : 0;
203                     GnssReflectingPlane reflectingPlane =
204                             (flags & HAS_REFLECTING_PLANE_MASK) != 0
205                                     ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null;
206                     float attenuationDb =
207                             (flags & HAS_ATTENUATION_MASK) != 0
208                                     ? parcel.readFloat() : 0;
209                     return new GnssExcessPathInfo(flags, excessPathLengthMeters,
210                             excessPathLengthUncertaintyMeters, reflectingPlane, attenuationDb);
211                 }
212 
213                 @Override
214                 public GnssExcessPathInfo[] newArray(int i) {
215                     return new GnssExcessPathInfo[i];
216                 }
217             };
218 
219     @Override
equals(Object obj)220     public boolean equals(Object obj) {
221         if (obj instanceof GnssExcessPathInfo) {
222             GnssExcessPathInfo that = (GnssExcessPathInfo) obj;
223             return this.mFlags == that.mFlags
224                     && (!hasExcessPathLength() || Float.compare(this.mExcessPathLengthMeters,
225                     that.mExcessPathLengthMeters) == 0)
226                     && (!hasExcessPathLengthUncertainty() || Float.compare(
227                     this.mExcessPathLengthUncertaintyMeters,
228                     that.mExcessPathLengthUncertaintyMeters) == 0)
229                     && (!hasReflectingPlane() || Objects.equals(this.mReflectingPlane,
230                     that.mReflectingPlane))
231                     && (!hasAttenuation() || Float.compare(this.mAttenuationDb,
232                     that.mAttenuationDb) == 0);
233         }
234         return false;
235     }
236 
237     @Override
hashCode()238     public int hashCode() {
239         return Objects.hash(mFlags,
240                 mExcessPathLengthMeters,
241                 mExcessPathLengthUncertaintyMeters,
242                 mReflectingPlane,
243                 mAttenuationDb);
244     }
245 
246     @NonNull
247     @Override
toString()248     public String toString() {
249         StringBuilder builder = new StringBuilder("GnssExcessPathInfo[");
250         if (hasExcessPathLength()) {
251             builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters);
252         }
253         if (hasExcessPathLengthUncertainty()) {
254             builder.append(" ExcessPathLengthUncertaintyMeters=").append(
255                     mExcessPathLengthUncertaintyMeters);
256         }
257         if (hasReflectingPlane()) {
258             builder.append(" ReflectingPlane=").append(mReflectingPlane);
259         }
260         if (hasAttenuation()) {
261             builder.append(" AttenuationDb=").append(mAttenuationDb);
262         }
263         builder.append(']');
264         return builder.toString();
265     }
266 
267     /** Builder for {@link GnssExcessPathInfo}. */
268     public static final class Builder {
269         private int mFlags;
270         private float mExcessPathLengthMeters;
271         private float mExcessPathLengthUncertaintyMeters;
272         @Nullable
273         private GnssReflectingPlane mReflectingPlane;
274         private float mAttenuationDb;
275 
276         /** Constructor for {@link Builder}. */
Builder()277         public Builder() {}
278 
279         /**
280          * Sets the excess path length to be subtracted from pseudorange before using it in
281          * calculating location.
282          */
283         @NonNull
setExcessPathLengthMeters( @loatRangefrom = 0.0f) float excessPathLengthMeters)284         public Builder setExcessPathLengthMeters(
285                 @FloatRange(from = 0.0f) float excessPathLengthMeters) {
286             Preconditions.checkArgumentInRange(excessPathLengthMeters, 0, Float.MAX_VALUE,
287                     "excessPathLengthMeters");
288             mExcessPathLengthMeters = excessPathLengthMeters;
289             mFlags |= HAS_EXCESS_PATH_LENGTH_MASK;
290             return this;
291         }
292 
293         /**
294          * Clears the excess path length.
295          *
296          * <p>This is to negate {@link #setExcessPathLengthMeters} call.
297          */
298         @NonNull
clearExcessPathLengthMeters()299         public Builder clearExcessPathLengthMeters() {
300             mExcessPathLengthMeters = 0;
301             mFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK;
302             return this;
303         }
304 
305         /** Sets the error estimate (1-sigma) for the excess path length estimate */
306         @NonNull
setExcessPathLengthUncertaintyMeters( @loatRangefrom = 0.0f) float excessPathLengthUncertaintyMeters)307         public Builder setExcessPathLengthUncertaintyMeters(
308                 @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) {
309             Preconditions.checkArgumentInRange(excessPathLengthUncertaintyMeters, 0,
310                     Float.MAX_VALUE, "excessPathLengthUncertaintyMeters");
311             mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
312             mFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK;
313             return this;
314         }
315 
316         /**
317          * Clears the error estimate (1-sigma) for the excess path length estimate
318          *
319          * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call.
320          */
321         @NonNull
clearExcessPathLengthUncertaintyMeters()322         public Builder clearExcessPathLengthUncertaintyMeters() {
323             mExcessPathLengthUncertaintyMeters = 0;
324             mFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK;
325             return this;
326         }
327 
328         /** Sets the reflecting plane information */
329         @NonNull
setReflectingPlane(@ullable GnssReflectingPlane reflectingPlane)330         public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) {
331             mReflectingPlane = reflectingPlane;
332             if (reflectingPlane != null) {
333                 mFlags |= HAS_REFLECTING_PLANE_MASK;
334             } else {
335                 mFlags &= ~HAS_REFLECTING_PLANE_MASK;
336             }
337             return this;
338         }
339 
340         /**
341          * Sets the attenuation value in dB.
342          */
343         @NonNull
setAttenuationDb(@loatRangefrom = 0.0f) float attenuationDb)344         public Builder setAttenuationDb(@FloatRange(from = 0.0f) float attenuationDb) {
345             Preconditions.checkArgumentInRange(attenuationDb, 0, Float.MAX_VALUE,
346                     "attenuationDb");
347             mAttenuationDb = attenuationDb;
348             mFlags |= HAS_ATTENUATION_MASK;
349             return this;
350         }
351 
352         /**
353          * Clears the attenuation value in dB.
354          *
355          * <p>This is to negate {@link #setAttenuationDb(float)} call.
356          */
357         @NonNull
clearAttenuationDb()358         public Builder clearAttenuationDb() {
359             mAttenuationDb = 0;
360             mFlags &= ~HAS_ATTENUATION_MASK;
361             return this;
362         }
363 
364         /** Builds a {@link GnssExcessPathInfo} instance as specified by this builder. */
365         @NonNull
build()366         public GnssExcessPathInfo build() {
367             return new GnssExcessPathInfo(
368                     mFlags,
369                     mExcessPathLengthMeters,
370                     mExcessPathLengthUncertaintyMeters,
371                     mReflectingPlane,
372                     mAttenuationDb);
373         }
374     }
375 }
376