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 SphericalCorrections mPhaseCenterVariationCorrections;
38     private final SphericalCorrections mSignalGainCorrections;
39 
40     /**
41      * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface
42      * and call {@link LocationManager#registerAntennaInfoListener};
43      */
44     public interface Listener {
45         /**
46          * Returns the latest GNSS antenna info. This event is triggered when a listener is
47          * registered, and whenever the antenna info changes (due to a device configuration change).
48          */
onGnssAntennaInfoReceived(@onNull List<GnssAntennaInfo> gnssAntennaInfos)49         void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos);
50     }
51 
52     /**
53      * Class containing information about the antenna phase center offset (PCO). PCO is defined with
54      * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen
55      * for mobiles - see sensor or form factor documents for details. Uncertainties are reported
56      *  to 1-sigma.
57      */
58     public static final class PhaseCenterOffset implements Parcelable {
59         private final double mOffsetXMm;
60         private final double mOffsetXUncertaintyMm;
61         private final double mOffsetYMm;
62         private final double mOffsetYUncertaintyMm;
63         private final double mOffsetZMm;
64         private final double mOffsetZUncertaintyMm;
65 
PhaseCenterOffset( double offsetXMm, double offsetXUncertaintyMm, double offsetYMm, double offsetYUncertaintyMm, double offsetZMm, double offsetZUncertaintyMm)66         public PhaseCenterOffset(
67                 double offsetXMm, double offsetXUncertaintyMm,
68                 double offsetYMm, double offsetYUncertaintyMm,
69                 double offsetZMm, double offsetZUncertaintyMm) {
70             mOffsetXMm = offsetXMm;
71             mOffsetYMm = offsetYMm;
72             mOffsetZMm = offsetZMm;
73             mOffsetXUncertaintyMm = offsetXUncertaintyMm;
74             mOffsetYUncertaintyMm = offsetYUncertaintyMm;
75             mOffsetZUncertaintyMm = offsetZUncertaintyMm;
76         }
77 
78         public static final @NonNull Creator<PhaseCenterOffset> CREATOR =
79                 new Creator<PhaseCenterOffset>() {
80                     @Override
81                     public PhaseCenterOffset createFromParcel(Parcel in) {
82                         return new PhaseCenterOffset(
83                                 in.readDouble(),
84                                 in.readDouble(),
85                                 in.readDouble(),
86                                 in.readDouble(),
87                                 in.readDouble(),
88                                 in.readDouble()
89                         );
90                     }
91 
92                     @Override
93                     public PhaseCenterOffset[] newArray(int size) {
94                         return new PhaseCenterOffset[size];
95                     }
96                 };
97 
98         @FloatRange()
getXOffsetMm()99         public double getXOffsetMm() {
100             return mOffsetXMm;
101         }
102 
103         @FloatRange()
getXOffsetUncertaintyMm()104         public double getXOffsetUncertaintyMm() {
105             return mOffsetXUncertaintyMm;
106         }
107 
108         @FloatRange()
getYOffsetMm()109         public double getYOffsetMm() {
110             return mOffsetYMm;
111         }
112 
113         @FloatRange()
getYOffsetUncertaintyMm()114         public double getYOffsetUncertaintyMm() {
115             return mOffsetYUncertaintyMm;
116         }
117 
118         @FloatRange()
getZOffsetMm()119         public double getZOffsetMm() {
120             return mOffsetZMm;
121         }
122 
123         @FloatRange()
getZOffsetUncertaintyMm()124         public double getZOffsetUncertaintyMm() {
125             return mOffsetZUncertaintyMm;
126         }
127 
128         @Override
describeContents()129         public int describeContents() {
130             return 0;
131         }
132 
133         @Override
writeToParcel(@onNull Parcel dest, int flags)134         public void writeToParcel(@NonNull Parcel dest, int flags) {
135             dest.writeDouble(mOffsetXMm);
136             dest.writeDouble(mOffsetXUncertaintyMm);
137             dest.writeDouble(mOffsetYMm);
138             dest.writeDouble(mOffsetYUncertaintyMm);
139             dest.writeDouble(mOffsetZMm);
140             dest.writeDouble(mOffsetZUncertaintyMm);
141         }
142 
143         @Override
toString()144         public String toString() {
145             return "PhaseCenterOffset{"
146                     + "OffsetXMm=" + mOffsetXMm + " +/-" + mOffsetXUncertaintyMm
147                     + ", OffsetYMm=" + mOffsetYMm + " +/-" + mOffsetYUncertaintyMm
148                     + ", OffsetZMm=" + mOffsetZMm + " +/-" + mOffsetZUncertaintyMm
149                     + '}';
150         }
151     }
152 
153     /**
154      * Represents corrections on a spherical mapping. Corrections are added to measurements to
155      * obtain the corrected values.
156      *
157      * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays.
158      *
159      * Each row (major indices) represents a fixed theta. The first row corresponds to a
160      * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
161      * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta
162      * = 360 / (number of rows).
163      *
164      * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending
165      * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles,
166      * i.e., deltaPhi = 180 / (number of columns - 1).
167      */
168     public static final class SphericalCorrections implements Parcelable{
169         private final double[][] mCorrections;
170         private final double[][] mCorrectionUncertainties;
171         private final double mDeltaTheta;
172         private final double mDeltaPhi;
173         private final int mNumRows;
174         private final int mNumColumns;
175 
SphericalCorrections(@onNull double[][] corrections, @NonNull double[][] correctionUncertainties)176         public SphericalCorrections(@NonNull double[][] corrections,
177                 @NonNull double[][] correctionUncertainties) {
178             if (corrections.length != correctionUncertainties.length
179                     || corrections[0].length != correctionUncertainties[0].length) {
180                 throw new IllegalArgumentException("Correction and correction uncertainty arrays "
181                         + "must have the same dimensions.");
182             }
183 
184             mNumRows = corrections.length;
185             if (mNumRows < 1) {
186                 throw new IllegalArgumentException("Arrays must have at least one row.");
187             }
188 
189             mNumColumns = corrections[0].length;
190             if (mNumColumns < 2) {
191                 throw new IllegalArgumentException("Arrays must have at least two columns.");
192             }
193 
194             mCorrections = corrections;
195             mCorrectionUncertainties = correctionUncertainties;
196             mDeltaTheta = 360.0d / mNumRows;
197             mDeltaPhi = 180.0d / (mNumColumns - 1);
198         }
199 
SphericalCorrections(Parcel in)200         SphericalCorrections(Parcel in) {
201             int numRows = in.readInt();
202             int numColumns = in.readInt();
203 
204             double[][] corrections =
205                     new double[numRows][numColumns];
206             double[][] correctionUncertainties =
207                     new double[numRows][numColumns];
208 
209             for (int row = 0; row < numRows; row++) {
210                 in.readDoubleArray(corrections[row]);
211             }
212 
213             for (int row = 0; row < numRows; row++) {
214                 in.readDoubleArray(correctionUncertainties[row]);
215             }
216 
217             mNumRows = numRows;
218             mNumColumns = numColumns;
219             mCorrections = corrections;
220             mCorrectionUncertainties = correctionUncertainties;
221             mDeltaTheta = 360.0d / mNumRows;
222             mDeltaPhi = 180.0d / (mNumColumns - 1);
223         }
224 
225         /**
226          * Array representing corrections on a spherical mapping. Corrections are added to
227          * measurements to obtain the corrected values.
228          *
229          * Each row (major indices) represents a fixed theta. The first row corresponds to a
230          * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
231          * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e.,
232          * deltaTheta = 360 / (number of rows).
233          *
234          * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and
235          * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith
236          * angles, i.e., deltaPhi = 180 / (number of columns - 1).
237          */
238         @NonNull
getCorrectionsArray()239         public double[][] getCorrectionsArray() {
240             return mCorrections;
241         }
242 
243         /**
244          * Array representing uncertainty on corrections on a spherical mapping.
245          *
246          * Each row (major indices) represents a fixed theta. The first row corresponds to a
247          * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
248          * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e.,
249          * deltaTheta = 360 / (number of rows).
250          *
251          * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and
252          * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith
253          * angles, i.e., deltaPhi = 180 / (number of columns - 1).
254          */
255         @NonNull
getCorrectionUncertaintiesArray()256         public double[][] getCorrectionUncertaintiesArray() {
257             return mCorrectionUncertainties;
258         }
259 
260         /**
261          * The fixed theta angle separation between successive rows.
262          */
263         @FloatRange(from = 0.0f, to = 360.0f)
getDeltaTheta()264         public double getDeltaTheta() {
265             return mDeltaTheta;
266         }
267 
268         /**
269          * The fixed phi angle separation between successive columns.
270          */
271         @FloatRange(from = 0.0f, to = 180.0f)
getDeltaPhi()272         public double getDeltaPhi() {
273             return mDeltaPhi;
274         }
275 
276 
277         public static final @NonNull Creator<SphericalCorrections> CREATOR =
278                 new Creator<SphericalCorrections>() {
279                     @Override
280                     public SphericalCorrections createFromParcel(Parcel in) {
281                         return new SphericalCorrections(in);
282                     }
283 
284                     @Override
285                     public SphericalCorrections[] newArray(int size) {
286                         return new SphericalCorrections[size];
287                     }
288                 };
289 
290         @Override
describeContents()291         public int describeContents() {
292             return 0;
293         }
294 
295         @Override
writeToParcel(@onNull Parcel dest, int flags)296         public void writeToParcel(@NonNull Parcel dest, int flags) {
297             dest.writeInt(mNumRows);
298             dest.writeInt(mNumColumns);
299             for (double[] row: mCorrections) {
300                 dest.writeDoubleArray(row);
301             }
302             for (double[] row: mCorrectionUncertainties) {
303                 dest.writeDoubleArray(row);
304             }
305         }
306 
307         @Override
toString()308         public String toString() {
309             return "SphericalCorrections{"
310                     + "Corrections=" + Arrays.toString(mCorrections)
311                     + ", CorrectionUncertainties=" + Arrays.toString(mCorrectionUncertainties)
312                     + ", DeltaTheta=" + mDeltaTheta
313                     + ", DeltaPhi=" + mDeltaPhi
314                     + '}';
315         }
316     }
317 
GnssAntennaInfo( double carrierFrequencyMHz, @NonNull PhaseCenterOffset phaseCenterOffset, @Nullable SphericalCorrections phaseCenterVariationCorrections, @Nullable SphericalCorrections signalGainCorrectionDbi)318     private GnssAntennaInfo(
319             double carrierFrequencyMHz,
320             @NonNull PhaseCenterOffset phaseCenterOffset,
321             @Nullable SphericalCorrections phaseCenterVariationCorrections,
322             @Nullable SphericalCorrections signalGainCorrectionDbi) {
323         if (phaseCenterOffset == null) {
324             throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null.");
325         }
326         mCarrierFrequencyMHz = carrierFrequencyMHz;
327         mPhaseCenterOffset = phaseCenterOffset;
328         mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
329         mSignalGainCorrections = signalGainCorrectionDbi;
330     }
331 
332     /**
333      * Builder class for GnssAntennaInfo.
334      */
335     public static class Builder {
336         private double mCarrierFrequencyMHz;
337         private PhaseCenterOffset mPhaseCenterOffset;
338         private SphericalCorrections mPhaseCenterVariationCorrections;
339         private SphericalCorrections mSignalGainCorrections;
340 
341         /**
342          * Set antenna carrier frequency (MHz).
343          * @param carrierFrequencyMHz antenna carrier frequency (MHz)
344          * @return Builder builder object
345          */
346         @NonNull
setCarrierFrequencyMHz(@loatRangefrom = 0.0f) double carrierFrequencyMHz)347         public Builder setCarrierFrequencyMHz(@FloatRange(from = 0.0f) double carrierFrequencyMHz) {
348             mCarrierFrequencyMHz = carrierFrequencyMHz;
349             return this;
350         }
351 
352         /**
353          * Set antenna phase center offset.
354          * @param phaseCenterOffset phase center offset object
355          * @return Builder builder object
356          */
357         @NonNull
setPhaseCenterOffset(@onNull PhaseCenterOffset phaseCenterOffset)358         public Builder setPhaseCenterOffset(@NonNull PhaseCenterOffset phaseCenterOffset) {
359             mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset);
360             return this;
361         }
362 
363         /**
364          * Set phase center variation corrections.
365          * @param phaseCenterVariationCorrections phase center variation corrections object
366          * @return Builder builder object
367          */
368         @NonNull
setPhaseCenterVariationCorrections( @ullable SphericalCorrections phaseCenterVariationCorrections)369         public Builder setPhaseCenterVariationCorrections(
370                 @Nullable SphericalCorrections phaseCenterVariationCorrections) {
371             mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
372             return this;
373         }
374 
375         /**
376          * Set signal gain corrections.
377          * @param signalGainCorrections signal gain corrections object
378          * @return Builder builder object
379          */
380         @NonNull
setSignalGainCorrections( @ullable SphericalCorrections signalGainCorrections)381         public Builder setSignalGainCorrections(
382                 @Nullable SphericalCorrections signalGainCorrections) {
383             mSignalGainCorrections = signalGainCorrections;
384             return this;
385         }
386 
387         /**
388          * Build GnssAntennaInfo object.
389          * @return instance of GnssAntennaInfo
390          */
391         @NonNull
build()392         public GnssAntennaInfo build() {
393             return new GnssAntennaInfo(mCarrierFrequencyMHz, mPhaseCenterOffset,
394                     mPhaseCenterVariationCorrections, mSignalGainCorrections);
395         }
396     }
397 
398     @FloatRange(from = 0.0f)
getCarrierFrequencyMHz()399     public double getCarrierFrequencyMHz() {
400         return mCarrierFrequencyMHz;
401     }
402 
403     @NonNull
getPhaseCenterOffset()404     public PhaseCenterOffset getPhaseCenterOffset() {
405         return mPhaseCenterOffset;
406     }
407 
408     @Nullable
getPhaseCenterVariationCorrections()409     public SphericalCorrections getPhaseCenterVariationCorrections() {
410         return mPhaseCenterVariationCorrections;
411     }
412 
413     @Nullable
getSignalGainCorrections()414     public SphericalCorrections getSignalGainCorrections() {
415         return mSignalGainCorrections;
416     }
417 
418     public static final @android.annotation.NonNull
419                     Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() {
420                             @Override
421                             public GnssAntennaInfo createFromParcel(Parcel in) {
422                                 double carrierFrequencyMHz = in.readDouble();
423 
424                                 ClassLoader classLoader = getClass().getClassLoader();
425                                 PhaseCenterOffset phaseCenterOffset =
426                                         in.readParcelable(classLoader);
427                                 SphericalCorrections phaseCenterVariationCorrections =
428                                         in.readParcelable(classLoader);
429                                 SphericalCorrections signalGainCorrections =
430                                         in.readParcelable(classLoader);
431 
432                                 return new GnssAntennaInfo(
433                                             carrierFrequencyMHz,
434                                             phaseCenterOffset,
435                                             phaseCenterVariationCorrections,
436                                             signalGainCorrections);
437                             }
438 
439                             @Override
440                             public GnssAntennaInfo[] newArray(int size) {
441                                 return new GnssAntennaInfo[size];
442                             }
443                     };
444 
445     @Override
describeContents()446     public int describeContents() {
447         return 0;
448     }
449 
450     @Override
writeToParcel(@onNull Parcel parcel, int flags)451     public void writeToParcel(@NonNull Parcel parcel, int flags) {
452         parcel.writeDouble(mCarrierFrequencyMHz);
453         parcel.writeParcelable(mPhaseCenterOffset, flags);
454         parcel.writeParcelable(mPhaseCenterVariationCorrections, flags);
455         parcel.writeParcelable(mSignalGainCorrections, flags);
456     }
457 
458     @Override
toString()459     public String toString() {
460         return "GnssAntennaInfo{"
461                 + "CarrierFrequencyMHz=" + mCarrierFrequencyMHz
462                 + ", PhaseCenterOffset=" + mPhaseCenterOffset
463                 + ", PhaseCenterVariationCorrections=" + mPhaseCenterVariationCorrections
464                 + ", SignalGainCorrections=" + mSignalGainCorrections
465                 + '}';
466     }
467 }
468