589 lines
22 KiB
Java
589 lines
22 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|