blob: 8c4b055ba7f8923b09529ebaa00a7b09b7781c0f [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.nano.Ephemeris.GpsEphemerisProto;
/**
* Calculate the GPS satellite clock correction based on parameters observed from the navigation
* message
* <p>Source: Page 88 - 90 of the ICD-GPS 200
*/
public class SatelliteClockCorrectionCalculator {
private static final double SPEED_OF_LIGHT_MPS = 299792458.0;
private static final double EARTH_UNIVERSAL_GRAVITATIONAL_CONSTANT_M3_SM2 = 3.986005e14;
private static final double RELATIVISTIC_CONSTANT_F = -4.442807633e-10;
private static final int SECONDS_IN_WEEK = 604800;
private static final double ACCURACY_TOLERANCE = 1.0e-11;
private static final int MAX_ITERATIONS = 100;
/**
* Compute the GPS satellite clock correction term in meters iteratively following page 88 - 90
* and 98 - 100 of the ICD GPS 200. The method returns a pair of satellite clock correction in
* meters and Kepler Eccentric Anomaly in Radians.
*
* @param ephemerisProto parameters of the navigation message
* @param receiverGpsTowAtTimeOfTransmission Reciever estimate of GPS time of week when signal was
* transmitted (seconds)
* @param receiverGpsWeekAtTimeOfTrasnmission Receiver estimate of GPS week when signal was
* transmitted (0-1024+)
* @throws Exception
*/
public static SatClockCorrection calculateSatClockCorrAndEccAnomAndTkIteratively(
GpsEphemerisProto ephemerisProto, double receiverGpsTowAtTimeOfTransmission,
double receiverGpsWeekAtTimeOfTrasnmission) throws Exception {
// Units are not added in the variable names to have the same name as the ICD-GPS200
// Mean anomaly (radians)
double meanAnomalyRad;
// Kepler's Equation for Eccentric Anomaly iteratively (Radians)
double eccentricAnomalyRad;
// Semi-major axis of orbit (meters)
double a = ephemerisProto.rootOfA * ephemerisProto.rootOfA;
// Computed mean motion (radians/seconds)
double n0 = Math.sqrt(EARTH_UNIVERSAL_GRAVITATIONAL_CONSTANT_M3_SM2 / (a * a * a));
// Corrected mean motion (radians/seconds)
double n = n0 + ephemerisProto.deltaN;
// In the following, Receiver GPS week and ephemeris GPS week are used to correct for week
// rollover when calculating the time from clock reference epoch (tcSec)
double timeOfTransmissionIncludingRxWeekSec =
receiverGpsWeekAtTimeOfTrasnmission * SECONDS_IN_WEEK + receiverGpsTowAtTimeOfTransmission;
// time from clock reference epoch (seconds) page 88 ICD-GPS200
double tcSec = timeOfTransmissionIncludingRxWeekSec
- (ephemerisProto.week * SECONDS_IN_WEEK + ephemerisProto.toc);
// Correction for week rollover
tcSec = fixWeekRollover(tcSec);
double oldEcentricAnomalyRad = 0.0;
double newSatClockCorrectionSeconds = 0.0;
double relativisticCorrection = 0.0;
double changeInSatClockCorrection = 0.0;
// Initial satellite clock correction (unknown relativistic correction). Iterate to correct
// with the relativistic effect and obtain a stable
final double initSatClockCorrectionSeconds = ephemerisProto.af0
+ ephemerisProto.af1 * tcSec
+ ephemerisProto.af2 * tcSec * tcSec - ephemerisProto.tgd;
double satClockCorrectionSeconds = initSatClockCorrectionSeconds;
double tkSec;
int satClockCorrectionsCounter = 0;
do {
int eccentricAnomalyCounter = 0;
// time from ephemeris reference epoch (seconds) page 98 ICD-GPS200
tkSec = timeOfTransmissionIncludingRxWeekSec - (
ephemerisProto.week * SECONDS_IN_WEEK + ephemerisProto.toe
+ satClockCorrectionSeconds);
// Correction for week rollover
tkSec = fixWeekRollover(tkSec);
// Mean anomaly (radians)
meanAnomalyRad = ephemerisProto.m0 + n * tkSec;
// eccentric anomaly (radians)
eccentricAnomalyRad = meanAnomalyRad;
// Iteratively solve for Kepler's eccentric anomaly according to ICD-GPS200 page 99
do {
oldEcentricAnomalyRad = eccentricAnomalyRad;
eccentricAnomalyRad =
meanAnomalyRad + ephemerisProto.e * Math.sin(eccentricAnomalyRad);
eccentricAnomalyCounter++;
if (eccentricAnomalyCounter > MAX_ITERATIONS) {
throw new Exception("Kepler Eccentric Anomaly calculation did not converge in "
+ MAX_ITERATIONS + " iterations");
}
} while (Math.abs(oldEcentricAnomalyRad - eccentricAnomalyRad) > ACCURACY_TOLERANCE);
// relativistic correction term (seconds)
relativisticCorrection = RELATIVISTIC_CONSTANT_F * ephemerisProto.e
* ephemerisProto.rootOfA * Math.sin(eccentricAnomalyRad);
// satellite clock correction including relativistic effect
newSatClockCorrectionSeconds = initSatClockCorrectionSeconds + relativisticCorrection;
changeInSatClockCorrection =
Math.abs(satClockCorrectionSeconds - newSatClockCorrectionSeconds);
satClockCorrectionSeconds = newSatClockCorrectionSeconds;
satClockCorrectionsCounter++;
if (satClockCorrectionsCounter > MAX_ITERATIONS) {
throw new Exception("Satellite Clock Correction calculation did not converge in "
+ MAX_ITERATIONS + " iterations");
}
} while (changeInSatClockCorrection > ACCURACY_TOLERANCE);
tkSec = timeOfTransmissionIncludingRxWeekSec - (
ephemerisProto.week * SECONDS_IN_WEEK + ephemerisProto.toe
+ satClockCorrectionSeconds);
// return satellite clock correction (meters) and Kepler Eccentric Anomaly in Radians
return new SatClockCorrection(satClockCorrectionSeconds * SPEED_OF_LIGHT_MPS,
eccentricAnomalyRad, tkSec);
}
/**
* Calculates Satellite Clock Error Rate in (meters/second) by subtracting the Satellite
* Clock Error Values at t+0.5s and t-0.5s.
*
* <p>This approximation is more accurate than differentiating because both the orbital
* and relativity terms have non-linearities that are not easily differentiable.
*/
public static double calculateSatClockCorrErrorRate(
GpsEphemerisProto ephemerisProto, double receiverGpsTowAtTimeOfTransmissionSeconds,
double receiverGpsWeekAtTimeOfTrasnmission) throws Exception {
SatClockCorrection satClockCorrectionPlus = calculateSatClockCorrAndEccAnomAndTkIteratively(
ephemerisProto, receiverGpsTowAtTimeOfTransmissionSeconds + 0.5,
receiverGpsWeekAtTimeOfTrasnmission);
SatClockCorrection satClockCorrectionMinus = calculateSatClockCorrAndEccAnomAndTkIteratively(
ephemerisProto, receiverGpsTowAtTimeOfTransmissionSeconds - 0.5,
receiverGpsWeekAtTimeOfTrasnmission);
double satelliteClockErrorRate = satClockCorrectionPlus.satelliteClockCorrectionMeters
- satClockCorrectionMinus.satelliteClockCorrectionMeters;
return satelliteClockErrorRate;
}
/**
* Method to check for week rollover according to ICD-GPS 200 page 98.
*
* <p>Result should be between -302400 and 302400 if the ephemeris is within one week of
* transmission, otherwise it is adjusted to the correct range
*/
private static double fixWeekRollover(double time) {
double correctedTime = time;
if (time > SECONDS_IN_WEEK / 2.0) {
correctedTime = time - SECONDS_IN_WEEK;
}
if (time < -SECONDS_IN_WEEK / 2.0) {
correctedTime = time + SECONDS_IN_WEEK;
}
return correctedTime;
}
/**
*
* Class containing the satellite clock correction parameters: The satellite clock correction in
* meters, Kepler Eccentric Anomaly in Radians and the time from the reference epoch in seconds.
*/
public static class SatClockCorrection {
/**
* Satellite clock correction in meters
*/
public final double satelliteClockCorrectionMeters;
/**
* Satellite clock correction in meters
*/
public final double satelliteClockRateCorrectionMetersPerSecond;
/**
* Kepler Eccentric Anomaly in Radians
*/
public final double eccentricAnomalyRadians;
/**
* Time from the reference epoch in Seconds
*/
public final double timeFromRefEpochSec;
/**
* Constructor
*/
public SatClockCorrection(double satelliteClockCorrectionMeters, double eccentricAnomalyRadians,
double timeFromRefEpochSec) {
this.satelliteClockCorrectionMeters = satelliteClockCorrectionMeters;
this.eccentricAnomalyRadians = eccentricAnomalyRadians;
this.timeFromRefEpochSec = timeFromRefEpochSec;
this.satelliteClockRateCorrectionMetersPerSecond = Double.NaN;
}
/**
* Alternative Constructor
*/
public SatClockCorrection(double satelliteClockCorrectionMeters,
double satelliteClockRateCorrectionMeters, double eccentricAnomalyRadians,
double timeFromRefEpochSec){
this.satelliteClockCorrectionMeters = satelliteClockCorrectionMeters;
this.eccentricAnomalyRadians = eccentricAnomalyRadians;
this.timeFromRefEpochSec = timeFromRefEpochSec;
this.satelliteClockRateCorrectionMetersPerSecond = satelliteClockRateCorrectionMeters;
}
}
}