EstimatedEarthFrameProvider.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.estimation.measurements;

  18. import java.io.Serializable;
  19. import java.util.Map;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.analysis.differentiation.Gradient;
  22. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Rotation;
  25. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  26. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  27. import org.hipparchus.util.FastMath;
  28. import org.orekit.errors.OrekitException;
  29. import org.orekit.errors.OrekitInternalError;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.frames.FieldTransform;
  32. import org.orekit.frames.Transform;
  33. import org.orekit.frames.TransformProvider;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.time.FieldAbsoluteDate;
  36. import org.orekit.time.UT1Scale;
  37. import org.orekit.utils.IERSConventions;
  38. import org.orekit.utils.ParameterDriver;

  39. /** Class modeling an Earth frame whose Earth Orientation Parameters can be estimated.
  40.  * <p>
  41.  * This class adds parameters for an additional polar motion
  42.  * and an additional prime meridian orientation on top of an underlying regular Earth
  43.  * frame like {@link org.orekit.frames.FramesFactory#getITRF(IERSConventions, boolean) ITRF}.
  44.  * The polar motion and prime meridian orientation are applied <em>after</em> regular Earth
  45.  * orientation parameters, so the value of the estimated parameters will be correction to EOP,
  46.  * they will not be the complete EOP values by themselves. Basically, this means that for
  47.  * Earth, the following transforms are applied in order, between inertial frame and this frame:
  48.  * </p>
  49.  * <ol>
  50.  *   <li>precession/nutation, as theoretical model plus celestial pole EOP parameters</li>
  51.  *   <li>body rotation, as theoretical model plus prime meridian EOP parameters</li>
  52.  *   <li>polar motion, which is only from EOP parameters (no theoretical models)</li>
  53.  *   <li>additional body rotation, controlled by {@link #getPrimeMeridianOffsetDriver()} and {@link #getPrimeMeridianDriftDriver()}</li>
  54.  *   <li>additional polar motion, controlled by {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()},
  55.  *   {@link #getPolarOffsetYDriver()} and {@link #getPolarDriftYDriver()}</li>
  56.  * </ol>
  57.  * @author Luc Maisonobe
  58.  * @since 9.1
  59.  */
  60. public class EstimatedEarthFrameProvider implements TransformProvider {

  61.     /** Earth Angular Velocity, in rad/s, from TIRF model. */
  62.     public static final double EARTH_ANGULAR_VELOCITY = 7.292115146706979e-5;

  63.     /** Serializable UID. */
  64.     private static final long serialVersionUID = 20170922L;

  65.     /** Angular scaling factor.
  66.      * <p>
  67.      * We use a power of 2 to avoid numeric noise introduction
  68.      * in the multiplications/divisions sequences.
  69.      * </p>
  70.      */
  71.     private static final double ANGULAR_SCALE = FastMath.scalb(1.0, -22);

  72.     /** Underlying raw UT1. */
  73.     private final UT1Scale baseUT1;

  74.     /** Estimated UT1. */
  75.     private final transient UT1Scale estimatedUT1;

  76.     /** Driver for prime meridian offset. */
  77.     private final transient ParameterDriver primeMeridianOffsetDriver;

  78.     /** Driver for prime meridian drift. */
  79.     private final transient ParameterDriver primeMeridianDriftDriver;

  80.     /** Driver for pole offset along X. */
  81.     private final transient ParameterDriver polarOffsetXDriver;

  82.     /** Driver for pole drift along X. */
  83.     private final transient ParameterDriver polarDriftXDriver;

  84.     /** Driver for pole offset along Y. */
  85.     private final transient ParameterDriver polarOffsetYDriver;

  86.     /** Driver for pole drift along Y. */
  87.     private final transient ParameterDriver polarDriftYDriver;

  88.     /** Build an estimated Earth frame.
  89.      * <p>
  90.      * The initial values for the pole and prime meridian parametric linear models
  91.      * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()},
  92.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()},
  93.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}) are set to 0.
  94.      * </p>
  95.      * @param baseUT1 underlying base UT1
  96.      * @since 9.1
  97.      */
  98.     public EstimatedEarthFrameProvider(final UT1Scale baseUT1) {

  99.         this.primeMeridianOffsetDriver = new ParameterDriver("prime-meridian-offset",
  100.                                                              0.0, ANGULAR_SCALE,
  101.                                                             -FastMath.PI, FastMath.PI);

  102.         this.primeMeridianDriftDriver = new ParameterDriver("prime-meridian-drift",
  103.                                                             0.0, ANGULAR_SCALE,
  104.                                                             Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  105.         this.polarOffsetXDriver = new ParameterDriver("polar-offset-X",
  106.                                                       0.0, ANGULAR_SCALE,
  107.                                                       -FastMath.PI, FastMath.PI);

  108.         this.polarDriftXDriver = new ParameterDriver("polar-drift-X",
  109.                                                      0.0, ANGULAR_SCALE,
  110.                                                      Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  111.         this.polarOffsetYDriver = new ParameterDriver("polar-offset-Y",
  112.                                                       0.0, ANGULAR_SCALE,
  113.                                                       -FastMath.PI, FastMath.PI);

  114.         this.polarDriftYDriver = new ParameterDriver("polar-drift-Y",
  115.                                                      0.0, ANGULAR_SCALE,
  116.                                                      Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  117.         this.baseUT1      = baseUT1;
  118.         this.estimatedUT1 = new EstimatedUT1Scale();

  119.     }

  120.     /** Get a driver allowing to add a prime meridian rotation.
  121.      * <p>
  122.      * The parameter is an angle in radians. In order to convert this
  123.      * value to a DUT1 in seconds, the value must be divided by
  124.      * {@link #EARTH_ANGULAR_VELOCITY} (nominal Angular Velocity of Earth).
  125.      * </p>
  126.      * @return driver for prime meridian rotation
  127.      */
  128.     public ParameterDriver getPrimeMeridianOffsetDriver() {
  129.         return primeMeridianOffsetDriver;
  130.     }

  131.     /** Get a driver allowing to add a prime meridian rotation rate.
  132.      * <p>
  133.      * The parameter is an angle rate in radians per second. In order to convert this
  134.      * value to a LOD in seconds, the value must be multiplied by -86400 and divided by
  135.      * {@link #EARTH_ANGULAR_VELOCITY} (nominal Angular Velocity of Earth).
  136.      * </p>
  137.      * @return driver for prime meridian rotation rate
  138.      */
  139.     public ParameterDriver getPrimeMeridianDriftDriver() {
  140.         return primeMeridianDriftDriver;
  141.     }

  142.     /** Get a driver allowing to add a polar offset along X.
  143.      * <p>
  144.      * The parameter is an angle in radians
  145.      * </p>
  146.      * @return driver for polar offset along X
  147.      */
  148.     public ParameterDriver getPolarOffsetXDriver() {
  149.         return polarOffsetXDriver;
  150.     }

  151.     /** Get a driver allowing to add a polar drift along X.
  152.      * <p>
  153.      * The parameter is an angle rate in radians per second
  154.      * </p>
  155.      * @return driver for polar drift along X
  156.      */
  157.     public ParameterDriver getPolarDriftXDriver() {
  158.         return polarDriftXDriver;
  159.     }

  160.     /** Get a driver allowing to add a polar offset along Y.
  161.      * <p>
  162.      * The parameter is an angle in radians
  163.      * </p>
  164.      * @return driver for polar offset along Y
  165.      */
  166.     public ParameterDriver getPolarOffsetYDriver() {
  167.         return polarOffsetYDriver;
  168.     }

  169.     /** Get a driver allowing to add a polar drift along Y.
  170.      * <p>
  171.      * The parameter is an angle rate in radians per second
  172.      * </p>
  173.      * @return driver for polar drift along Y
  174.      */
  175.     public ParameterDriver getPolarDriftYDriver() {
  176.         return polarDriftYDriver;
  177.     }

  178.     /** Get the estimated UT1 time scale.
  179.      * @return estimated UT1 time scale
  180.      */
  181.     public UT1Scale getEstimatedUT1() {
  182.         return estimatedUT1;
  183.     }

  184.     /** {@inheritDoc} */
  185.     @Override
  186.     public Transform getTransform(final AbsoluteDate date) {

  187.         // take parametric prime meridian shift into account
  188.         final double theta    = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
  189.         final double thetaDot = primeMeridianDriftDriver.getValue();
  190.         final Transform meridianShift =
  191.                         new Transform(date,
  192.                                       new Rotation(Vector3D.PLUS_K, theta, RotationConvention.FRAME_TRANSFORM),
  193.                                       new Vector3D(0, 0, thetaDot));

  194.         // take parametric pole shift into account
  195.         final double xpNeg     = -linearModel(date, polarOffsetXDriver, polarDriftXDriver);
  196.         final double ypNeg     = -linearModel(date, polarOffsetYDriver, polarDriftYDriver);
  197.         final double xpNegDot  = -polarDriftXDriver.getValue();
  198.         final double ypNegDot  = -polarDriftYDriver.getValue();
  199.         final Transform poleShift =
  200.                         new Transform(date,
  201.                                       new Transform(date,
  202.                                                     new Rotation(Vector3D.PLUS_J, xpNeg, RotationConvention.FRAME_TRANSFORM),
  203.                                                     new Vector3D(0.0, xpNegDot, 0.0)),
  204.                                       new Transform(date,
  205.                                                     new Rotation(Vector3D.PLUS_I, ypNeg, RotationConvention.FRAME_TRANSFORM),
  206.                                                     new Vector3D(ypNegDot, 0.0, 0.0)));

  207.         return new Transform(date, meridianShift, poleShift);

  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {

  212.         final T zero = date.getField().getZero();

  213.         // prime meridian shift parameters
  214.         final T theta    = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
  215.         final T thetaDot = zero.add(primeMeridianDriftDriver.getValue());

  216.         // pole shift parameters
  217.         final T xpNeg    = linearModel(date, polarOffsetXDriver, polarDriftXDriver).negate();
  218.         final T ypNeg    = linearModel(date, polarOffsetYDriver, polarDriftYDriver).negate();
  219.         final T xpNegDot = zero.subtract(polarDriftXDriver.getValue());
  220.         final T ypNegDot = zero.subtract(polarDriftYDriver.getValue());

  221.         return getTransform(date, theta, thetaDot, xpNeg, xpNegDot, ypNeg, ypNegDot);

  222.     }

  223.     /** Get the transform with derivatives.
  224.      * @param date date of the transform
  225.      * @param freeParameters total number of free parameters in the gradient
  226.      * @param indices indices of the estimated parameters in derivatives computations
  227.      * @return computed transform with derivatives
  228.      * @since 10.2
  229.      */
  230.     public FieldTransform<Gradient> getTransform(final FieldAbsoluteDate<Gradient> date,
  231.                                                  final int freeParameters,
  232.                                                  final Map<String, Integer> indices) {

  233.         // prime meridian shift parameters
  234.         final Gradient theta    = linearModel(freeParameters, date,
  235.                                               primeMeridianOffsetDriver, primeMeridianDriftDriver,
  236.                                               indices);
  237.         final Gradient thetaDot = primeMeridianDriftDriver.getValue(freeParameters, indices);

  238.         // pole shift parameters
  239.         final Gradient xpNeg    = linearModel(freeParameters, date,
  240.                                                          polarOffsetXDriver, polarDriftXDriver, indices).negate();
  241.         final Gradient ypNeg    = linearModel(freeParameters, date,
  242.                                                          polarOffsetYDriver, polarDriftYDriver, indices).negate();
  243.         final Gradient xpNegDot = polarDriftXDriver.getValue(freeParameters, indices).negate();
  244.         final Gradient ypNegDot = polarDriftYDriver.getValue(freeParameters, indices).negate();

  245.         return getTransform(date, theta, thetaDot, xpNeg, xpNegDot, ypNeg, ypNegDot);

  246.     }

  247.     /** Get the transform with derivatives.
  248.      * @param date date of the transform
  249.      * @param theta angle of the prime meridian
  250.      * @param thetaDot angular rate of the prime meridian
  251.      * @param xpNeg opposite of the angle of the pole motion along X
  252.      * @param xpNegDot opposite of the angular rate of the pole motion along X
  253.      * @param ypNeg opposite of the angle of the pole motion along Y
  254.      * @param ypNegDot opposite of the angular rate of the pole motion along Y
  255.      * @param <T> type of the field elements
  256.      * @return computed transform with derivatives
  257.      */
  258.     private <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date,
  259.                                                                            final T theta, final T thetaDot,
  260.                                                                            final T xpNeg, final T xpNegDot,
  261.                                                                            final T ypNeg, final T ypNegDot) {

  262.         final T                zero  = date.getField().getZero();
  263.         final FieldVector3D<T> plusI = FieldVector3D.getPlusI(date.getField());
  264.         final FieldVector3D<T> plusJ = FieldVector3D.getPlusJ(date.getField());
  265.         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(date.getField());

  266.         // take parametric prime meridian shift into account
  267.         final FieldTransform<T> meridianShift =
  268.                         new FieldTransform<>(date,
  269.                                              new FieldRotation<>(plusK, theta, RotationConvention.FRAME_TRANSFORM),
  270.                                              new FieldVector3D<>(zero, zero, thetaDot));

  271.         // take parametric pole shift into account
  272.         final FieldTransform<T> poleShift =
  273.                         new FieldTransform<>(date,
  274.                                       new FieldTransform<>(date,
  275.                                                            new FieldRotation<>(plusJ, xpNeg, RotationConvention.FRAME_TRANSFORM),
  276.                                                            new FieldVector3D<>(zero, xpNegDot, zero)),
  277.                                       new FieldTransform<>(date,
  278.                                                            new FieldRotation<>(plusI, ypNeg, RotationConvention.FRAME_TRANSFORM),
  279.                                                            new FieldVector3D<>(ypNegDot, zero, zero)));

  280.         return new FieldTransform<>(date, meridianShift, poleShift);

  281.     }

  282.     /** Evaluate a parametric linear model.
  283.      * @param date current date
  284.      * @param offsetDriver driver for the offset parameter
  285.      * @param driftDriver driver for the drift parameter
  286.      * @return current value of the linear model
  287.      */
  288.     private double linearModel(final AbsoluteDate date,
  289.                                final ParameterDriver offsetDriver, final ParameterDriver driftDriver) {
  290.         if (offsetDriver.getReferenceDate() == null) {
  291.             throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
  292.                                       offsetDriver.getName());
  293.         }
  294.         final double dt     = date.durationFrom(offsetDriver.getReferenceDate());
  295.         final double offset = offsetDriver.getValue();
  296.         final double drift  = driftDriver.getValue();
  297.         return dt * drift + offset;
  298.     }

  299.     /** Evaluate a parametric linear model.
  300.      * @param date current date
  301.      * @param offsetDriver driver for the offset parameter
  302.      * @param driftDriver driver for the drift parameter
  303.      * @return current value of the linear model
  304.      * @param <T> type of the filed elements
  305.      */
  306.     private <T extends CalculusFieldElement<T>> T linearModel(final FieldAbsoluteDate<T> date,
  307.                                                           final ParameterDriver offsetDriver,
  308.                                                           final ParameterDriver driftDriver) {
  309.         if (offsetDriver.getReferenceDate() == null) {
  310.             throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
  311.                                       offsetDriver.getName());
  312.         }
  313.         final T dt          = date.durationFrom(offsetDriver.getReferenceDate());
  314.         final double offset = offsetDriver.getValue();
  315.         final double drift  = driftDriver.getValue();
  316.         return dt.multiply(drift).add(offset);
  317.     }

  318.     /** Evaluate a parametric linear model.
  319.      * @param freeParameters total number of free parameters in the gradient
  320.      * @param date current date
  321.      * @param offsetDriver driver for the offset parameter
  322.      * @param driftDriver driver for the drift parameter
  323.      * @param indices indices of the estimated parameters in derivatives computations
  324.      * @return current value of the linear model
  325.      * @since 10.2
  326.      */
  327.     private Gradient linearModel(final int freeParameters, final FieldAbsoluteDate<Gradient> date,
  328.                                  final ParameterDriver offsetDriver, final ParameterDriver driftDriver,
  329.                                  final Map<String, Integer> indices) {
  330.         if (offsetDriver.getReferenceDate() == null) {
  331.             throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
  332.                                       offsetDriver.getName());
  333.         }
  334.         final Gradient dt     = date.durationFrom(offsetDriver.getReferenceDate());
  335.         final Gradient offset = offsetDriver.getValue(freeParameters, indices);
  336.         final Gradient drift  = driftDriver.getValue(freeParameters, indices);
  337.         return dt.multiply(drift).add(offset);
  338.     }

  339.     /** Replace the instance with a data transfer object for serialization.
  340.      * <p>
  341.      * This intermediate class serializes the files supported names, the ephemeris type
  342.      * and the body name.
  343.      * </p>
  344.      * @return data transfer object that will be serialized
  345.      */
  346.     private Object writeReplace() {
  347.         return new DataTransferObject(baseUT1,
  348.                                       primeMeridianOffsetDriver.getValue(),
  349.                                       primeMeridianDriftDriver.getValue(),
  350.                                       polarOffsetXDriver.getValue(),
  351.                                       polarDriftXDriver.getValue(),
  352.                                       polarOffsetYDriver.getValue(),
  353.                                       polarDriftYDriver.getValue());
  354.     }

  355.     /** Local time scale for estimated UT1. */
  356.     private class EstimatedUT1Scale extends UT1Scale {

  357.         /** Serializable UID. */
  358.         private static final long serialVersionUID = 20170922L;

  359.         /** Simple constructor.
  360.          */
  361.         EstimatedUT1Scale() {
  362.             super(baseUT1.getEOPHistory(), baseUT1.getUTCScale());
  363.         }

  364.         /** {@inheritDoc} */
  365.         @Override
  366.         public <T extends CalculusFieldElement<T>> T offsetFromTAI(final FieldAbsoluteDate<T> date) {
  367.             final T dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver).divide(EARTH_ANGULAR_VELOCITY);
  368.             return baseUT1.offsetFromTAI(date).add(dut1);
  369.         }

  370.         /** {@inheritDoc} */
  371.         @Override
  372.         public double offsetFromTAI(final AbsoluteDate date) {
  373.             final double dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver) / EARTH_ANGULAR_VELOCITY;
  374.             return baseUT1.offsetFromTAI(date) + dut1;
  375.         }

  376.         /** {@inheritDoc} */
  377.         @Override
  378.         public String getName() {
  379.             return baseUT1.getName() + "/estimated";
  380.         }

  381.     }

  382.     /** Internal class used only for serialization. */
  383.     private static class DataTransferObject implements Serializable {

  384.         /** Serializable UID. */
  385.         private static final long serialVersionUID = 20171124L;

  386.         /** Underlying raw UT1. */
  387.         private final UT1Scale baseUT1;

  388.         /** Current prime meridian offset. */
  389.         private final double primeMeridianOffset;

  390.         /** Current prime meridian drift. */
  391.         private final double primeMeridianDrift;

  392.         /** Current pole offset along X. */
  393.         private final double polarOffsetX;

  394.         /** Current pole drift along X. */
  395.         private final double polarDriftX;

  396.         /** Current pole offset along Y. */
  397.         private final double polarOffsetY;

  398.         /** Current pole drift along Y. */
  399.         private final double polarDriftY;

  400.         /** Simple constructor.
  401.          * @param baseUT1 underlying raw UT1
  402.          * @param primeMeridianOffset current prime meridian offset
  403.          * @param primeMeridianDrift current prime meridian drift
  404.          * @param polarOffsetX current pole offset along X
  405.          * @param polarDriftX current pole drift along X
  406.          * @param polarOffsetY current pole offset along Y
  407.          * @param polarDriftY current pole drift along Y
  408.          */
  409.         DataTransferObject(final  UT1Scale baseUT1,
  410.                            final double primeMeridianOffset, final double primeMeridianDrift,
  411.                            final double polarOffsetX,        final double polarDriftX,
  412.                            final double polarOffsetY,        final double polarDriftY) {
  413.             this.baseUT1             = baseUT1;
  414.             this.primeMeridianOffset = primeMeridianOffset;
  415.             this.primeMeridianDrift  = primeMeridianDrift;
  416.             this.polarOffsetX        = polarOffsetX;
  417.             this.polarDriftX         = polarDriftX;
  418.             this.polarOffsetY        = polarOffsetY;
  419.             this.polarDriftY         = polarDriftY;
  420.         }

  421.         /** Replace the deserialized data transfer object with a {@link EstimatedEarthFrameProvider}.
  422.          * @return replacement {@link EstimatedEarthFrameProvider}
  423.          */
  424.         private Object readResolve() {
  425.             try {
  426.                 final EstimatedEarthFrameProvider provider = new EstimatedEarthFrameProvider(baseUT1);
  427.                 provider.getPrimeMeridianOffsetDriver().setValue(primeMeridianOffset);
  428.                 provider.getPrimeMeridianDriftDriver().setValue(primeMeridianDrift);
  429.                 provider.getPolarOffsetXDriver().setValue(polarOffsetX);
  430.                 provider.getPolarDriftXDriver().setValue(polarDriftX);
  431.                 provider.getPolarOffsetYDriver().setValue(polarOffsetY);
  432.                 provider.getPolarDriftYDriver().setValue(polarDriftY);
  433.                 return provider;
  434.             } catch (OrekitException oe) {
  435.                 // this should never happen as values already come from previous drivers
  436.                 throw new OrekitInternalError(oe);
  437.             }
  438.         }

  439.     }

  440. }