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