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