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