| /* | |
| * 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); | |
| } | |
| } |