TimeStampedPVCoordinates.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.utils;

  18. import java.io.Serializable;
  19. import java.util.Collection;
  20. import java.util.stream.Stream;

  21. import org.hipparchus.analysis.differentiation.Derivative;
  22. import org.hipparchus.analysis.interpolation.HermiteInterpolator;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.util.FastMath;
  26. import org.orekit.annotation.DefaultDataContext;
  27. import org.orekit.data.DataContext;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.frames.Frame;
  30. import org.orekit.frames.Transform;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.time.TimeScale;
  33. import org.orekit.time.TimeStamped;

  34. /** {@link TimeStamped time-stamped} version of {@link PVCoordinates}.
  35.  * <p>Instances of this class are guaranteed to be immutable.</p>
  36.  * @author Luc Maisonobe
  37.  * @since 7.0
  38.  */
  39. public class TimeStampedPVCoordinates extends PVCoordinates implements TimeStamped {

  40.     /** Serializable UID. */
  41.     private static final long serialVersionUID = 20140723L;

  42.     /** The date. */
  43.     private final AbsoluteDate date;

  44.     /** Builds a TimeStampedPVCoordinates pair.
  45.      * @param date coordinates date
  46.      * @param position the position vector (m)
  47.      * @param velocity the velocity vector (m/s)
  48.      * @param acceleration the acceleration vector (m/s²)
  49.      */
  50.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  51.                                     final Vector3D position, final Vector3D velocity, final Vector3D acceleration) {
  52.         super(position, velocity, acceleration);
  53.         this.date = date;
  54.     }

  55.     /**
  56.      * Build from position and velocity. Acceleration is set to zero.
  57.      *
  58.      * @param date coordinates date
  59.      * @param position the position vector (m)
  60.      * @param velocity the velocity vector (m/s)
  61.      */
  62.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  63.                                     final Vector3D position,
  64.                                     final Vector3D velocity) {
  65.         this(date, position, velocity, Vector3D.ZERO);
  66.     }

  67.     /**
  68.      * Build from position velocity acceleration coordinates.
  69.      *
  70.      * @param date coordinates date
  71.      * @param pv position velocity, and acceleration coordinates, in meters and seconds.
  72.      */
  73.     public TimeStampedPVCoordinates(final AbsoluteDate date, final PVCoordinates pv) {
  74.         this(date, pv.getPosition(), pv.getVelocity(), pv.getAcceleration());
  75.     }

  76.     /** Multiplicative constructor
  77.      * <p>Build a TimeStampedPVCoordinates from another one and a scale factor.</p>
  78.      * <p>The TimeStampedPVCoordinates built will be a * pv</p>
  79.      * @param date date of the built coordinates
  80.      * @param a scale factor
  81.      * @param pv base (unscaled) PVCoordinates
  82.      */
  83.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  84.                                     final double a, final PVCoordinates pv) {
  85.         super(new Vector3D(a, pv.getPosition()),
  86.               new Vector3D(a, pv.getVelocity()),
  87.               new Vector3D(a, pv.getAcceleration()));
  88.         this.date = date;
  89.     }

  90.     /** Subtractive constructor
  91.      * <p>Build a relative TimeStampedPVCoordinates from a start and an end position.</p>
  92.      * <p>The TimeStampedPVCoordinates built will be end - start.</p>
  93.      * @param date date of the built coordinates
  94.      * @param start Starting PVCoordinates
  95.      * @param end ending PVCoordinates
  96.      */
  97.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  98.                                     final PVCoordinates start, final PVCoordinates end) {
  99.         super(end.getPosition().subtract(start.getPosition()),
  100.               end.getVelocity().subtract(start.getVelocity()),
  101.               end.getAcceleration().subtract(start.getAcceleration()));
  102.         this.date = date;
  103.     }

  104.     /** Linear constructor
  105.      * <p>Build a TimeStampedPVCoordinates from two other ones and corresponding scale factors.</p>
  106.      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2</p>
  107.      * @param date date of the built coordinates
  108.      * @param a1 first scale factor
  109.      * @param pv1 first base (unscaled) PVCoordinates
  110.      * @param a2 second scale factor
  111.      * @param pv2 second base (unscaled) PVCoordinates
  112.      */
  113.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  114.                                     final double a1, final PVCoordinates pv1,
  115.                                     final double a2, final PVCoordinates pv2) {
  116.         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition()),
  117.               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity()),
  118.               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration()));
  119.         this.date = date;
  120.     }

  121.     /** Linear constructor
  122.      * <p>Build a TimeStampedPVCoordinates from three other ones and corresponding scale factors.</p>
  123.      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3</p>
  124.      * @param date date of the built coordinates
  125.      * @param a1 first scale factor
  126.      * @param pv1 first base (unscaled) PVCoordinates
  127.      * @param a2 second scale factor
  128.      * @param pv2 second base (unscaled) PVCoordinates
  129.      * @param a3 third scale factor
  130.      * @param pv3 third base (unscaled) PVCoordinates
  131.      */
  132.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  133.                                     final double a1, final PVCoordinates pv1,
  134.                                     final double a2, final PVCoordinates pv2,
  135.                                     final double a3, final PVCoordinates pv3) {
  136.         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition(),     a3, pv3.getPosition()),
  137.               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity(),     a3, pv3.getVelocity()),
  138.               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration(), a3, pv3.getAcceleration()));
  139.         this.date = date;
  140.     }

  141.     /** Linear constructor
  142.      * <p>Build a TimeStampedPVCoordinates from four other ones and corresponding scale factors.</p>
  143.      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4</p>
  144.      * @param date date of the built coordinates
  145.      * @param a1 first scale factor
  146.      * @param pv1 first base (unscaled) PVCoordinates
  147.      * @param a2 second scale factor
  148.      * @param pv2 second base (unscaled) PVCoordinates
  149.      * @param a3 third scale factor
  150.      * @param pv3 third base (unscaled) PVCoordinates
  151.      * @param a4 fourth scale factor
  152.      * @param pv4 fourth base (unscaled) PVCoordinates
  153.      */
  154.     public TimeStampedPVCoordinates(final AbsoluteDate date,
  155.                                     final double a1, final PVCoordinates pv1,
  156.                                     final double a2, final PVCoordinates pv2,
  157.                                     final double a3, final PVCoordinates pv3,
  158.                                     final double a4, final PVCoordinates pv4) {
  159.         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition(),     a3, pv3.getPosition(),     a4, pv4.getPosition()),
  160.               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity(),     a3, pv3.getVelocity(),     a4, pv4.getVelocity()),
  161.               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration(), a3, pv3.getAcceleration(), a4, pv4.getAcceleration()));
  162.         this.date = date;
  163.     }

  164.     /** Builds a TimeStampedPVCoordinates triplet from  a {@link FieldVector3D}&lt;{@link Derivative}&gt;.
  165.      * <p>
  166.      * The vector components must have time as their only derivation parameter and
  167.      * have consistent derivation orders.
  168.      * </p>
  169.      * @param date date of the built coordinates
  170.      * @param p vector with time-derivatives embedded within the coordinates
  171.      * @param <U> type of the derivative
  172.      */
  173.     public <U extends Derivative<U>> TimeStampedPVCoordinates(final AbsoluteDate date, final FieldVector3D<U> p) {
  174.         super(p);
  175.         this.date = date;
  176.     }

  177.     /** {@inheritDoc} */
  178.     public AbsoluteDate getDate() {
  179.         return date;
  180.     }

  181.     /** Get a time-shifted state.
  182.      * <p>
  183.      * The state can be slightly shifted to close dates. This shift is based on
  184.      * a simple Taylor expansion. It is <em>not</em> intended as a replacement for
  185.      * proper orbit propagation (it is not even Keplerian!) but should be sufficient
  186.      * for either small time shifts or coarse accuracy.
  187.      * </p>
  188.      * @param dt time shift in seconds
  189.      * @return a new state, shifted with respect to the instance (which is immutable)
  190.      */
  191.     public TimeStampedPVCoordinates shiftedBy(final double dt) {
  192.         final PVCoordinates spv = super.shiftedBy(dt);
  193.         return new TimeStampedPVCoordinates(date.shiftedBy(dt),
  194.                                             spv.getPosition(), spv.getVelocity(), spv.getAcceleration());
  195.     }

  196.     /** Create a local provider using simply Taylor expansion through {@link #shiftedBy(double)}.
  197.      * <p>
  198.      * The time evolution is based on a simple Taylor expansion. It is <em>not</em> intended as a
  199.      * replacement for proper orbit propagation (it is not even Keplerian!) but should be sufficient
  200.      * for either small time shifts or coarse accuracy.
  201.      * </p>
  202.      * @param instanceFrame frame in which the instance is defined
  203.      * @return provider based on Taylor expansion, for small time shifts around instance date
  204.      */
  205.     public PVCoordinatesProvider toTaylorProvider(final Frame instanceFrame) {
  206.         return new PVCoordinatesProvider() {
  207.             /** {@inheritDoc} */
  208.             public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d,  final Frame f) {
  209.                 final TimeStampedPVCoordinates shifted   = shiftedBy(d.durationFrom(date));
  210.                 final Transform                transform = instanceFrame.getTransformTo(f, d);
  211.                 return transform.transformPVCoordinates(shifted);
  212.             }
  213.         };
  214.     }

  215.     /** Interpolate position-velocity.
  216.      * <p>
  217.      * The interpolated instance is created by polynomial Hermite interpolation
  218.      * ensuring velocity remains the exact derivative of position.
  219.      * </p>
  220.      * <p>
  221.      * Note that even if first time derivatives (velocities)
  222.      * from sample can be ignored, the interpolated instance always includes
  223.      * interpolated derivatives. This feature can be used explicitly to
  224.      * compute these derivatives when it would be too complex to compute them
  225.      * from an analytical formula: just compute a few sample points from the
  226.      * explicit formula and set the derivatives to zero in these sample points,
  227.      * then use interpolation to add derivatives consistent with the positions.
  228.      * </p>
  229.      * @param date interpolation date
  230.      * @param filter filter for derivatives from the sample to use in interpolation
  231.      * @param sample sample points on which interpolation should be done
  232.      * @return a new position-velocity, interpolated at specified date
  233.      */
  234.     public static TimeStampedPVCoordinates interpolate(final AbsoluteDate date,
  235.                                                        final CartesianDerivativesFilter filter,
  236.                                                        final Collection<? extends TimeStampedPVCoordinates> sample) {
  237.         return interpolate(date, filter, sample.stream());
  238.     }

  239.     /** Interpolate position-velocity.
  240.      * <p>
  241.      * The interpolated instance is created by polynomial Hermite interpolation
  242.      * ensuring velocity remains the exact derivative of position.
  243.      * </p>
  244.      * <p>
  245.      * Note that even if first time derivatives (velocities)
  246.      * from sample can be ignored, the interpolated instance always includes
  247.      * interpolated derivatives. This feature can be used explicitly to
  248.      * compute these derivatives when it would be too complex to compute them
  249.      * from an analytical formula: just compute a few sample points from the
  250.      * explicit formula and set the derivatives to zero in these sample points,
  251.      * then use interpolation to add derivatives consistent with the positions.
  252.      * </p>
  253.      * @param date interpolation date
  254.      * @param filter filter for derivatives from the sample to use in interpolation
  255.      * @param sample sample points on which interpolation should be done
  256.      * @return a new position-velocity, interpolated at specified date
  257.      * @since 9.0
  258.      */
  259.     public static TimeStampedPVCoordinates interpolate(final AbsoluteDate date,
  260.                                                        final CartesianDerivativesFilter filter,
  261.                                                        final Stream<? extends TimeStampedPVCoordinates> sample) {

  262.         // set up an interpolator taking derivatives into account
  263.         final HermiteInterpolator interpolator = new HermiteInterpolator();

  264.         // add sample points
  265.         switch (filter) {
  266.             case USE_P :
  267.                 // populate sample with position data, ignoring velocity
  268.                 sample.forEach(pv -> {
  269.                     final Vector3D position = pv.getPosition();
  270.                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
  271.                                                 position.toArray());
  272.                 });
  273.                 break;
  274.             case USE_PV :
  275.                 // populate sample with position and velocity data
  276.                 sample.forEach(pv -> {
  277.                     final Vector3D position = pv.getPosition();
  278.                     final Vector3D velocity = pv.getVelocity();
  279.                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
  280.                                                 position.toArray(), velocity.toArray());
  281.                 });
  282.                 break;
  283.             case USE_PVA :
  284.                 // populate sample with position, velocity and acceleration data
  285.                 sample.forEach(pv -> {
  286.                     final Vector3D position     = pv.getPosition();
  287.                     final Vector3D velocity     = pv.getVelocity();
  288.                     final Vector3D acceleration = pv.getAcceleration();
  289.                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
  290.                                                 position.toArray(), velocity.toArray(), acceleration.toArray());
  291.                 });
  292.                 break;
  293.             default :
  294.                 // this should never happen
  295.                 throw new OrekitInternalError(null);
  296.         }

  297.         // interpolate
  298.         final double[][] p = interpolator.derivatives(0.0, 2);

  299.         // build a new interpolated instance
  300.         return new TimeStampedPVCoordinates(date, new Vector3D(p[0]), new Vector3D(p[1]), new Vector3D(p[2]));

  301.     }

  302.     /** Return a string representation of this date, position, velocity, and acceleration.
  303.      *
  304.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  305.      *
  306.      * @return string representation of this.
  307.      */
  308.     @Override
  309.     @DefaultDataContext
  310.     public String toString() {
  311.         return toString(DataContext.getDefault().getTimeScales().getUTC());
  312.     }

  313.     /**
  314.      * Return a string representation of this date, position, velocity, and acceleration.
  315.      *
  316.      * @param utc time scale used to print the date.
  317.      * @return string representation of this.
  318.      */
  319.     public String toString(final TimeScale utc) {
  320.         final String comma = ", ";
  321.         return new StringBuilder().append('{').
  322.                                   append(date.toString(utc)).append(", P(").
  323.                                   append(getPosition().getX()).append(comma).
  324.                                   append(getPosition().getY()).append(comma).
  325.                                   append(getPosition().getZ()).append("), V(").
  326.                                   append(getVelocity().getX()).append(comma).
  327.                                   append(getVelocity().getY()).append(comma).
  328.                                   append(getVelocity().getZ()).append("), A(").
  329.                                   append(getAcceleration().getX()).append(comma).
  330.                                   append(getAcceleration().getY()).append(comma).
  331.                                   append(getAcceleration().getZ()).append(")}").toString();
  332.     }

  333.     /** Replace the instance with a data transfer object for serialization.
  334.      * @return data transfer object that will be serialized
  335.      */
  336.     @DefaultDataContext
  337.     private Object writeReplace() {
  338.         return new DTO(this);
  339.     }

  340.     /** Internal class used only for serialization. */
  341.     @DefaultDataContext
  342.     private static class DTO implements Serializable {

  343.         /** Serializable UID. */
  344.         private static final long serialVersionUID = 20140723L;

  345.         /** Double values. */
  346.         private double[] d;

  347.         /** Simple constructor.
  348.          * @param pv instance to serialize
  349.          */
  350.         private DTO(final TimeStampedPVCoordinates pv) {

  351.             // decompose date
  352.             final AbsoluteDate j2000Epoch =
  353.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  354.             final double epoch  = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
  355.             final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));

  356.             this.d = new double[] {
  357.                 epoch, offset,
  358.                 pv.getPosition().getX(),     pv.getPosition().getY(),     pv.getPosition().getZ(),
  359.                 pv.getVelocity().getX(),     pv.getVelocity().getY(),     pv.getVelocity().getZ(),
  360.                 pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ()
  361.             };

  362.         }

  363.         /** Replace the deserialized data transfer object with a {@link TimeStampedPVCoordinates}.
  364.          * @return replacement {@link TimeStampedPVCoordinates}
  365.          */
  366.         private Object readResolve() {
  367.             final AbsoluteDate j2000Epoch =
  368.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  369.             return new TimeStampedPVCoordinates(j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  370.                                                 new Vector3D(d[2], d[3], d[ 4]),
  371.                                                 new Vector3D(d[5], d[6], d[ 7]),
  372.                                                 new Vector3D(d[8], d[9], d[10]));
  373.         }

  374.     }

  375. }