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