blob: 9199c756d26413b89751397325068cd1bf222d6d [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.gnss.pseudorange;
import android.location.cts.gnss.pseudorange.SatelliteClockCorrectionCalculator.SatClockCorrection;
import android.location.cts.gnss.nano.Ephemeris.GpsEphemerisProto;
/* Class to calculate GPS satellite positions from the ephemeris data */
public class SatellitePositionCalculator {
private static final double SPEED_OF_LIGHT_MPS = 299792458.0;
private static final double UNIVERSAL_GRAVITATIONAL_PARAMETER_M3_SM2 = 3.986005e14;
private static final int NUMBER_OF_ITERATIONS_FOR_SAT_POS_CALCULATION = 5;
private static final double EARTH_ROTATION_RATE_RAD_PER_SEC = 7.2921151467e-5;
/**
*
* Calculate GPS satellite position and velocity from ephemeris including the Sagnac effect
* starting from unknown user to satellite distance and speed. So we start from an initial guess
* of the user to satellite range and range rate and iterate to include the Sagnac effect. Few
* iterations are enough to achieve a satellite position with millimeter accuracy.
* A {@code PositionAndVelocity} class is returned containing satellite position in meters
* (x, y and z) and velocity in meters per second (x, y, z)
*
* <p>Satelite position and velocity equations are obtained from:
* http://www.gps.gov/technical/icwg/ICD-GPS-200C.pdf) pages 94 - 101 and
* http://fenrir.naruoka.org/download/autopilot/note/080205_gps/gps_velocity.pdf
*
* @param ephemerisProto parameters of the navigation message
* @param receiverGpsTowAtTimeOfTransmissionCorrectedSec Receiver estimate of GPS time of week
* when signal was transmitted corrected with the satellite clock drift (seconds)
* @param receiverGpsWeekAtTimeOfTransmission Receiver estimate of GPS week when signal was
* transmitted (0-1024+)
* @param userPosXMeters Last known user x-position (if known) [meters]
* @param userPosYMeters Last known user y-position (if known) [meters]
* @param userPosZMeters Last known user z-position (if known) [meters]
* @throws Exception
*/
public static PositionAndVelocity calculateSatellitePositionAndVelocityFromEphemeris
(GpsEphemerisProto ephemerisProto, double receiverGpsTowAtTimeOfTransmissionCorrectedSec,
int receiverGpsWeekAtTimeOfTransmission,
double userPosXMeters,
double userPosYMeters,
double userPosZMeters) throws Exception {
// lets start with a first user to sat distance guess of 70 ms and zero velocity
RangeAndRangeRate userSatRangeAndRate = new RangeAndRangeRate
(0.070 * SPEED_OF_LIGHT_MPS, 0.0 /* range rate*/);
// To apply sagnac effect correction, We are starting from an approximate guess of the user to
// satellite range, iterate 3 times and that should be enough to reach millimeter accuracy
PositionAndVelocity satPosAndVel = new PositionAndVelocity(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
PositionAndVelocity userPosAndVel =
new PositionAndVelocity(userPosXMeters, userPosYMeters, userPosZMeters,
0.0 /* user velocity x*/, 0.0 /* user velocity y*/, 0.0 /* user velocity z */);
for (int i = 0; i < NUMBER_OF_ITERATIONS_FOR_SAT_POS_CALCULATION; i++) {
calculateSatellitePositionAndVelocity(ephemerisProto,
receiverGpsTowAtTimeOfTransmissionCorrectedSec, receiverGpsWeekAtTimeOfTransmission,
userSatRangeAndRate, satPosAndVel);
computeUserToSatelliteRangeAndRangeRate(userPosAndVel, satPosAndVel, userSatRangeAndRate);
}
return satPosAndVel;
}
/**
* Calculate GPS satellite position and velocity from ephemeris based on the ICD-GPS-200.
* Satellite position in meters (x, y and z) and velocity in meters per second (x, y, z) are set
* in the passed {@code PositionAndVelocity} instance.
*
* <p>Sources: http://www.gps.gov/technical/icwg/ICD-GPS-200C.pdf) pages 94 - 101 and
* http://fenrir.naruoka.org/download/autopilot/note/080205_gps/gps_velocity.pdf
*
* @param ephemerisProto parameters of the navigation message
* @param receiverGpsTowAtTimeOfTransmissionCorrected Receiver estimate of GPS time of week when
* signal was transmitted corrected with the satellite clock drift (seconds)
* @param receiverGpsWeekAtTimeOfTransmission Receiver estimate of GPS week when signal was
* transmitted (0-1024+)
* @param userSatRangeAndRate user to satellite range and range rate
* @param satPosAndVel Satellite position and velocity instance in which the method results will
* be set
* @throws Exception
*/
public static void calculateSatellitePositionAndVelocity(GpsEphemerisProto ephemerisProto,
double receiverGpsTowAtTimeOfTransmissionCorrected, int receiverGpsWeekAtTimeOfTransmission,
RangeAndRangeRate userSatRangeAndRate, PositionAndVelocity satPosAndVel) throws Exception {
// Calculate satellite clock correction (meters), Kepler Eccentric anomaly (radians) and time
// from ephemeris refrence epoch (tkSec) iteratively
SatClockCorrection satClockCorrectionValues =
SatelliteClockCorrectionCalculator.calculateSatClockCorrAndEccAnomAndTkIteratively(
ephemerisProto, receiverGpsTowAtTimeOfTransmissionCorrected,
receiverGpsWeekAtTimeOfTransmission);
double eccentricAnomalyRadians = satClockCorrectionValues.eccentricAnomalyRadians;
double tkSec = satClockCorrectionValues.timeFromRefEpochSec;
// True_anomaly (angle from perigee)
double trueAnomalyRadians = Math.atan2(
Math.sqrt(1.0 - ephemerisProto.e * ephemerisProto.e)
* Math.sin(eccentricAnomalyRadians),
Math.cos(eccentricAnomalyRadians) - ephemerisProto.e);
// Argument of latitude of the satellite
double argumentOfLatitudeRadians = trueAnomalyRadians + ephemerisProto.omega;
// Radius of satellite orbit
double radiusOfSatelliteOrbitMeters = ephemerisProto.rootOfA * ephemerisProto.rootOfA
* (1.0 - ephemerisProto.e * Math.cos(eccentricAnomalyRadians));
// Radius correction due to second harmonic perturbations of the orbit
double radiusCorrectionMeters = ephemerisProto.crc
* Math.cos(2.0 * argumentOfLatitudeRadians) + ephemerisProto.crs
* Math.sin(2.0 * argumentOfLatitudeRadians);
// Argument of latitude correction due to second harmonic perturbations of the orbit
double argumentOfLatitudeCorrectionRadians = ephemerisProto.cuc
* Math.cos(2.0 * argumentOfLatitudeRadians) + ephemerisProto.cus
* Math.sin(2.0 * argumentOfLatitudeRadians);
// Correction to inclination due to second harmonic perturbations of the orbit
double inclinationCorrectionRadians = ephemerisProto.cic
* Math.cos(2.0 * argumentOfLatitudeRadians) + ephemerisProto.cis
* Math.sin(2.0 * argumentOfLatitudeRadians);
// Corrected radius of satellite orbit
radiusOfSatelliteOrbitMeters += radiusCorrectionMeters;
// Corrected argument of latitude
argumentOfLatitudeRadians += argumentOfLatitudeCorrectionRadians;
// Corrected inclination
double inclinationRadians =
ephemerisProto.i0 + inclinationCorrectionRadians + ephemerisProto.iDot * tkSec;
// Position in orbital plane
double xPositionMeters = radiusOfSatelliteOrbitMeters * Math.cos(argumentOfLatitudeRadians);
double yPositionMeters = radiusOfSatelliteOrbitMeters * Math.sin(argumentOfLatitudeRadians);
// Corrected longitude of the ascending node (signal propagation time is included to compensate
// for the Sagnac effect)
double omegaKRadians = ephemerisProto.omega0
+ (ephemerisProto.omegaDot - EARTH_ROTATION_RATE_RAD_PER_SEC) * tkSec
- EARTH_ROTATION_RATE_RAD_PER_SEC
* (ephemerisProto.toe + userSatRangeAndRate.rangeMeters / SPEED_OF_LIGHT_MPS);
// compute the resulting satellite position
double satPosXMeters = xPositionMeters * Math.cos(omegaKRadians) - yPositionMeters
* Math.cos(inclinationRadians) * Math.sin(omegaKRadians);
double satPosYMeters = xPositionMeters * Math.sin(omegaKRadians) + yPositionMeters
* Math.cos(inclinationRadians) * Math.cos(omegaKRadians);
double satPosZMeters = yPositionMeters * Math.sin(inclinationRadians);
// Satellite Velocity Computation using the broadcast ephemeris
// http://fenrir.naruoka.org/download/autopilot/note/080205_gps/gps_velocity.pdf
// Units are not added in some of the variable names to have the same name as the ICD-GPS200
// Semi-major axis of orbit (meters)
double a = ephemerisProto.rootOfA * ephemerisProto.rootOfA;
// Computed mean motion (radians/seconds)
double n0 = Math.sqrt(UNIVERSAL_GRAVITATIONAL_PARAMETER_M3_SM2 / (a * a * a));
// Corrected mean motion (radians/seconds)
double n = n0 + ephemerisProto.deltaN;
// Derivative of mean anomaly (radians/seconds)
double meanAnomalyDotRadPerSec = n;
// Derivative of eccentric anomaly (radians/seconds)
double eccentricAnomalyDotRadPerSec =
meanAnomalyDotRadPerSec / (1.0 - ephemerisProto.e * Math.cos(eccentricAnomalyRadians));
// Derivative of true anomaly (radians/seconds)
double trueAnomalydotRadPerSec = Math.sin(eccentricAnomalyRadians)
* eccentricAnomalyDotRadPerSec
* (1.0 + ephemerisProto.e * Math.cos(trueAnomalyRadians)) / (
Math.sin(trueAnomalyRadians)
* (1.0 - ephemerisProto.e * Math.cos(eccentricAnomalyRadians)));
// Derivative of argument of latitude (radians/seconds)
double argumentOfLatitudeDotRadPerSec = trueAnomalydotRadPerSec + 2.0 * (ephemerisProto.cus
* Math.cos(2.0 * argumentOfLatitudeRadians) - ephemerisProto.cuc
* Math.sin(2.0 * argumentOfLatitudeRadians)) * trueAnomalydotRadPerSec;
// Derivative of radius of satellite orbit (m/s)
double radiusOfSatelliteOrbitDotMPerSec = a * ephemerisProto.e
* Math.sin(eccentricAnomalyRadians) * n
/ (1.0 - ephemerisProto.e * Math.cos(eccentricAnomalyRadians)) + 2.0 * (
ephemerisProto.crs * Math.cos(2.0 * argumentOfLatitudeRadians)
- ephemerisProto.crc * Math.sin(2.0 * argumentOfLatitudeRadians))
* trueAnomalydotRadPerSec;
// Derivative of the inclination (radians/seconds)
double inclinationDotRadPerSec = ephemerisProto.iDot + (ephemerisProto.cis
* Math.cos(2.0 * argumentOfLatitudeRadians) - ephemerisProto.cic
* Math.sin(2.0 * argumentOfLatitudeRadians)) * 2.0 * trueAnomalydotRadPerSec;
double xVelocityMPS = radiusOfSatelliteOrbitDotMPerSec * Math.cos(argumentOfLatitudeRadians)
- yPositionMeters * argumentOfLatitudeDotRadPerSec;
double yVelocityMPS = radiusOfSatelliteOrbitDotMPerSec * Math.sin(argumentOfLatitudeRadians)
+ xPositionMeters * argumentOfLatitudeDotRadPerSec;
// Corrected rate of right ascension including compensation for the Sagnac effect
double omegaDotRadPerSec = ephemerisProto.omegaDot - EARTH_ROTATION_RATE_RAD_PER_SEC
* (1.0 + userSatRangeAndRate.rangeRateMetersPerSec / SPEED_OF_LIGHT_MPS);
// compute the resulting satellite velocity
double satVelXMPS =
(xVelocityMPS - yPositionMeters * Math.cos(inclinationRadians) * omegaDotRadPerSec)
* Math.cos(omegaKRadians) - (xPositionMeters * omegaDotRadPerSec + yVelocityMPS
* Math.cos(inclinationRadians) - yPositionMeters * Math.sin(inclinationRadians)
* inclinationDotRadPerSec) * Math.sin(omegaKRadians);
double satVelYMPS =
(xVelocityMPS - yPositionMeters * Math.cos(inclinationRadians) * omegaDotRadPerSec)
* Math.sin(omegaKRadians) + (xPositionMeters * omegaDotRadPerSec + yVelocityMPS
* Math.cos(inclinationRadians) - yPositionMeters * Math.sin(inclinationRadians)
* inclinationDotRadPerSec) * Math.cos(omegaKRadians);
double satVelZMPS = yVelocityMPS * Math.sin(inclinationRadians) + yPositionMeters
* Math.cos(inclinationRadians) * inclinationDotRadPerSec;
satPosAndVel.positionXMeters = satPosXMeters;
satPosAndVel.positionYMeters = satPosYMeters;
satPosAndVel.positionZMeters = satPosZMeters;
satPosAndVel.velocityXMetersPerSec = satVelXMPS;
satPosAndVel.velocityYMetersPerSec = satVelYMPS;
satPosAndVel.velocityZMetersPerSec = satVelZMPS;
}
/**
* Compute and set the passed {@code RangeAndRangeRate} instance containing user to satellite
* range (meters) and range rate (m/s) given the user position (ECEF meters), user velocity (m/s),
* satellite position (ECEF meters) and satellite velocity (m/s).
*/
private static void computeUserToSatelliteRangeAndRangeRate(PositionAndVelocity userPosAndVel,
PositionAndVelocity satPosAndVel, RangeAndRangeRate rangeAndRangeRate) {
double dXMeters = satPosAndVel.positionXMeters - userPosAndVel.positionXMeters;
double dYMeters = satPosAndVel.positionYMeters - userPosAndVel.positionYMeters;
double dZMeters = satPosAndVel.positionZMeters - userPosAndVel.positionZMeters;
// range in meters
double rangeMeters = Math.sqrt(dXMeters * dXMeters + dYMeters * dYMeters + dZMeters * dZMeters);
// range rate in meters / second
double rangeRateMetersPerSec =
((userPosAndVel.velocityXMetersPerSec - satPosAndVel.velocityXMetersPerSec) * dXMeters
+ (userPosAndVel.velocityYMetersPerSec - satPosAndVel.velocityYMetersPerSec) * dYMeters
+ (userPosAndVel.velocityZMetersPerSec - satPosAndVel.velocityZMetersPerSec) * dZMeters)
/ rangeMeters;
rangeAndRangeRate.rangeMeters = rangeMeters;
rangeAndRangeRate.rangeRateMetersPerSec = rangeRateMetersPerSec;
}
/**
*
* A class containing position values (x, y, z) in meters and velocity values (x, y, z) in meters
* per seconds
*/
public static class PositionAndVelocity {
/* x - position in meters */
public double positionXMeters;
/* y - position in meters */
public double positionYMeters;
/* z - position in meters */
public double positionZMeters;
/* x - velocity in meters */
public double velocityXMetersPerSec;
/* y - velocity in meters */
public double velocityYMetersPerSec;
/* z - velocity in meters */
public double velocityZMetersPerSec;
/* Constructor */
public PositionAndVelocity(double positionXMeters,
double positionYMeters,
double positionZMeters,
double velocityXMetersPerSec,
double velocityYMetersPerSec,
double velocityZMetersPerSec) {
this.positionXMeters = positionXMeters;
this.positionYMeters = positionYMeters;
this.positionZMeters = positionZMeters;
this.velocityXMetersPerSec = velocityXMetersPerSec;
this.velocityYMetersPerSec = velocityYMetersPerSec;
this.velocityZMetersPerSec = velocityZMetersPerSec;
}
}
/* A class containing range of satellite to user in meters and range rate in meters per seconds */
public static class RangeAndRangeRate {
/* Range in meters */
public double rangeMeters;
/* Range rate in meters per seconds */
public double rangeRateMetersPerSec;
/* Constructor */
public RangeAndRangeRate(double rangeMeters, double rangeRateMetersPerSec) {
this.rangeMeters = rangeMeters;
this.rangeRateMetersPerSec = rangeRateMetersPerSec;
}
}
}