1 /* 2 * Copyright (C) 2020 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 android.annotation.FloatRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import java.util.Arrays; 26 import java.util.List; 27 import java.util.Objects; 28 29 /** 30 * A class that contains information about a GNSS antenna. GNSS antenna characteristics can change 31 * with device configuration, such as when a device is folded open or closed. Antenna information is 32 * delivered to registered instances of {@link Listener}. 33 * 34 * <p> Antenna info parameters are measured for each specific device model by the device 35 * manufacturers and provided to the Android framework. 36 */ 37 public final class GnssAntennaInfo implements Parcelable { 38 private final double mCarrierFrequencyMHz; 39 private final PhaseCenterOffset mPhaseCenterOffset; 40 private final @Nullable SphericalCorrections mPhaseCenterVariationCorrections; 41 private final @Nullable SphericalCorrections mSignalGainCorrections; 42 43 /** 44 * Used for receiving GNSS antenna info from the GNSS engine. 45 */ 46 public interface Listener { 47 /** 48 * Invoked on a change to GNSS antenna info. 49 */ onGnssAntennaInfoReceived(@onNull List<GnssAntennaInfo> gnssAntennaInfos)50 void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos); 51 } 52 53 /** 54 * Class containing information about the antenna phase center offset (PCO). PCO is defined with 55 * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen 56 * for mobiles - see sensor or form factor documents for details. Uncertainties are reported 57 * to 1-sigma. 58 */ 59 public static final class PhaseCenterOffset implements Parcelable { 60 private final double mOffsetXMm; 61 private final double mOffsetXUncertaintyMm; 62 private final double mOffsetYMm; 63 private final double mOffsetYUncertaintyMm; 64 private final double mOffsetZMm; 65 private final double mOffsetZUncertaintyMm; 66 PhaseCenterOffset( double offsetXMm, double offsetXUncertaintyMm, double offsetYMm, double offsetYUncertaintyMm, double offsetZMm, double offsetZUncertaintyMm)67 public PhaseCenterOffset( 68 double offsetXMm, double offsetXUncertaintyMm, 69 double offsetYMm, double offsetYUncertaintyMm, 70 double offsetZMm, double offsetZUncertaintyMm) { 71 mOffsetXMm = offsetXMm; 72 mOffsetYMm = offsetYMm; 73 mOffsetZMm = offsetZMm; 74 mOffsetXUncertaintyMm = offsetXUncertaintyMm; 75 mOffsetYUncertaintyMm = offsetYUncertaintyMm; 76 mOffsetZUncertaintyMm = offsetZUncertaintyMm; 77 } 78 79 public static final @NonNull Creator<PhaseCenterOffset> CREATOR = 80 new Creator<PhaseCenterOffset>() { 81 @Override 82 public PhaseCenterOffset createFromParcel(Parcel in) { 83 return new PhaseCenterOffset( 84 in.readDouble(), 85 in.readDouble(), 86 in.readDouble(), 87 in.readDouble(), 88 in.readDouble(), 89 in.readDouble() 90 ); 91 } 92 93 @Override 94 public PhaseCenterOffset[] newArray(int size) { 95 return new PhaseCenterOffset[size]; 96 } 97 }; 98 99 /** 100 * Returns the x-axis offset of the phase center from the origin of the Android sensor 101 * coordinate system, in millimeters. 102 */ 103 @FloatRange() getXOffsetMm()104 public double getXOffsetMm() { 105 return mOffsetXMm; 106 } 107 108 /** 109 * Returns the 1-sigma uncertainty of the x-axis offset of the phase center from the origin 110 * of the Android sensor coordinate system, in millimeters. 111 */ 112 @FloatRange() getXOffsetUncertaintyMm()113 public double getXOffsetUncertaintyMm() { 114 return mOffsetXUncertaintyMm; 115 } 116 117 /** 118 * Returns the y-axis offset of the phase center from the origin of the Android sensor 119 * coordinate system, in millimeters. 120 */ 121 @FloatRange() getYOffsetMm()122 public double getYOffsetMm() { 123 return mOffsetYMm; 124 } 125 126 /** 127 * Returns the 1-sigma uncertainty of the y-axis offset of the phase center from the origin 128 * of the Android sensor coordinate system, in millimeters. 129 */ 130 @FloatRange() getYOffsetUncertaintyMm()131 public double getYOffsetUncertaintyMm() { 132 return mOffsetYUncertaintyMm; 133 } 134 135 /** 136 * Returns the z-axis offset of the phase center from the origin of the Android sensor 137 * coordinate system, in millimeters. 138 */ 139 @FloatRange() getZOffsetMm()140 public double getZOffsetMm() { 141 return mOffsetZMm; 142 } 143 144 /** 145 * Returns the 1-sigma uncertainty of the z-axis offset of the phase center from the origin 146 * of the Android sensor coordinate system, in millimeters. 147 */ 148 @FloatRange() getZOffsetUncertaintyMm()149 public double getZOffsetUncertaintyMm() { 150 return mOffsetZUncertaintyMm; 151 } 152 153 @Override describeContents()154 public int describeContents() { 155 return 0; 156 } 157 158 @Override writeToParcel(@onNull Parcel dest, int flags)159 public void writeToParcel(@NonNull Parcel dest, int flags) { 160 dest.writeDouble(mOffsetXMm); 161 dest.writeDouble(mOffsetXUncertaintyMm); 162 dest.writeDouble(mOffsetYMm); 163 dest.writeDouble(mOffsetYUncertaintyMm); 164 dest.writeDouble(mOffsetZMm); 165 dest.writeDouble(mOffsetZUncertaintyMm); 166 } 167 168 @Override toString()169 public String toString() { 170 return "PhaseCenterOffset{" 171 + "OffsetXMm=" + mOffsetXMm + " +/-" + mOffsetXUncertaintyMm 172 + ", OffsetYMm=" + mOffsetYMm + " +/-" + mOffsetYUncertaintyMm 173 + ", OffsetZMm=" + mOffsetZMm + " +/-" + mOffsetZUncertaintyMm 174 + '}'; 175 } 176 177 @Override equals(Object o)178 public boolean equals(Object o) { 179 if (this == o) { 180 return true; 181 } 182 if (!(o instanceof PhaseCenterOffset)) { 183 return false; 184 } 185 PhaseCenterOffset that = (PhaseCenterOffset) o; 186 return Double.compare(that.mOffsetXMm, mOffsetXMm) == 0 187 && Double.compare(that.mOffsetXUncertaintyMm, mOffsetXUncertaintyMm) == 0 188 && Double.compare(that.mOffsetYMm, mOffsetYMm) == 0 189 && Double.compare(that.mOffsetYUncertaintyMm, mOffsetYUncertaintyMm) == 0 190 && Double.compare(that.mOffsetZMm, mOffsetZMm) == 0 191 && Double.compare(that.mOffsetZUncertaintyMm, mOffsetZUncertaintyMm) == 0; 192 } 193 194 @Override hashCode()195 public int hashCode() { 196 return Objects.hash(mOffsetXMm, mOffsetYMm, mOffsetZMm); 197 } 198 } 199 200 /** 201 * Represents corrections on a spherical mapping. Corrections are added to measurements to 202 * obtain the corrected values. 203 * 204 * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays. 205 * 206 * Each row (major indices) represents a fixed theta. The first row corresponds to a 207 * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) 208 * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta 209 * = 360 / (number of rows). 210 * 211 * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending 212 * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles, 213 * i.e., deltaPhi = 180 / (number of columns - 1). 214 */ 215 public static final class SphericalCorrections implements Parcelable { 216 217 private final int mNumRows; 218 private final int mNumColumns; 219 private final double[][] mCorrections; 220 private final double[][] mCorrectionUncertainties; 221 SphericalCorrections(@onNull double[][] corrections, @NonNull double[][] correctionUncertainties)222 public SphericalCorrections(@NonNull double[][] corrections, 223 @NonNull double[][] correctionUncertainties) { 224 if (corrections.length != correctionUncertainties.length || corrections.length < 1) { 225 throw new IllegalArgumentException("correction and uncertainty arrays must have " 226 + "the same (non-zero) dimensions"); 227 } 228 229 mNumRows = corrections.length; 230 mNumColumns = corrections[0].length; 231 for (int i = 0; i < corrections.length; i++) { 232 if (corrections[i].length != mNumColumns 233 || correctionUncertainties[i].length != mNumColumns || mNumColumns < 2) { 234 throw new IllegalArgumentException("correction and uncertainty arrays must all " 235 + " have the same (greater than 2) number of columns"); 236 } 237 } 238 239 mCorrections = corrections; 240 mCorrectionUncertainties = correctionUncertainties; 241 } 242 SphericalCorrections(Parcel in)243 private SphericalCorrections(Parcel in) { 244 int numRows = in.readInt(); 245 int numColumns = in.readInt(); 246 247 double[][] corrections = 248 new double[numRows][numColumns]; 249 double[][] correctionUncertainties = 250 new double[numRows][numColumns]; 251 252 for (int row = 0; row < numRows; row++) { 253 for (int col = 0; col < numColumns; col++) { 254 corrections[row][col] = in.readDouble(); 255 correctionUncertainties[row][col] = in.readDouble(); 256 } 257 } 258 259 mNumRows = numRows; 260 mNumColumns = numColumns; 261 mCorrections = corrections; 262 mCorrectionUncertainties = correctionUncertainties; 263 } 264 265 /** 266 * Array representing corrections on a spherical mapping. Corrections are added to 267 * measurements to obtain the corrected values. 268 * 269 * Each row (major indices) represents a fixed theta. The first row corresponds to a 270 * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) 271 * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., 272 * deltaTheta = 360 / (number of rows). 273 * 274 * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and 275 * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith 276 * angles, i.e., deltaPhi = 180 / (number of columns - 1). 277 */ 278 @NonNull getCorrectionsArray()279 public double[][] getCorrectionsArray() { 280 return mCorrections; 281 } 282 283 /** 284 * Array representing uncertainty on corrections on a spherical mapping. 285 * 286 * Each row (major indices) represents a fixed theta. The first row corresponds to a 287 * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) 288 * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., 289 * deltaTheta = 360 / (number of rows). 290 * 291 * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and 292 * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith 293 * angles, i.e., deltaPhi = 180 / (number of columns - 1). 294 */ 295 @NonNull getCorrectionUncertaintiesArray()296 public double[][] getCorrectionUncertaintiesArray() { 297 return mCorrectionUncertainties; 298 } 299 300 /** 301 * The fixed theta angle separation between successive rows. 302 */ 303 @FloatRange(from = 0.0f, to = 360.0f) getDeltaTheta()304 public double getDeltaTheta() { 305 return 360.0D / mNumRows; 306 } 307 308 /** 309 * The fixed phi angle separation between successive columns. 310 */ 311 @FloatRange(from = 0.0f, to = 180.0f) getDeltaPhi()312 public double getDeltaPhi() { 313 return 180.0D / (mNumColumns - 1); 314 } 315 316 317 public static final @NonNull Creator<SphericalCorrections> CREATOR = 318 new Creator<SphericalCorrections>() { 319 @Override 320 public SphericalCorrections createFromParcel(Parcel in) { 321 return new SphericalCorrections(in); 322 } 323 324 @Override 325 public SphericalCorrections[] newArray(int size) { 326 return new SphericalCorrections[size]; 327 } 328 }; 329 330 @Override describeContents()331 public int describeContents() { 332 return 0; 333 } 334 335 @Override writeToParcel(@onNull Parcel dest, int flags)336 public void writeToParcel(@NonNull Parcel dest, int flags) { 337 dest.writeInt(mNumRows); 338 dest.writeInt(mNumColumns); 339 for (int row = 0; row < mNumRows; row++) { 340 for (int col = 0; col < mNumColumns; col++) { 341 dest.writeDouble(mCorrections[row][col]); 342 dest.writeDouble(mCorrectionUncertainties[row][col]); 343 } 344 } 345 } 346 347 @Override toString()348 public String toString() { 349 return "SphericalCorrections{" 350 + "Corrections=" + Arrays.deepToString(mCorrections) 351 + ", CorrectionUncertainties=" + Arrays.deepToString(mCorrectionUncertainties) 352 + ", DeltaTheta=" + getDeltaTheta() 353 + ", DeltaPhi=" + getDeltaPhi() 354 + '}'; 355 } 356 357 @Override equals(Object o)358 public boolean equals(Object o) { 359 if (this == o) { 360 return true; 361 } 362 if (!(o instanceof SphericalCorrections)) { 363 return false; 364 } 365 SphericalCorrections that = (SphericalCorrections) o; 366 return mNumRows == that.mNumRows 367 && mNumColumns == that.mNumColumns 368 && Arrays.deepEquals(mCorrections, that.mCorrections) 369 && Arrays.deepEquals(mCorrectionUncertainties, that.mCorrectionUncertainties); 370 } 371 372 @Override hashCode()373 public int hashCode() { 374 int result = Arrays.deepHashCode(mCorrections); 375 result = 31 * result + Arrays.deepHashCode(mCorrectionUncertainties); 376 return result; 377 } 378 } 379 GnssAntennaInfo( double carrierFrequencyMHz, PhaseCenterOffset phaseCenterOffset, @Nullable SphericalCorrections phaseCenterVariationCorrections, @Nullable SphericalCorrections signalGainCorrectionDbi)380 private GnssAntennaInfo( 381 double carrierFrequencyMHz, 382 PhaseCenterOffset phaseCenterOffset, 383 @Nullable SphericalCorrections phaseCenterVariationCorrections, 384 @Nullable SphericalCorrections signalGainCorrectionDbi) { 385 mCarrierFrequencyMHz = carrierFrequencyMHz; 386 mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); 387 mPhaseCenterVariationCorrections = phaseCenterVariationCorrections; 388 mSignalGainCorrections = signalGainCorrectionDbi; 389 } 390 391 /** 392 * Builder class for GnssAntennaInfo. 393 */ 394 public static class Builder { 395 private double mCarrierFrequencyMHz; 396 private PhaseCenterOffset mPhaseCenterOffset; 397 private @Nullable SphericalCorrections mPhaseCenterVariationCorrections; 398 private @Nullable SphericalCorrections mSignalGainCorrections; 399 400 /** 401 * @deprecated Prefer {@link #Builder(double, PhaseCenterOffset)}. 402 */ 403 @Deprecated Builder()404 public Builder() { 405 this(0, new PhaseCenterOffset(0, 0, 0, 0, 0, 0)); 406 } 407 Builder(double carrierFrequencyMHz, @NonNull PhaseCenterOffset phaseCenterOffset)408 public Builder(double carrierFrequencyMHz, @NonNull PhaseCenterOffset phaseCenterOffset) { 409 mCarrierFrequencyMHz = carrierFrequencyMHz; 410 mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); 411 } 412 Builder(@onNull GnssAntennaInfo antennaInfo)413 public Builder(@NonNull GnssAntennaInfo antennaInfo) { 414 mCarrierFrequencyMHz = antennaInfo.mCarrierFrequencyMHz; 415 mPhaseCenterOffset = antennaInfo.mPhaseCenterOffset; 416 mPhaseCenterVariationCorrections = antennaInfo.mPhaseCenterVariationCorrections; 417 mSignalGainCorrections = antennaInfo.mSignalGainCorrections; 418 } 419 420 /** 421 * Set antenna carrier frequency (MHz). 422 * 423 * @param carrierFrequencyMHz antenna carrier frequency (MHz) 424 * @return Builder builder object 425 */ 426 @NonNull setCarrierFrequencyMHz(@loatRangefrom = 0.0f) double carrierFrequencyMHz)427 public Builder setCarrierFrequencyMHz(@FloatRange(from = 0.0f) double carrierFrequencyMHz) { 428 mCarrierFrequencyMHz = carrierFrequencyMHz; 429 return this; 430 } 431 432 /** 433 * Set antenna phase center offset. 434 * 435 * @param phaseCenterOffset phase center offset object 436 * @return Builder builder object 437 */ 438 @NonNull setPhaseCenterOffset(@onNull PhaseCenterOffset phaseCenterOffset)439 public Builder setPhaseCenterOffset(@NonNull PhaseCenterOffset phaseCenterOffset) { 440 mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); 441 return this; 442 } 443 444 /** 445 * Set phase center variation corrections. 446 * 447 * @param phaseCenterVariationCorrections phase center variation corrections object 448 * @return Builder builder object 449 */ 450 @NonNull setPhaseCenterVariationCorrections( @ullable SphericalCorrections phaseCenterVariationCorrections)451 public Builder setPhaseCenterVariationCorrections( 452 @Nullable SphericalCorrections phaseCenterVariationCorrections) { 453 mPhaseCenterVariationCorrections = phaseCenterVariationCorrections; 454 return this; 455 } 456 457 /** 458 * Set signal gain corrections. 459 * 460 * @param signalGainCorrections signal gain corrections object 461 * @return Builder builder object 462 */ 463 @NonNull setSignalGainCorrections( @ullable SphericalCorrections signalGainCorrections)464 public Builder setSignalGainCorrections( 465 @Nullable SphericalCorrections signalGainCorrections) { 466 mSignalGainCorrections = signalGainCorrections; 467 return this; 468 } 469 470 /** 471 * Build GnssAntennaInfo object. 472 * 473 * @return instance of GnssAntennaInfo 474 */ 475 @NonNull build()476 public GnssAntennaInfo build() { 477 return new GnssAntennaInfo(mCarrierFrequencyMHz, mPhaseCenterOffset, 478 mPhaseCenterVariationCorrections, mSignalGainCorrections); 479 } 480 } 481 482 @FloatRange(from = 0.0f) getCarrierFrequencyMHz()483 public double getCarrierFrequencyMHz() { 484 return mCarrierFrequencyMHz; 485 } 486 487 /** 488 * Returns a {@link PhaseCenterOffset} object encapsulating the phase center offset and 489 * corresponding uncertainties in millimeters. 490 * 491 * @return {@link PhaseCenterOffset} 492 */ 493 @NonNull getPhaseCenterOffset()494 public PhaseCenterOffset getPhaseCenterOffset() { 495 return mPhaseCenterOffset; 496 } 497 498 /** 499 * Returns a {@link SphericalCorrections} object encapsulating the phase center variation 500 * corrections and corresponding uncertainties in millimeters. 501 * 502 * @return phase center variation corrections as {@link SphericalCorrections} 503 */ 504 @Nullable getPhaseCenterVariationCorrections()505 public SphericalCorrections getPhaseCenterVariationCorrections() { 506 return mPhaseCenterVariationCorrections; 507 } 508 509 /** 510 * Returns a {@link SphericalCorrections} object encapsulating the signal gain 511 * corrections and corresponding uncertainties in dBi. 512 * 513 * @return signal gain corrections as {@link SphericalCorrections} 514 */ 515 @Nullable getSignalGainCorrections()516 public SphericalCorrections getSignalGainCorrections() { 517 return mSignalGainCorrections; 518 } 519 520 public static final @NonNull Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() { 521 @Override 522 public GnssAntennaInfo createFromParcel(Parcel in) { 523 double carrierFrequencyMHz = in.readDouble(); 524 PhaseCenterOffset phaseCenterOffset = 525 in.readTypedObject(PhaseCenterOffset.CREATOR); 526 SphericalCorrections phaseCenterVariationCorrections = 527 in.readTypedObject(SphericalCorrections.CREATOR); 528 SphericalCorrections signalGainCorrections = 529 in.readTypedObject(SphericalCorrections.CREATOR); 530 531 return new GnssAntennaInfo( 532 carrierFrequencyMHz, 533 phaseCenterOffset, 534 phaseCenterVariationCorrections, 535 signalGainCorrections); 536 } 537 538 @Override 539 public GnssAntennaInfo[] newArray(int size) { 540 return new GnssAntennaInfo[size]; 541 } 542 }; 543 544 @Override describeContents()545 public int describeContents() { 546 return 0; 547 } 548 549 @Override writeToParcel(@onNull Parcel parcel, int flags)550 public void writeToParcel(@NonNull Parcel parcel, int flags) { 551 parcel.writeDouble(mCarrierFrequencyMHz); 552 parcel.writeTypedObject(mPhaseCenterOffset, flags); 553 parcel.writeTypedObject(mPhaseCenterVariationCorrections, flags); 554 parcel.writeTypedObject(mSignalGainCorrections, flags); 555 } 556 557 @Override toString()558 public String toString() { 559 return "GnssAntennaInfo{" 560 + "CarrierFrequencyMHz=" + mCarrierFrequencyMHz 561 + ", PhaseCenterOffset=" + mPhaseCenterOffset 562 + ", PhaseCenterVariationCorrections=" + mPhaseCenterVariationCorrections 563 + ", SignalGainCorrections=" + mSignalGainCorrections 564 + '}'; 565 } 566 567 @Override equals(Object o)568 public boolean equals(Object o) { 569 if (this == o) { 570 return true; 571 } 572 if (!(o instanceof GnssAntennaInfo)) { 573 return false; 574 } 575 GnssAntennaInfo that = (GnssAntennaInfo) o; 576 return Double.compare(that.mCarrierFrequencyMHz, mCarrierFrequencyMHz) == 0 577 && mPhaseCenterOffset.equals(that.mPhaseCenterOffset) 578 && Objects.equals(mPhaseCenterVariationCorrections, 579 that.mPhaseCenterVariationCorrections) 580 && Objects.equals(mSignalGainCorrections, that.mSignalGainCorrections); 581 } 582 583 @Override hashCode()584 public int hashCode() { 585 return Objects.hash(mCarrierFrequencyMHz, mPhaseCenterOffset, 586 mPhaseCenterVariationCorrections, mSignalGainCorrections); 587 } 588 } 589