| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.location; |
| |
| import android.annotation.FloatRange; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * A class that contains information about a GNSS antenna. GNSS antenna characteristics can change |
| * with device configuration, such as when a device is folded open or closed. Antenna information is |
| * delivered to registered instances of {@link Listener}. |
| * |
| * <p> Antenna info parameters are measured for each specific device model by the device |
| * manufacturers and provided to the Android framework. |
| */ |
| public final class GnssAntennaInfo implements Parcelable { |
| private final double mCarrierFrequencyMHz; |
| private final PhaseCenterOffset mPhaseCenterOffset; |
| private final @Nullable SphericalCorrections mPhaseCenterVariationCorrections; |
| private final @Nullable SphericalCorrections mSignalGainCorrections; |
| |
| /** |
| * Used for receiving GNSS antenna info from the GNSS engine. |
| */ |
| public interface Listener { |
| /** |
| * Invoked on a change to GNSS antenna info. |
| */ |
| void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos); |
| } |
| |
| /** |
| * Class containing information about the antenna phase center offset (PCO). PCO is defined with |
| * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen |
| * for mobiles - see sensor or form factor documents for details. Uncertainties are reported |
| * to 1-sigma. |
| */ |
| public static final class PhaseCenterOffset implements Parcelable { |
| private final double mOffsetXMm; |
| private final double mOffsetXUncertaintyMm; |
| private final double mOffsetYMm; |
| private final double mOffsetYUncertaintyMm; |
| private final double mOffsetZMm; |
| private final double mOffsetZUncertaintyMm; |
| |
| public PhaseCenterOffset( |
| double offsetXMm, double offsetXUncertaintyMm, |
| double offsetYMm, double offsetYUncertaintyMm, |
| double offsetZMm, double offsetZUncertaintyMm) { |
| mOffsetXMm = offsetXMm; |
| mOffsetYMm = offsetYMm; |
| mOffsetZMm = offsetZMm; |
| mOffsetXUncertaintyMm = offsetXUncertaintyMm; |
| mOffsetYUncertaintyMm = offsetYUncertaintyMm; |
| mOffsetZUncertaintyMm = offsetZUncertaintyMm; |
| } |
| |
| public static final @NonNull Creator<PhaseCenterOffset> CREATOR = |
| new Creator<PhaseCenterOffset>() { |
| @Override |
| public PhaseCenterOffset createFromParcel(Parcel in) { |
| return new PhaseCenterOffset( |
| in.readDouble(), |
| in.readDouble(), |
| in.readDouble(), |
| in.readDouble(), |
| in.readDouble(), |
| in.readDouble() |
| ); |
| } |
| |
| @Override |
| public PhaseCenterOffset[] newArray(int size) { |
| return new PhaseCenterOffset[size]; |
| } |
| }; |
| |
| /** |
| * Returns the x-axis offset of the phase center from the origin of the Android sensor |
| * coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getXOffsetMm() { |
| return mOffsetXMm; |
| } |
| |
| /** |
| * Returns the 1-sigma uncertainty of the x-axis offset of the phase center from the origin |
| * of the Android sensor coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getXOffsetUncertaintyMm() { |
| return mOffsetXUncertaintyMm; |
| } |
| |
| /** |
| * Returns the y-axis offset of the phase center from the origin of the Android sensor |
| * coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getYOffsetMm() { |
| return mOffsetYMm; |
| } |
| |
| /** |
| * Returns the 1-sigma uncertainty of the y-axis offset of the phase center from the origin |
| * of the Android sensor coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getYOffsetUncertaintyMm() { |
| return mOffsetYUncertaintyMm; |
| } |
| |
| /** |
| * Returns the z-axis offset of the phase center from the origin of the Android sensor |
| * coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getZOffsetMm() { |
| return mOffsetZMm; |
| } |
| |
| /** |
| * Returns the 1-sigma uncertainty of the z-axis offset of the phase center from the origin |
| * of the Android sensor coordinate system, in millimeters. |
| */ |
| @FloatRange() |
| public double getZOffsetUncertaintyMm() { |
| return mOffsetZUncertaintyMm; |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeDouble(mOffsetXMm); |
| dest.writeDouble(mOffsetXUncertaintyMm); |
| dest.writeDouble(mOffsetYMm); |
| dest.writeDouble(mOffsetYUncertaintyMm); |
| dest.writeDouble(mOffsetZMm); |
| dest.writeDouble(mOffsetZUncertaintyMm); |
| } |
| |
| @Override |
| public String toString() { |
| return "PhaseCenterOffset{" |
| + "OffsetXMm=" + mOffsetXMm + " +/-" + mOffsetXUncertaintyMm |
| + ", OffsetYMm=" + mOffsetYMm + " +/-" + mOffsetYUncertaintyMm |
| + ", OffsetZMm=" + mOffsetZMm + " +/-" + mOffsetZUncertaintyMm |
| + '}'; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof PhaseCenterOffset)) { |
| return false; |
| } |
| PhaseCenterOffset that = (PhaseCenterOffset) o; |
| return Double.compare(that.mOffsetXMm, mOffsetXMm) == 0 |
| && Double.compare(that.mOffsetXUncertaintyMm, mOffsetXUncertaintyMm) == 0 |
| && Double.compare(that.mOffsetYMm, mOffsetYMm) == 0 |
| && Double.compare(that.mOffsetYUncertaintyMm, mOffsetYUncertaintyMm) == 0 |
| && Double.compare(that.mOffsetZMm, mOffsetZMm) == 0 |
| && Double.compare(that.mOffsetZUncertaintyMm, mOffsetZUncertaintyMm) == 0; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mOffsetXMm, mOffsetYMm, mOffsetZMm); |
| } |
| } |
| |
| /** |
| * Represents corrections on a spherical mapping. Corrections are added to measurements to |
| * obtain the corrected values. |
| * |
| * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays. |
| * |
| * Each row (major indices) represents a fixed theta. The first row corresponds to a |
| * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) |
| * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta |
| * = 360 / (number of rows). |
| * |
| * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending |
| * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles, |
| * i.e., deltaPhi = 180 / (number of columns - 1). |
| */ |
| public static final class SphericalCorrections implements Parcelable { |
| |
| private final int mNumRows; |
| private final int mNumColumns; |
| private final double[][] mCorrections; |
| private final double[][] mCorrectionUncertainties; |
| |
| public SphericalCorrections(@NonNull double[][] corrections, |
| @NonNull double[][] correctionUncertainties) { |
| if (corrections.length != correctionUncertainties.length || corrections.length < 1) { |
| throw new IllegalArgumentException("correction and uncertainty arrays must have " |
| + "the same (non-zero) dimensions"); |
| } |
| |
| mNumRows = corrections.length; |
| mNumColumns = corrections[0].length; |
| for (int i = 0; i < corrections.length; i++) { |
| if (corrections[i].length != mNumColumns |
| || correctionUncertainties[i].length != mNumColumns || mNumColumns < 2) { |
| throw new IllegalArgumentException("correction and uncertainty arrays must all " |
| + " have the same (greater than 2) number of columns"); |
| } |
| } |
| |
| mCorrections = corrections; |
| mCorrectionUncertainties = correctionUncertainties; |
| } |
| |
| private SphericalCorrections(Parcel in) { |
| int numRows = in.readInt(); |
| int numColumns = in.readInt(); |
| |
| double[][] corrections = |
| new double[numRows][numColumns]; |
| double[][] correctionUncertainties = |
| new double[numRows][numColumns]; |
| |
| for (int row = 0; row < numRows; row++) { |
| for (int col = 0; col < numColumns; col++) { |
| corrections[row][col] = in.readDouble(); |
| correctionUncertainties[row][col] = in.readDouble(); |
| } |
| } |
| |
| mNumRows = numRows; |
| mNumColumns = numColumns; |
| mCorrections = corrections; |
| mCorrectionUncertainties = correctionUncertainties; |
| } |
| |
| /** |
| * Array representing corrections on a spherical mapping. Corrections are added to |
| * measurements to obtain the corrected values. |
| * |
| * Each row (major indices) represents a fixed theta. The first row corresponds to a |
| * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) |
| * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., |
| * deltaTheta = 360 / (number of rows). |
| * |
| * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and |
| * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith |
| * angles, i.e., deltaPhi = 180 / (number of columns - 1). |
| */ |
| @NonNull |
| public double[][] getCorrectionsArray() { |
| return mCorrections; |
| } |
| |
| /** |
| * Array representing uncertainty on corrections on a spherical mapping. |
| * |
| * Each row (major indices) represents a fixed theta. The first row corresponds to a |
| * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) |
| * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., |
| * deltaTheta = 360 / (number of rows). |
| * |
| * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and |
| * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith |
| * angles, i.e., deltaPhi = 180 / (number of columns - 1). |
| */ |
| @NonNull |
| public double[][] getCorrectionUncertaintiesArray() { |
| return mCorrectionUncertainties; |
| } |
| |
| /** |
| * The fixed theta angle separation between successive rows. |
| */ |
| @FloatRange(from = 0.0f, to = 360.0f) |
| public double getDeltaTheta() { |
| return 360.0D / mNumRows; |
| } |
| |
| /** |
| * The fixed phi angle separation between successive columns. |
| */ |
| @FloatRange(from = 0.0f, to = 180.0f) |
| public double getDeltaPhi() { |
| return 180.0D / (mNumColumns - 1); |
| } |
| |
| |
| public static final @NonNull Creator<SphericalCorrections> CREATOR = |
| new Creator<SphericalCorrections>() { |
| @Override |
| public SphericalCorrections createFromParcel(Parcel in) { |
| return new SphericalCorrections(in); |
| } |
| |
| @Override |
| public SphericalCorrections[] newArray(int size) { |
| return new SphericalCorrections[size]; |
| } |
| }; |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeInt(mNumRows); |
| dest.writeInt(mNumColumns); |
| for (int row = 0; row < mNumRows; row++) { |
| for (int col = 0; col < mNumColumns; col++) { |
| dest.writeDouble(mCorrections[row][col]); |
| dest.writeDouble(mCorrectionUncertainties[row][col]); |
| } |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "SphericalCorrections{" |
| + "Corrections=" + Arrays.deepToString(mCorrections) |
| + ", CorrectionUncertainties=" + Arrays.deepToString(mCorrectionUncertainties) |
| + ", DeltaTheta=" + getDeltaTheta() |
| + ", DeltaPhi=" + getDeltaPhi() |
| + '}'; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof SphericalCorrections)) { |
| return false; |
| } |
| SphericalCorrections that = (SphericalCorrections) o; |
| return mNumRows == that.mNumRows |
| && mNumColumns == that.mNumColumns |
| && Arrays.deepEquals(mCorrections, that.mCorrections) |
| && Arrays.deepEquals(mCorrectionUncertainties, that.mCorrectionUncertainties); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = Arrays.deepHashCode(mCorrections); |
| result = 31 * result + Arrays.deepHashCode(mCorrectionUncertainties); |
| return result; |
| } |
| } |
| |
| private GnssAntennaInfo( |
| double carrierFrequencyMHz, |
| PhaseCenterOffset phaseCenterOffset, |
| @Nullable SphericalCorrections phaseCenterVariationCorrections, |
| @Nullable SphericalCorrections signalGainCorrectionDbi) { |
| mCarrierFrequencyMHz = carrierFrequencyMHz; |
| mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); |
| mPhaseCenterVariationCorrections = phaseCenterVariationCorrections; |
| mSignalGainCorrections = signalGainCorrectionDbi; |
| } |
| |
| /** |
| * Builder class for GnssAntennaInfo. |
| */ |
| public static class Builder { |
| private double mCarrierFrequencyMHz; |
| private PhaseCenterOffset mPhaseCenterOffset; |
| private @Nullable SphericalCorrections mPhaseCenterVariationCorrections; |
| private @Nullable SphericalCorrections mSignalGainCorrections; |
| |
| /** |
| * @deprecated Prefer {@link #Builder(double, PhaseCenterOffset)}. |
| */ |
| @Deprecated |
| public Builder() { |
| this(0, new PhaseCenterOffset(0, 0, 0, 0, 0, 0)); |
| } |
| |
| public Builder(double carrierFrequencyMHz, @NonNull PhaseCenterOffset phaseCenterOffset) { |
| mCarrierFrequencyMHz = carrierFrequencyMHz; |
| mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); |
| } |
| |
| public Builder(@NonNull GnssAntennaInfo antennaInfo) { |
| mCarrierFrequencyMHz = antennaInfo.mCarrierFrequencyMHz; |
| mPhaseCenterOffset = antennaInfo.mPhaseCenterOffset; |
| mPhaseCenterVariationCorrections = antennaInfo.mPhaseCenterVariationCorrections; |
| mSignalGainCorrections = antennaInfo.mSignalGainCorrections; |
| } |
| |
| /** |
| * Set antenna carrier frequency (MHz). |
| * |
| * @param carrierFrequencyMHz antenna carrier frequency (MHz) |
| * @return Builder builder object |
| */ |
| @NonNull |
| public Builder setCarrierFrequencyMHz(@FloatRange(from = 0.0f) double carrierFrequencyMHz) { |
| mCarrierFrequencyMHz = carrierFrequencyMHz; |
| return this; |
| } |
| |
| /** |
| * Set antenna phase center offset. |
| * |
| * @param phaseCenterOffset phase center offset object |
| * @return Builder builder object |
| */ |
| @NonNull |
| public Builder setPhaseCenterOffset(@NonNull PhaseCenterOffset phaseCenterOffset) { |
| mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset); |
| return this; |
| } |
| |
| /** |
| * Set phase center variation corrections. |
| * |
| * @param phaseCenterVariationCorrections phase center variation corrections object |
| * @return Builder builder object |
| */ |
| @NonNull |
| public Builder setPhaseCenterVariationCorrections( |
| @Nullable SphericalCorrections phaseCenterVariationCorrections) { |
| mPhaseCenterVariationCorrections = phaseCenterVariationCorrections; |
| return this; |
| } |
| |
| /** |
| * Set signal gain corrections. |
| * |
| * @param signalGainCorrections signal gain corrections object |
| * @return Builder builder object |
| */ |
| @NonNull |
| public Builder setSignalGainCorrections( |
| @Nullable SphericalCorrections signalGainCorrections) { |
| mSignalGainCorrections = signalGainCorrections; |
| return this; |
| } |
| |
| /** |
| * Build GnssAntennaInfo object. |
| * |
| * @return instance of GnssAntennaInfo |
| */ |
| @NonNull |
| public GnssAntennaInfo build() { |
| return new GnssAntennaInfo(mCarrierFrequencyMHz, mPhaseCenterOffset, |
| mPhaseCenterVariationCorrections, mSignalGainCorrections); |
| } |
| } |
| |
| @FloatRange(from = 0.0f) |
| public double getCarrierFrequencyMHz() { |
| return mCarrierFrequencyMHz; |
| } |
| |
| /** |
| * Returns a {@link PhaseCenterOffset} object encapsulating the phase center offset and |
| * corresponding uncertainties in millimeters. |
| * |
| * @return {@link PhaseCenterOffset} |
| */ |
| @NonNull |
| public PhaseCenterOffset getPhaseCenterOffset() { |
| return mPhaseCenterOffset; |
| } |
| |
| /** |
| * Returns a {@link SphericalCorrections} object encapsulating the phase center variation |
| * corrections and corresponding uncertainties in millimeters. |
| * |
| * @return phase center variation corrections as {@link SphericalCorrections} |
| */ |
| @Nullable |
| public SphericalCorrections getPhaseCenterVariationCorrections() { |
| return mPhaseCenterVariationCorrections; |
| } |
| |
| /** |
| * Returns a {@link SphericalCorrections} object encapsulating the signal gain |
| * corrections and corresponding uncertainties in dBi. |
| * |
| * @return signal gain corrections as {@link SphericalCorrections} |
| */ |
| @Nullable |
| public SphericalCorrections getSignalGainCorrections() { |
| return mSignalGainCorrections; |
| } |
| |
| public static final @NonNull Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() { |
| @Override |
| public GnssAntennaInfo createFromParcel(Parcel in) { |
| double carrierFrequencyMHz = in.readDouble(); |
| PhaseCenterOffset phaseCenterOffset = |
| in.readTypedObject(PhaseCenterOffset.CREATOR); |
| SphericalCorrections phaseCenterVariationCorrections = |
| in.readTypedObject(SphericalCorrections.CREATOR); |
| SphericalCorrections signalGainCorrections = |
| in.readTypedObject(SphericalCorrections.CREATOR); |
| |
| return new GnssAntennaInfo( |
| carrierFrequencyMHz, |
| phaseCenterOffset, |
| phaseCenterVariationCorrections, |
| signalGainCorrections); |
| } |
| |
| @Override |
| public GnssAntennaInfo[] newArray(int size) { |
| return new GnssAntennaInfo[size]; |
| } |
| }; |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel parcel, int flags) { |
| parcel.writeDouble(mCarrierFrequencyMHz); |
| parcel.writeTypedObject(mPhaseCenterOffset, flags); |
| parcel.writeTypedObject(mPhaseCenterVariationCorrections, flags); |
| parcel.writeTypedObject(mSignalGainCorrections, flags); |
| } |
| |
| @Override |
| public String toString() { |
| return "GnssAntennaInfo{" |
| + "CarrierFrequencyMHz=" + mCarrierFrequencyMHz |
| + ", PhaseCenterOffset=" + mPhaseCenterOffset |
| + ", PhaseCenterVariationCorrections=" + mPhaseCenterVariationCorrections |
| + ", SignalGainCorrections=" + mSignalGainCorrections |
| + '}'; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof GnssAntennaInfo)) { |
| return false; |
| } |
| GnssAntennaInfo that = (GnssAntennaInfo) o; |
| return Double.compare(that.mCarrierFrequencyMHz, mCarrierFrequencyMHz) == 0 |
| && mPhaseCenterOffset.equals(that.mPhaseCenterOffset) |
| && Objects.equals(mPhaseCenterVariationCorrections, |
| that.mPhaseCenterVariationCorrections) |
| && Objects.equals(mSignalGainCorrections, that.mSignalGainCorrections); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mCarrierFrequencyMHz, mPhaseCenterOffset, |
| mPhaseCenterVariationCorrections, mSignalGainCorrections); |
| } |
| } |