From 225bb703318e62088c10967e6b2a2c31b3810d93 Mon Sep 17 00:00:00 2001
From: Luc Maisonobe <luc@orekit.org>
Date: Thu, 17 Jan 2019 17:19:55 +0100
Subject: [PATCH] Added a way to manage clock corrections from GPSPropagator.

---
 src/main/java/org/orekit/gnss/GPSAlmanac.java |  12 +-
 .../gnss/ClockCorrectionsProvider.java        | 115 ++++++++++++++++++
 .../analytical/gnss/GPSOrbitalElements.java   |  82 +++++++++++++
 src/site/xdoc/changes.xml                     |  15 ++-
 .../gnss/GPSOrbitalElementsTest.java          |  60 +++++++++
 .../analytical/gnss/GPSPropagatorTest.java    |  23 ++++
 6 files changed, 296 insertions(+), 11 deletions(-)
 create mode 100644 src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java
 create mode 100644 src/test/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElementsTest.java

diff --git a/src/main/java/org/orekit/gnss/GPSAlmanac.java b/src/main/java/org/orekit/gnss/GPSAlmanac.java
index b9b17f799..601421121 100644
--- a/src/main/java/org/orekit/gnss/GPSAlmanac.java
+++ b/src/main/java/org/orekit/gnss/GPSAlmanac.java
@@ -232,20 +232,12 @@ public class GPSAlmanac implements GPSOrbitalElements {
         return 0;
     }
 
-    /**
-     * Gets the Zeroth Order Clock Correction.
-     *
-     * @return the Zeroth Order Clock Correction (s)
-     */
+    @Override
     public double getAf0() {
         return af0;
     }
 
-    /**
-     * Gets the First Order Clock Correction.
-     *
-     * @return the First Order Clock Correction (s/s)
-     */
+    @Override
     public double getAf1() {
         return af1;
     }
diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java b/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java
new file mode 100644
index 000000000..f92325a3c
--- /dev/null
+++ b/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java
@@ -0,0 +1,115 @@
+/* Copyright 2002-2019 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You 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 org.orekit.propagation.analytical.gnss;
+
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.orekit.propagation.AdditionalStateProvider;
+import org.orekit.propagation.SpacecraftState;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.GPSDate;
+import org.orekit.utils.Constants;
+import org.orekit.utils.PVCoordinates;
+
+/** Provider for clock corrections as additional states.
+ * <p>
+ * The value of this additional state is a three elements array containing
+ * </p>
+ * <ul>
+ *   <li>at index 0, the polynomial satellite clock model
+ *       Δtₛₐₜ = {@link GPSOrbitalElements#getAf0() a₀} +
+ *               {@link GPSOrbitalElements#getAf1() a₁} (t - {@link GPSOrbitalElements#getToc() toc}) +
+ *               {@link GPSOrbitalElements#getAf1() a₂} (t - {@link GPSOrbitalElements#getToc() toc})²
+ *   </li>
+ *   <li>at index 1 the relativistic clock correction due to eccentricity</li>
+ *   <li>at index 2 the estimated group delay differential {@link GPSOrbitalElements#getTGD() TGD} for L1-L2 correction</li>
+ * </ul>
+ * @author Luc Maisonobe
+ * @since 9.3
+ */
+public class ClockCorrectionsProvider implements AdditionalStateProvider {
+
+    /** Name of the additional state for satellite clock corrections.
+     * @since 9.3
+     */
+    public static final String CLOCK_CORRECTIONS = "";
+
+    /** Duration of the GPS cycle in seconds. */
+    private static final double GPS_CYCLE_DURATION = GPSOrbitalElements.GPS_WEEK_IN_SECONDS *
+                                                     GPSOrbitalElements.GPS_WEEK_NB;
+    /** The GPS orbital elements. */
+    private final GPSOrbitalElements gpsOrbit;
+
+    /** Clock reference epoch. */
+    private final AbsoluteDate clockRef;
+
+    /** Simple constructor.
+     * @param gpsOrbit GPS orbital elements
+     */
+    public ClockCorrectionsProvider(final GPSOrbitalElements gpsOrbit) {
+        this.gpsOrbit = gpsOrbit;
+        this.clockRef = new GPSDate(gpsOrbit.getWeek(), gpsOrbit.getToc() * 1000.0).getDate();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getName() {
+        return CLOCK_CORRECTIONS;
+    }
+
+    /**
+     * Get the duration from clock Reference epoch.
+     * <p>This takes the GPS week roll-over into account.</p>
+     *
+     * @param date the considered date
+     * @return the duration from clock Reference epoch (s)
+     */
+    private double getDT(final AbsoluteDate date) {
+        // Time from ephemeris reference epoch
+        double dt = date.durationFrom(clockRef);
+        // Adjusts the time to take roll over week into account
+        while (dt > 0.5 * GPS_CYCLE_DURATION) {
+            dt -= GPS_CYCLE_DURATION;
+        }
+        while (dt < -0.5 * GPS_CYCLE_DURATION) {
+            dt += GPS_CYCLE_DURATION;
+        }
+        // Returns the time from ephemeris reference epoch
+        return dt;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double[] getAdditionalState(final SpacecraftState state) {
+
+        // polynomial clock model
+        final double  dt    = getDT(state.getDate());
+        final double  dtSat = gpsOrbit.getAf0() + dt * (gpsOrbit.getAf1() + dt * gpsOrbit.getAf2());
+
+        // relativistic effect due to eccentricity
+        final PVCoordinates pv    = state.getPVCoordinates();
+        final double        dtRel = -2 * Vector3D.dotProduct(pv.getPosition(), pv.getVelocity()) /
+                        (Constants.SPEED_OF_LIGHT * Constants.SPEED_OF_LIGHT);
+
+        // estimated group delay differential
+        final double tg = gpsOrbit.getTGD();
+
+        return new double[] {
+            dtSat, dtRel, tg
+        };
+    }
+
+}
diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElements.java b/src/main/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElements.java
index 65c5484f7..ff6e1807f 100644
--- a/src/main/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElements.java
+++ b/src/main/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElements.java
@@ -167,4 +167,86 @@ public interface GPSOrbitalElements extends TimeStamped {
      */
     double getCis();
 
+    /**
+     * Gets the Issue Of Data Clock (IODC).
+     *
+     * @return the Issue Of Data Clock (IODC)
+     * @since 9.3
+     */
+    default int getIODC() {
+        return 0;
+    }
+
+    /**
+     * Gets the Issue Of Data Ephemeris (IODE).
+     *
+     * @return the Issue Of Data Ephemeris (IODE)
+     * @since 9.3
+     */
+    default int getIODE() {
+        return 0;
+    }
+
+    /**
+     * Gets the Zeroth Order Clock Correction.
+     *
+     * @return the Zeroth Order Clock Correction (s)
+     * @see #getAf1()
+     * @see #getAf2()
+     * @see #getToc()
+     * @since 9.3
+     */
+    default double getAf0() {
+        return 0.0;
+    }
+
+    /**
+     * Gets the First Order Clock Correction.
+     *
+     * @return the First Order Clock Correction (s/s)
+     * @see #getAf0()
+     * @see #getAf2()
+     * @see #getToc()
+     * @since 9.3
+     */
+    default double getAf1() {
+        return 0.0;
+    }
+
+    /**
+     * Gets the Second Order Clock Correction.
+     *
+     * @return the Second Order Clock Correction (s/s²)
+     * @see #getAf0()
+     * @see #getAf1()
+     * @see #getToc()
+     * @since 9.3
+     */
+    default double getAf2() {
+        return 0.0;
+    }
+
+    /**
+     * Gets the clock correction reference time toc.
+     *
+     * @return the clock correction reference time (s)
+     * @see #getAf0()
+     * @see #getAf1()
+     * @see #getAf2()
+     * @since 9.3
+     */
+    default double getToc() {
+        return 0.0;
+    }
+
+    /**
+     * Gets the estimated group delay differential TGD for L1-L2 correction.
+     *
+     * @return the estimated group delay differential TGD for L1-L2 correction (s)
+     * @since 9.3
+     */
+    default double getTGD() {
+        return 0.0;
+    }
+
 }
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index 7f6c5d4ef..bc40741a9 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -20,7 +20,20 @@
     <title>Orekit Changes</title>
   </properties>
   <body>
-    <release version="TBD" date="TBD" description="TBD">
+    <release version="9.3" date="2019-01-21" description="Version 9.3 is a minor version of Orekit.
+    It includes both new features and bug fixes. New features introduced in 9.3 are: a new GPSDate class,
+    changed OrekitException from checked to unchecked exceptions, parameter drivers scales and reference
+    value can be changed, access to Kalman filter internal matrices, position-only measurements in orbit determination,
+    support for unofficial versions 2.12 and 2.20 of Rinex files (mainly for spaceborne receivers),
+    direct building of appropriate attitude law with eclipses for all GNSS satellite types, nter-satellites
+    view detector, measurement generation feature, possibility fo use Marshall Solar Activity Future Estimation
+    to feed NRL MSISE 2000 atmosphere model, new tropospheric models: Mendes-Pavlis, Vienna 1, Vienna 3, estimated model,
+    new mapping functions for tropospheric effect: Global Mapping Function, Niell Mapping Function, Global
+    Pression Temperature Models GPT and GPT2, possibility to estimate tropospheric zenith delay,
+    clock offset that can be estimated (both for ground station and satellite clocks).">
+      <action dev="luc" type="add" issue="516">
+        Added a way to manage clock corrections from GPSPropagator.
+      </action>
       <action dev="bryan" type="add" issue="498">
         Added several tropospheric models: Mendes-Pavlis, Vienna 1, Vienna 3, estimated model
         where the total zenith delay can be estimated during Orbit Determination.
diff --git a/src/test/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElementsTest.java b/src/test/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElementsTest.java
new file mode 100644
index 000000000..cd76abfaf
--- /dev/null
+++ b/src/test/java/org/orekit/propagation/analytical/gnss/GPSOrbitalElementsTest.java
@@ -0,0 +1,60 @@
+/* Copyright 2002-2019 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You 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 org.orekit.propagation.analytical.gnss;
+
+
+import org.hipparchus.util.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+import org.orekit.time.AbsoluteDate;
+
+public class GPSOrbitalElementsTest {
+
+    @Test
+    public void testDefaultMethods() {
+        GPSOrbitalElements goe = new GPSOrbitalElements() {
+            public AbsoluteDate getDate() { return null; }
+            public int    getWeek()       { return 0; }
+            public double getTime()       { return 0; }
+            public double getSma()        { return 0; }
+            public double getPa()         { return 0; }
+            public int    getPRN()        { return 0; }
+            public double getOmegaDot()   { return 0; }
+            public double getOmega0()     { return 0; }
+            public double getMeanMotion() { return 0; }
+            public double getM0()         { return 0; }
+            public double getIDot()       { return 0; }
+            public double getI0()         { return 0; }
+            public double getE()          { return 0; }
+            public double getCus()        { return 0; }
+            public double getCuc()        { return 0; }
+            public double getCrs()        { return 0; }
+            public double getCrc()        { return 0; }
+            public double getCis()        { return 0; }
+            public double getCic()        { return 0; }
+        };
+        Assert.assertEquals(0,   goe.getIODC());
+        Assert.assertEquals(0,   goe.getIODE());
+        Assert.assertEquals(0.0, goe.getAf0(), Precision.SAFE_MIN);
+        Assert.assertEquals(0.0, goe.getAf1(), Precision.SAFE_MIN);
+        Assert.assertEquals(0.0, goe.getAf2(), Precision.SAFE_MIN);
+        Assert.assertEquals(0.0, goe.getToc(), Precision.SAFE_MIN);
+        Assert.assertEquals(0.0, goe.getTGD(), Precision.SAFE_MIN);
+
+    }
+
+}
diff --git a/src/test/java/org/orekit/propagation/analytical/gnss/GPSPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/gnss/GPSPropagatorTest.java
index 626fe9af1..bfdf3db6c 100644
--- a/src/test/java/org/orekit/propagation/analytical/gnss/GPSPropagatorTest.java
+++ b/src/test/java/org/orekit/propagation/analytical/gnss/GPSPropagatorTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.hipparchus.geometry.euclidean.threed.Vector3D;
 import org.hipparchus.util.FastMath;
+import org.hipparchus.util.Precision;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -31,6 +32,7 @@ import org.orekit.frames.Frame;
 import org.orekit.frames.FramesFactory;
 import org.orekit.gnss.GPSAlmanac;
 import org.orekit.gnss.SEMParser;
+import org.orekit.propagation.SpacecraftState;
 import org.orekit.propagation.analytical.tle.TLE;
 import org.orekit.propagation.analytical.tle.TLEPropagator;
 import org.orekit.time.AbsoluteDate;
@@ -57,6 +59,27 @@ public class GPSPropagatorTest {
         almanacs = reader.getAlmanacs();
     }
 
+    @Test
+    public void testClockCorrections() {
+        final GPSPropagator propagator = new GPSPropagator.Builder(almanacs.get(0)).build();
+        propagator.addAdditionalStateProvider(new ClockCorrectionsProvider(almanacs.get(0)));
+        // Propagate at the GPS date and one GPS cycle later
+        final AbsoluteDate date0 = almanacs.get(0).getDate();
+        double dtRelMin = 0;
+        double dtRelMax = 0;
+        for (double dt = 0; dt < 0.5 * Constants.JULIAN_DAY; dt += 1.0) {
+            SpacecraftState state = propagator.propagate(date0.shiftedBy(dt));
+            double[] corrections = state.getAdditionalState(ClockCorrectionsProvider.CLOCK_CORRECTIONS);
+            Assert.assertEquals(3, corrections.length);
+            Assert.assertEquals(1.33514404296875E-05, corrections[0], 1.0e-19);
+            dtRelMin = FastMath.min(dtRelMin, corrections[1]);
+            dtRelMax = FastMath.max(dtRelMax, corrections[1]);
+            Assert.assertEquals(0.0, corrections[2], Precision.SAFE_MIN);
+        }
+        Assert.assertEquals(-1.1679e-8, dtRelMin, 1.0e-12);
+        Assert.assertEquals(+1.1679e-8, dtRelMax, 1.0e-12);
+    }
+
     @Test
     public void testGPSCycle() {
         // Builds the GPSPropagator from the almanac
-- 
GitLab