blob: f93a390e1c16475c3030491bbf12a67f83a659f6 [file] [log] [blame]
/*
* Copyright (C) 2017 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.cts.pseudorange;
import android.location.cts.pseudorange.Ecef2LlaConverter.GeodeticLlaValues;
import org.apache.commons.math.linear.RealMatrix;
/**
* Transformations from ECEF coordiantes to Topocentric coordinates
*/
public class EcefToTopocentricConverter {
private static final double MIN_DISTANCE_MAGNITUDE_METERS = 1.0e-22;
private static final int EAST_IDX = 0;
private static final int NORTH_IDX = 1;
private static final int UP_IDX = 2;
/**
* Transformation of {@code inputVectorMeters} with origin at {@code originECEFMeters} into
* topocentric coordinate system. The result is {@code TopocentricAEDValues} containing azimuth
* from north +ve clockwise, radians; elevation angle, radians; distance, vector length meters
*
* <p>Source: http://www.navipedia.net/index.php/Transformations_between_ECEF_and_ENU_coordinates
* http://kom.aau.dk/~borre/life-l99/topocent.m
*
*/
public static TopocentricAEDValues convertCartesianToTopocentericRadMeters(
final double[] originECEFMeters, final double[] inputVectorMeters) {
GeodeticLlaValues latLngAlt = Ecef2LlaConverter.convertECEFToLLACloseForm(originECEFMeters[0],
originECEFMeters[1], originECEFMeters[2]);
RealMatrix rotationMatrix =
Ecef2EnuConverter.
getRotationMatrix(latLngAlt.latitudeRadians, latLngAlt.longitudeRadians).transpose();
double[] eastNorthUpVectorMeters = GpsMathOperations.matrixByColVectMultiplication(
rotationMatrix.transpose().getData(), inputVectorMeters);
double eastMeters = eastNorthUpVectorMeters[EAST_IDX];
double northMeters = eastNorthUpVectorMeters[NORTH_IDX];
double upMeters = eastNorthUpVectorMeters[UP_IDX];
// calculate azimuth, elevation and height from the ENU values
double horizontalDistanceMeters = Math.hypot(eastMeters, northMeters);
double azimuthRadians;
double elevationRadians;
if (horizontalDistanceMeters < MIN_DISTANCE_MAGNITUDE_METERS) {
elevationRadians = Math.PI / 2.0;
azimuthRadians = 0;
} else {
elevationRadians = Math.atan2(upMeters, horizontalDistanceMeters);
azimuthRadians = Math.atan2(eastMeters, northMeters);
}
if (azimuthRadians < 0) {
azimuthRadians += 2 * Math.PI;
}
double distanceMeters = Math.sqrt(Math.pow(inputVectorMeters[0], 2)
+ Math.pow(inputVectorMeters[1], 2) + Math.pow(inputVectorMeters[2], 2));
return new TopocentricAEDValues(elevationRadians, azimuthRadians, distanceMeters);
}
/**
* Calculate azimuth, elevation in radians,and distance in meters between the user position in
* ECEF meters {@code userPositionECEFMeters} and the satellite position in ECEF meters
* {@code satPositionECEFMeters}
*/
public static TopocentricAEDValues calculateElAzDistBetween2Points(
double[] userPositionECEFMeters, double[] satPositionECEFMeters) {
return convertCartesianToTopocentericRadMeters(userPositionECEFMeters,
GpsMathOperations.subtractTwoVectors(satPositionECEFMeters, userPositionECEFMeters));
}
/**
*
* Class containing topocenter coordinates: azimuth in radians, elevation in radians, and distance
* in meters
*/
public static class TopocentricAEDValues {
public final double elevationRadians;
public final double azimuthRadians;
public final double distanceMeters;
public TopocentricAEDValues(double elevationRadians, double azimuthRadians,
double distanceMeters) {
this.elevationRadians = elevationRadians;
this.azimuthRadians = azimuthRadians;
this.distanceMeters = distanceMeters;
}
}
}