FieldTransform.java

  1. /* Copyright 2002-2025 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.frames;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.List;
  22. import java.util.stream.Stream;

  23. import org.hipparchus.CalculusFieldElement;
  24. import org.hipparchus.Field;
  25. import org.hipparchus.geometry.euclidean.threed.FieldLine;
  26. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  27. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  28. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.FieldAbsoluteDate;
  31. import org.orekit.time.FieldTimeInterpolator;
  32. import org.orekit.time.FieldTimeShiftable;
  33. import org.orekit.utils.AngularDerivativesFilter;
  34. import org.orekit.utils.CartesianDerivativesFilter;
  35. import org.orekit.utils.FieldAngularCoordinates;
  36. import org.orekit.utils.FieldPVCoordinates;
  37. import org.orekit.utils.PVCoordinates;
  38. import org.orekit.utils.TimeStampedFieldAngularCoordinates;
  39. import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator;
  40. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  41. import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator;
  42. import org.orekit.utils.TimeStampedPVCoordinates;


  43. /** Transformation class in three-dimensional space.
  44.  *
  45.  * <p>This class represents the transformation engine between {@link Frame frames}.
  46.  * It is used both to define the relationship between each frame and its
  47.  * parent frame and to gather all individual transforms into one
  48.  * operation when converting between frames far away from each other.</p>
  49.  * <p>The convention used in OREKIT is vectorial transformation. It means
  50.  * that a transformation is defined as a transform to apply to the
  51.  * coordinates of a vector expressed in the old frame to obtain the
  52.  * same vector expressed in the new frame.
  53.  *
  54.  * <p>Instances of this class are guaranteed to be immutable.</p>
  55.  *
  56.  * <h2> Examples </h2>
  57.  *
  58.  * <h3> Example of translation from R<sub>A</sub> to R<sub>B</sub> </h3>
  59.  *
  60.  * <p> We want to transform the {@link FieldPVCoordinates} PV<sub>A</sub> to
  61.  * PV<sub>B</sub> with :
  62.  * <p> PV<sub>A</sub> = ({1, 0, 0}, {2, 0, 0}, {3, 0, 0}); <br>
  63.  *     PV<sub>B</sub> = ({0, 0, 0}, {0, 0, 0}, {0, 0, 0});
  64.  *
  65.  * <p> The transform to apply then is defined as follows :
  66.  *
  67.  * <pre>
  68.  * Vector3D translation  = new Vector3D(-1, 0, 0);
  69.  * Vector3D velocity     = new Vector3D(-2, 0, 0);
  70.  * Vector3D acceleration = new Vector3D(-3, 0, 0);
  71.  *
  72.  * Transform R1toR2 = new Transform(date, translation, velocity, acceleration);
  73.  *
  74.  * PVB = R1toR2.transformPVCoordinate(PVA);
  75.  * </pre>
  76.  *
  77.  * <h3> Example of rotation from R<sub>A</sub> to R<sub>B</sub> </h3>
  78.  * <p> We want to transform the {@link FieldPVCoordinates} PV<sub>A</sub> to
  79.  * PV<sub>B</sub> with
  80.  *
  81.  * <p> PV<sub>A</sub> = ({1, 0, 0}, { 1, 0, 0}); <br>
  82.  *     PV<sub>B</sub> = ({0, 1, 0}, {-2, 1, 0});
  83.  *
  84.  * <p> The transform to apply then is defined as follows :
  85.  *
  86.  * <pre>
  87.  * Rotation rotation = new Rotation(Vector3D.PLUS_K, FastMath.PI / 2);
  88.  * Vector3D rotationRate = new Vector3D(0, 0, -2);
  89.  *
  90.  * Transform R1toR2 = new Transform(rotation, rotationRate);
  91.  *
  92.  * PVB = R1toR2.transformPVCoordinates(PVA);
  93.  * </pre>
  94.  *
  95.  * @author Luc Maisonobe
  96.  * @author Fabien Maussion
  97.  * @param <T> the type of the field elements
  98.  * @since 9.0
  99.  */
  100. public class FieldTransform<T extends CalculusFieldElement<T>>
  101.     implements FieldTimeShiftable<FieldTransform<T>, T>, FieldKinematicTransform<T> {

  102.     /** Date of the transform. */
  103.     private final FieldAbsoluteDate<T> date;

  104.     /** Date of the transform. */
  105.     private final AbsoluteDate aDate;

  106.     /** Cartesian coordinates of the target frame with respect to the original frame. */
  107.     private final FieldPVCoordinates<T> cartesian;

  108.     /** Angular coordinates of the target frame with respect to the original frame. */
  109.     private final FieldAngularCoordinates<T> angular;

  110.     /** Build a transform from its primitive operations.
  111.      * @param date date of the transform
  112.      * @param aDate date of the transform
  113.      * @param cartesian Cartesian coordinates of the target frame with respect to the original frame
  114.      * @param angular angular coordinates of the target frame with respect to the original frame
  115.      */
  116.     private FieldTransform(final FieldAbsoluteDate<T> date, final AbsoluteDate aDate,
  117.                            final FieldPVCoordinates<T> cartesian,
  118.                            final FieldAngularCoordinates<T> angular) {
  119.         this.date      = date;
  120.         this.aDate     = aDate;
  121.         this.cartesian = cartesian;
  122.         this.angular   = angular;
  123.     }

  124.     /** Build a transform from a regular transform.
  125.      * @param field field of the elements
  126.      * @param transform regular transform to convert
  127.      */
  128.     public FieldTransform(final Field<T> field, final Transform transform) {
  129.         this(new FieldAbsoluteDate<>(field, transform.getDate()), transform.getDate(),
  130.              new FieldPVCoordinates<>(field, transform.getCartesian()),
  131.              new FieldAngularCoordinates<>(field, transform.getAngular()));
  132.     }

  133.     /** Build a translation transform.
  134.      * @param date date of the transform
  135.      * @param translation translation to apply (i.e. coordinates of
  136.      * the transformed origin, or coordinates of the origin of the
  137.      * old frame in the new frame)
  138.      */
  139.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldVector3D<T> translation) {
  140.         this(date, date.toAbsoluteDate(),
  141.              new FieldPVCoordinates<>(translation,
  142.                                       FieldVector3D.getZero(date.getField()),
  143.                                       FieldVector3D.getZero(date.getField())),
  144.              FieldAngularCoordinates.getIdentity(date.getField()));
  145.     }

  146.     /** Build a rotation transform.
  147.      * @param date date of the transform
  148.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  149.      * coordinates of a vector expressed in the old frame to obtain the
  150.      * same vector expressed in the new frame )
  151.      */
  152.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldRotation<T> rotation) {
  153.         this(date, date.toAbsoluteDate(),
  154.              FieldPVCoordinates.getZero(date.getField()),
  155.              new FieldAngularCoordinates<>(rotation, FieldVector3D.getZero(date.getField())));
  156.     }

  157.     /** Build a translation transform, with its first time derivative.
  158.      * @param date date of the transform
  159.      * @param translation translation to apply (i.e. coordinates of
  160.      * the transformed origin, or coordinates of the origin of the
  161.      * old frame in the new frame)
  162.      * @param velocity the velocity of the translation (i.e. origin
  163.      * of the old frame velocity in the new frame)
  164.      */
  165.     public FieldTransform(final FieldAbsoluteDate<T> date,
  166.                           final FieldVector3D<T> translation,
  167.                           final FieldVector3D<T> velocity) {
  168.         this(date,
  169.              new FieldPVCoordinates<>(translation,
  170.                                       velocity,
  171.                                       FieldVector3D.getZero(date.getField())));
  172.     }

  173.     /** Build a translation transform, with its first and second time derivatives.
  174.      * @param date date of the transform
  175.      * @param translation translation to apply (i.e. coordinates of
  176.      * the transformed origin, or coordinates of the origin of the
  177.      * old frame in the new frame)
  178.      * @param velocity the velocity of the translation (i.e. origin
  179.      * of the old frame velocity in the new frame)
  180.      * @param acceleration the acceleration of the translation (i.e. origin
  181.      * of the old frame acceleration in the new frame)
  182.      */
  183.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldVector3D<T> translation,
  184.                           final FieldVector3D<T> velocity, final FieldVector3D<T> acceleration) {
  185.         this(date,
  186.              new FieldPVCoordinates<>(translation, velocity, acceleration));
  187.     }

  188.     /** Build a translation transform, with its first time derivative.
  189.      * @param date date of the transform
  190.      * @param cartesian Cartesian part of the transformation to apply (i.e. coordinates of
  191.      * the transformed origin, or coordinates of the origin of the
  192.      * old frame in the new frame, with their derivatives)
  193.      */
  194.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldPVCoordinates<T> cartesian) {
  195.         this(date, date.toAbsoluteDate(),
  196.              cartesian,
  197.              FieldAngularCoordinates.getIdentity(date.getField()));
  198.     }

  199.     /** Build a combined translation and rotation transform.
  200.      * @param date date of the transform
  201.      * @param translation translation to apply (i.e. coordinates of
  202.      * the transformed origin, or coordinates of the origin of the
  203.      * old frame in the new frame)
  204.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  205.      * coordinates of a vector expressed in the old frame to obtain the
  206.      * same vector expressed in the new frame )
  207.      * @since 12.1
  208.      */
  209.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldVector3D<T> translation,
  210.                           final FieldRotation<T> rotation) {
  211.         this(date, date.toAbsoluteDate(), new FieldPVCoordinates<>(translation, FieldVector3D.getZero(date.getField())),
  212.                 new FieldAngularCoordinates<>(rotation, FieldVector3D.getZero(date.getField())));
  213.     }

  214.     /** Build a rotation transform.
  215.      * @param date date of the transform
  216.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  217.      * coordinates of a vector expressed in the old frame to obtain the
  218.      * same vector expressed in the new frame )
  219.      * @param rotationRate the axis of the instant rotation
  220.      * expressed in the new frame. (norm representing angular rate)
  221.      */
  222.     public FieldTransform(final FieldAbsoluteDate<T> date,
  223.                           final FieldRotation<T> rotation,
  224.                           final FieldVector3D<T> rotationRate) {
  225.         this(date,
  226.              new FieldAngularCoordinates<>(rotation,
  227.                                            rotationRate,
  228.                                            FieldVector3D.getZero(date.getField())));
  229.     }

  230.     /** Build a rotation transform.
  231.      * @param date date of the transform
  232.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  233.      * coordinates of a vector expressed in the old frame to obtain the
  234.      * same vector expressed in the new frame )
  235.      * @param rotationRate the axis of the instant rotation
  236.      * @param rotationAcceleration the axis of the instant rotation
  237.      * expressed in the new frame. (norm representing angular rate)
  238.      */
  239.     public FieldTransform(final FieldAbsoluteDate<T> date,
  240.                           final FieldRotation<T> rotation,
  241.                           final FieldVector3D<T> rotationRate,
  242.                           final FieldVector3D<T> rotationAcceleration) {
  243.         this(date, new FieldAngularCoordinates<>(rotation, rotationRate, rotationAcceleration));
  244.     }

  245.     /** Build a rotation transform.
  246.      * @param date date of the transform
  247.      * @param angular angular part of the transformation to apply (i.e. rotation to
  248.      * apply to the coordinates of a vector expressed in the old frame to obtain the
  249.      * same vector expressed in the new frame, with its rotation rate)
  250.      */
  251.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldAngularCoordinates<T> angular) {
  252.         this(date, date.toAbsoluteDate(),
  253.              FieldPVCoordinates.getZero(date.getField()),
  254.              angular);
  255.     }

  256.     /** Build a transform by combining two existing ones.
  257.      * <p>
  258.      * Note that the dates of the two existing transformed are <em>ignored</em>,
  259.      * and the combined transform date is set to the date supplied in this constructor
  260.      * without any attempt to shift the raw transforms. This is a design choice allowing
  261.      * user full control of the combination.
  262.      * </p>
  263.      * @param date date of the transform
  264.      * @param first first transform applied
  265.      * @param second second transform applied
  266.      */
  267.     public FieldTransform(final FieldAbsoluteDate<T> date,
  268.                           final FieldTransform<T> first,
  269.                           final FieldTransform<T> second) {
  270.         this(date, date.toAbsoluteDate(),
  271.              new FieldPVCoordinates<>(FieldStaticTransform.compositeTranslation(first, second),
  272.                                       compositeVelocity(first, second),
  273.                                       compositeAcceleration(first, second)),
  274.              new FieldAngularCoordinates<>(FieldStaticTransform.compositeRotation(first, second),
  275.                                            compositeRotationRate(first, second),
  276.                                            compositeRotationAcceleration(first, second)));
  277.     }

  278.     /** Get the identity transform.
  279.      * @param field field for the components
  280.      * @param <T> the type of the field elements
  281.      * @return identity transform
  282.      */
  283.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> getIdentity(final Field<T> field) {
  284.         return new FieldIdentityTransform<>(field);
  285.     }

  286.     /** Compute a composite velocity.
  287.      * @param first first applied transform
  288.      * @param second second applied transform
  289.      * @param <T> the type of the field elements
  290.      * @return velocity part of the composite transform
  291.      */
  292.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeVelocity(final FieldTransform<T> first, final FieldTransform<T> second) {

  293.         final FieldVector3D<T> v1 = first.cartesian.getVelocity();
  294.         final FieldRotation<T> r1 = first.angular.getRotation();
  295.         final FieldVector3D<T> o1 = first.angular.getRotationRate();
  296.         final FieldVector3D<T> p2 = second.cartesian.getPosition();
  297.         final FieldVector3D<T> v2 = second.cartesian.getVelocity();

  298.         final FieldVector3D<T> crossP = FieldVector3D.crossProduct(o1, p2);

  299.         return v1.add(r1.applyInverseTo(v2.add(crossP)));

  300.     }

  301.     /** Compute a composite acceleration.
  302.      * @param first first applied transform
  303.      * @param second second applied transform
  304.      * @param <T> the type of the field elements
  305.      * @return acceleration part of the composite transform
  306.      */
  307.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeAcceleration(final FieldTransform<T> first, final FieldTransform<T> second) {

  308.         final FieldVector3D<T> a1    = first.cartesian.getAcceleration();
  309.         final FieldRotation<T> r1    = first.angular.getRotation();
  310.         final FieldVector3D<T> o1    = first.angular.getRotationRate();
  311.         final FieldVector3D<T> oDot1 = first.angular.getRotationAcceleration();
  312.         final FieldVector3D<T> p2    = second.cartesian.getPosition();
  313.         final FieldVector3D<T> v2    = second.cartesian.getVelocity();
  314.         final FieldVector3D<T> a2    = second.cartesian.getAcceleration();

  315.         final FieldVector3D<T> crossCrossP = FieldVector3D.crossProduct(o1,    FieldVector3D.crossProduct(o1, p2));
  316.         final FieldVector3D<T> crossV      = FieldVector3D.crossProduct(o1,    v2);
  317.         final FieldVector3D<T> crossDotP   = FieldVector3D.crossProduct(oDot1, p2);

  318.         return a1.add(r1.applyInverseTo(new FieldVector3D<>(1, a2, 2, crossV, 1, crossCrossP, 1, crossDotP)));

  319.     }

  320.     /** Compute a composite rotation rate.
  321.      * @param first first applied transform
  322.      * @param second second applied transform
  323.      * @param <T> the type of the field elements
  324.      * @return rotation rate part of the composite transform
  325.      */
  326.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeRotationRate(final FieldTransform<T> first, final FieldTransform<T> second) {

  327.         final FieldVector3D<T> o1 = first.angular.getRotationRate();
  328.         final FieldRotation<T> r2 = second.angular.getRotation();
  329.         final FieldVector3D<T> o2 = second.angular.getRotationRate();

  330.         return o2.add(r2.applyTo(o1));

  331.     }

  332.     /** Compute a composite rotation acceleration.
  333.      * @param first first applied transform
  334.      * @param second second applied transform
  335.      * @param <T> the type of the field elements
  336.      * @return rotation acceleration part of the composite transform
  337.      */
  338.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeRotationAcceleration(final FieldTransform<T> first, final FieldTransform<T> second) {

  339.         final FieldVector3D<T> o1    = first.angular.getRotationRate();
  340.         final FieldVector3D<T> oDot1 = first.angular.getRotationAcceleration();
  341.         final FieldRotation<T> r2    = second.angular.getRotation();
  342.         final FieldVector3D<T> o2    = second.angular.getRotationRate();
  343.         final FieldVector3D<T> oDot2 = second.angular.getRotationAcceleration();

  344.         return new FieldVector3D<>( 1, oDot2,
  345.                                     1, r2.applyTo(oDot1),
  346.                                    -1, FieldVector3D.crossProduct(o2, r2.applyTo(o1)));

  347.     }

  348.     /** {@inheritDoc} */
  349.     @Override
  350.     public AbsoluteDate getDate() {
  351.         return aDate;
  352.     }

  353.     /** Get the date.
  354.      * @return date attached to the object
  355.      */
  356.     @Override
  357.     public FieldAbsoluteDate<T> getFieldDate() {
  358.         return date;
  359.     }

  360.     /** {@inheritDoc} */
  361.     @Override
  362.     public FieldTransform<T> shiftedBy(final double dt) {
  363.         return new FieldTransform<>(date.shiftedBy(dt), aDate.shiftedBy(dt),
  364.                                     cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  365.     }

  366.     /** Get a time-shifted instance.
  367.      * @param dt time shift in seconds
  368.      * @return a new instance, shifted with respect to instance (which is not changed)
  369.      */
  370.     public FieldTransform<T> shiftedBy(final T dt) {
  371.         return new FieldTransform<>(date.shiftedBy(dt), aDate.shiftedBy(dt.getReal()),
  372.                                     cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  373.     }

  374.     /**
  375.      * Shift the transform in time considering all rates, then return only the
  376.      * translation and rotation portion of the transform.
  377.      *
  378.      * @param dt time shift in seconds.
  379.      * @return shifted transform as a static transform. It is static in the
  380.      * sense that it can only be used to transform directions and positions, but
  381.      * not velocities or accelerations.
  382.      * @see #shiftedBy(double)
  383.      */
  384.     public FieldStaticTransform<T> staticShiftedBy(final T dt) {
  385.         return FieldStaticTransform.of(date.shiftedBy(dt),
  386.                                        cartesian.positionShiftedBy(dt),
  387.                                        angular.rotationShiftedBy(dt));
  388.     }

  389.     /**
  390.      * Create a so-called static transform from the instance.
  391.      *
  392.      * @return static part of the transform. It is static in the
  393.      * sense that it can only be used to transform directions and positions, but
  394.      * not velocities or accelerations.
  395.      * @see FieldStaticTransform
  396.      */
  397.     public FieldStaticTransform<T> toStaticTransform() {
  398.         return FieldStaticTransform.of(date, cartesian.getPosition(), angular.getRotation());
  399.     }

  400.     /** Interpolate a transform from a sample set of existing transforms.
  401.      * <p>
  402.      * Calling this method is equivalent to call {@link #interpolate(FieldAbsoluteDate,
  403.      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
  404.      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
  405.      * {@link AngularDerivativesFilter#USE_RRA}
  406.      * set to true.
  407.      * </p>
  408.      * @param interpolationDate interpolation date
  409.      * @param sample sample points on which interpolation should be done
  410.      * @param <T> the type of the field elements
  411.      * @return a new instance, interpolated at specified date
  412.      */
  413.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> interpolate(final FieldAbsoluteDate<T> interpolationDate,
  414.                                                                                 final Collection<FieldTransform<T>> sample) {
  415.         return interpolate(interpolationDate,
  416.                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
  417.                            sample);
  418.     }

  419.     /** Interpolate a transform from a sample set of existing transforms.
  420.      * <p>
  421.      * Note that even if first time derivatives (velocities and rotation rates)
  422.      * from sample can be ignored, the interpolated instance always includes
  423.      * interpolated derivatives. This feature can be used explicitly to
  424.      * compute these derivatives when it would be too complex to compute them
  425.      * from an analytical formula: just compute a few sample points from the
  426.      * explicit formula and set the derivatives to zero in these sample points,
  427.      * then use interpolation to add derivatives consistent with the positions
  428.      * and rotations.
  429.      * </p>
  430.      * <p>
  431.      * As this implementation of interpolation is polynomial, it should be used only
  432.      * with small samples (about 10-20 points) in order to avoid <a
  433.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  434.      * and numerical problems (including NaN appearing).
  435.      * </p>
  436.      * @param date interpolation date
  437.      * @param cFilter filter for derivatives from the sample to use in interpolation
  438.      * @param aFilter filter for derivatives from the sample to use in interpolation
  439.      * @param sample sample points on which interpolation should be done
  440.      * @return a new instance, interpolated at specified date
  441.           * @param <T> the type of the field elements
  442.      */
  443.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> interpolate(final FieldAbsoluteDate<T> date,
  444.                                                                                 final CartesianDerivativesFilter cFilter,
  445.                                                                                 final AngularDerivativesFilter aFilter,
  446.                                                                                 final Collection<FieldTransform<T>> sample) {
  447.         return interpolate(date, cFilter, aFilter, sample.stream());
  448.     }

  449.     /** Interpolate a transform from a sample set of existing transforms.
  450.      * <p>
  451.      * Note that even if first time derivatives (velocities and rotation rates)
  452.      * from sample can be ignored, the interpolated instance always includes
  453.      * interpolated derivatives. This feature can be used explicitly to
  454.      * compute these derivatives when it would be too complex to compute them
  455.      * from an analytical formula: just compute a few sample points from the
  456.      * explicit formula and set the derivatives to zero in these sample points,
  457.      * then use interpolation to add derivatives consistent with the positions
  458.      * and rotations.
  459.      * </p>
  460.      * <p>
  461.      * As this implementation of interpolation is polynomial, it should be used only
  462.      * with small samples (about 10-20 points) in order to avoid <a
  463.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  464.      * and numerical problems (including NaN appearing).
  465.      * </p>
  466.      * @param date interpolation date
  467.      * @param cFilter filter for derivatives from the sample to use in interpolation
  468.      * @param aFilter filter for derivatives from the sample to use in interpolation
  469.      * @param sample sample points on which interpolation should be done
  470.      * @return a new instance, interpolated at specified date
  471.           * @param <T> the type of the field elements
  472.      */
  473.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> interpolate(final FieldAbsoluteDate<T> date,
  474.                                                                                 final CartesianDerivativesFilter cFilter,
  475.                                                                                 final AngularDerivativesFilter aFilter,
  476.                                                                                 final Stream<FieldTransform<T>> sample) {

  477.         // Create samples
  478.         final List<TimeStampedFieldPVCoordinates<T>>      datedPV = new ArrayList<>();
  479.         final List<TimeStampedFieldAngularCoordinates<T>> datedAC = new ArrayList<>();
  480.         sample.forEach(t -> {
  481.             datedPV.add(new TimeStampedFieldPVCoordinates<>(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
  482.             datedAC.add(new TimeStampedFieldAngularCoordinates<>(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
  483.         });

  484.         // Create interpolators
  485.         final FieldTimeInterpolator<TimeStampedFieldPVCoordinates<T>, T> pvInterpolator =
  486.                 new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(datedPV.size(), cFilter);

  487.         final FieldTimeInterpolator<TimeStampedFieldAngularCoordinates<T>, T> angularInterpolator =
  488.                 new TimeStampedFieldAngularCoordinatesHermiteInterpolator<>(datedPV.size(), aFilter);

  489.         // Interpolate
  490.         final TimeStampedFieldPVCoordinates<T>      interpolatedPV = pvInterpolator.interpolate(date, datedPV);
  491.         final TimeStampedFieldAngularCoordinates<T> interpolatedAC = angularInterpolator.interpolate(date, datedAC);

  492.         return new FieldTransform<>(date, date.toAbsoluteDate(), interpolatedPV, interpolatedAC);
  493.     }

  494.     /** Get the inverse transform of the instance.
  495.      * @return inverse transform of the instance
  496.      */
  497.     @Override
  498.     public FieldTransform<T> getInverse() {

  499.         final FieldRotation<T> r    = angular.getRotation();
  500.         final FieldVector3D<T> o    = angular.getRotationRate();
  501.         final FieldVector3D<T> oDot = angular.getRotationAcceleration();
  502.         final FieldVector3D<T> rp   = r.applyTo(cartesian.getPosition());
  503.         final FieldVector3D<T> rv   = r.applyTo(cartesian.getVelocity());
  504.         final FieldVector3D<T> ra   = r.applyTo(cartesian.getAcceleration());

  505.         final FieldVector3D<T> pInv        = rp.negate();
  506.         final FieldVector3D<T> crossP      = FieldVector3D.crossProduct(o, rp);
  507.         final FieldVector3D<T> vInv        = crossP.subtract(rv);
  508.         final FieldVector3D<T> crossV      = FieldVector3D.crossProduct(o, rv);
  509.         final FieldVector3D<T> crossDotP   = FieldVector3D.crossProduct(oDot, rp);
  510.         final FieldVector3D<T> crossCrossP = FieldVector3D.crossProduct(o, crossP);
  511.         final FieldVector3D<T> aInv        = new FieldVector3D<>(-1, ra,
  512.                                                                   2, crossV,
  513.                                                                   1, crossDotP,
  514.                                                                  -1, crossCrossP);

  515.         return new FieldTransform<>(date, aDate, new FieldPVCoordinates<>(pInv, vInv, aInv), angular.revert());

  516.     }

  517.     /** Get a frozen transform.
  518.      * <p>
  519.      * This method creates a copy of the instance but frozen in time,
  520.      * i.e. with velocity, acceleration and rotation rate forced to zero.
  521.      * </p>
  522.      * @return a new transform, without any time-dependent parts
  523.      */
  524.     public FieldTransform<T> freeze() {
  525.         return new FieldTransform<>(date, aDate,
  526.                                     new FieldPVCoordinates<>(cartesian.getPosition(),
  527.                                                              FieldVector3D.getZero(date.getField()),
  528.                                                              FieldVector3D.getZero(date.getField())),
  529.                                     new FieldAngularCoordinates<>(angular.getRotation(),
  530.                                                                   FieldVector3D.getZero(date.getField()),
  531.                                                                   FieldVector3D.getZero(date.getField())));
  532.     }

  533.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  534.      * <p>
  535.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  536.      * consistency between the transform {@link #getDate() date} and the time-stamped
  537.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  538.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  539.      * the input argument, regardless of the instance {@link #getDate() date}.
  540.      * </p>
  541.      * @param pv time-stamped  position-velocity to transform.
  542.      * @return transformed time-stamped position-velocity
  543.      */
  544.     public FieldPVCoordinates<T> transformPVCoordinates(final PVCoordinates pv) {
  545.         return angular.applyTo(new FieldPVCoordinates<>(cartesian.getPosition().add(pv.getPosition()),
  546.                                                         cartesian.getVelocity().add(pv.getVelocity()),
  547.                                                         cartesian.getAcceleration().add(pv.getAcceleration())));
  548.     }

  549.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  550.      * <p>
  551.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  552.      * consistency between the transform {@link #getDate() date} and the time-stamped
  553.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  554.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  555.      * the input argument, regardless of the instance {@link #getDate() date}.
  556.      * </p>
  557.      * @param pv time-stamped  position-velocity to transform.
  558.      * @return transformed time-stamped position-velocity
  559.      */
  560.     public TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedPVCoordinates pv) {
  561.         return angular.applyTo(new TimeStampedFieldPVCoordinates<>(pv.getDate(),
  562.                                                                    cartesian.getPosition().add(pv.getPosition()),
  563.                                                                    cartesian.getVelocity().add(pv.getVelocity()),
  564.                                                                    cartesian.getAcceleration().add(pv.getAcceleration())));
  565.     }

  566.     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
  567.      * <p>
  568.      * BEWARE! This method does explicit computation of velocity and acceleration by combining
  569.      * the transform velocity, acceleration, rotation rate and rotation acceleration with the
  570.      * velocity and acceleration from the argument. This implies that this method should
  571.      * <em>not</em> be used when derivatives are contained in the {@link CalculusFieldElement field
  572.      * elements} (typically when using {@link org.hipparchus.analysis.differentiation.DerivativeStructure
  573.      * DerivativeStructure} elements where time is one of the differentiation parameter), otherwise
  574.      * the time derivatives would be computed twice, once explicitly in this method and once implicitly
  575.      * in the field operations. If time derivatives are contained in the field elements themselves,
  576.      * then rather than this method the {@link #transformPosition(FieldVector3D) transformPosition}
  577.      * method should be used, so the derivatives are computed once, as part of the field. This
  578.      * method is rather expected to be used when the field elements are {@link
  579.      * org.hipparchus.analysis.differentiation.DerivativeStructure DerivativeStructure} instances
  580.      * where the differentiation parameters are not time (they can typically be initial state
  581.      * for computing state transition matrices or force models parameters, or ground stations
  582.      * positions, ...).
  583.      * </p>
  584.      * <p>
  585.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  586.      * consistency between the transform {@link #getDate() date} and the time-stamped
  587.      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
  588.      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
  589.      * the input argument, regardless of the instance {@link #getDate() date}.
  590.      * </p>
  591.      * @param pv time-stamped position-velocity to transform.
  592.      * @return transformed time-stamped position-velocity
  593.      */
  594.     public FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  595.         return angular.applyTo(new FieldPVCoordinates<>(pv.getPosition().add(cartesian.getPosition()),
  596.                                                         pv.getVelocity().add(cartesian.getVelocity()),
  597.                                                         pv.getAcceleration().add(cartesian.getAcceleration())));
  598.     }

  599.     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
  600.      * <p>
  601.      * BEWARE! This method does explicit computation of velocity and acceleration by combining
  602.      * the transform velocity, acceleration, rotation rate and rotation acceleration with the
  603.      * velocity and acceleration from the argument. This implies that this method should
  604.      * <em>not</em> be used when derivatives are contained in the {@link CalculusFieldElement field
  605.      * elements} (typically when using {@link org.hipparchus.analysis.differentiation.DerivativeStructure
  606.      * DerivativeStructure} elements where time is one of the differentiation parameter), otherwise
  607.      * the time derivatives would be computed twice, once explicitly in this method and once implicitly
  608.      * in the field operations. If time derivatives are contained in the field elements themselves,
  609.      * then rather than this method the {@link #transformPosition(FieldVector3D) transformPosition}
  610.      * method should be used, so the derivatives are computed once, as part of the field. This
  611.      * method is rather expected to be used when the field elements are {@link
  612.      * org.hipparchus.analysis.differentiation.DerivativeStructure DerivativeStructure} instances
  613.      * where the differentiation parameters are not time (they can typically be initial state
  614.      * for computing state transition matrices or force models parameters, or ground stations
  615.      * positions, ...).
  616.      * </p>
  617.      * <p>
  618.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  619.      * consistency between the transform {@link #getDate() date} and the time-stamped
  620.      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
  621.      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
  622.      * the input argument, regardless of the instance {@link #getDate() date}.
  623.      * </p>
  624.      * @param pv time-stamped position-velocity to transform.
  625.      * @return transformed time-stamped position-velocity
  626.      */
  627.     public TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {
  628.         return angular.applyTo(new TimeStampedFieldPVCoordinates<>(pv.getDate(),
  629.                                                                    pv.getPosition().add(cartesian.getPosition()),
  630.                                                                    pv.getVelocity().add(cartesian.getVelocity()),
  631.                                                                    pv.getAcceleration().add(cartesian.getAcceleration())));
  632.     }

  633.     /** Compute the Jacobian of the {@link #transformPVCoordinates(FieldPVCoordinates)}
  634.      * method of the transform.
  635.      * <p>
  636.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  637.      * of the transformed {@link FieldPVCoordinates} with respect to Cartesian coordinate j
  638.      * of the input {@link FieldPVCoordinates} in method {@link #transformPVCoordinates(FieldPVCoordinates)}.
  639.      * </p>
  640.      * <p>
  641.      * This definition implies that if we define position-velocity coordinates
  642.      * <pre>PV₁ = transform.transformPVCoordinates(PV₀)</pre>
  643.      * then their differentials dPV₁ and dPV₀ will obey the following relation
  644.      * where J is the matrix computed by this method:
  645.      * <pre>dPV₁ = J &times; dPV₀</pre>
  646.      *
  647.      * @param selector selector specifying the size of the upper left corner that must be filled
  648.      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
  649.      * velocities and accelerations)
  650.      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
  651.      * the Jacobian, the rest of the matrix remaining untouched
  652.      */
  653.     public void getJacobian(final CartesianDerivativesFilter selector, final T[][] jacobian) {

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

  655.         if (selector.getMaxOrder() == 0) {
  656.             // elementary matrix for rotation
  657.             final T[][] mData = angular.getRotation().getMatrix();

  658.             // dP1/dP0
  659.             System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  660.             System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  661.             System.arraycopy(mData[2], 0, jacobian[2], 0, 3);
  662.         }

  663.         else if (selector.getMaxOrder() == 1) {
  664.             // use KinematicTransform Jacobian
  665.             final T[][] mData = getPVJacobian();
  666.             for (int i = 0; i < mData.length; i++) {
  667.                 System.arraycopy(mData[i], 0, jacobian[i], 0, mData[i].length);
  668.             }
  669.         }

  670.         else if (selector.getMaxOrder() >= 2) {
  671.             getJacobian(CartesianDerivativesFilter.USE_PV, jacobian);

  672.             // dP1/dA0
  673.             Arrays.fill(jacobian[0], 6, 9, zero);
  674.             Arrays.fill(jacobian[1], 6, 9, zero);
  675.             Arrays.fill(jacobian[2], 6, 9, zero);

  676.             // dV1/dA0
  677.             Arrays.fill(jacobian[3], 6, 9, zero);
  678.             Arrays.fill(jacobian[4], 6, 9, zero);
  679.             Arrays.fill(jacobian[5], 6, 9, zero);

  680.             // dA1/dP0
  681.             final FieldVector3D<T> o = angular.getRotationRate();
  682.             final T ox = o.getX();
  683.             final T oy = o.getY();
  684.             final T oz = o.getZ();
  685.             final FieldVector3D<T> oDot = angular.getRotationAcceleration();
  686.             final T oDotx  = oDot.getX();
  687.             final T oDoty  = oDot.getY();
  688.             final T oDotz  = oDot.getZ();
  689.             for (int i = 0; i < 3; ++i) {
  690.                 jacobian[6][i] = oDotz.multiply(jacobian[1][i]).subtract(oDoty.multiply(jacobian[2][i])).add(oz.multiply(jacobian[4][i]).subtract(oy.multiply(jacobian[5][i])));
  691.                 jacobian[7][i] = oDotx.multiply(jacobian[2][i]).subtract(oDotz.multiply(jacobian[0][i])).add(ox.multiply(jacobian[5][i]).subtract(oz.multiply(jacobian[3][i])));
  692.                 jacobian[8][i] = oDoty.multiply(jacobian[0][i]).subtract(oDotx.multiply(jacobian[1][i])).add(oy.multiply(jacobian[3][i]).subtract(ox.multiply(jacobian[4][i])));
  693.             }

  694.             // dA1/dV0
  695.             for (int i = 0; i < 3; ++i) {
  696.                 jacobian[6][i + 3] = oz.multiply(jacobian[1][i]).subtract(oy.multiply(jacobian[2][i])).multiply(2);
  697.                 jacobian[7][i + 3] = ox.multiply(jacobian[2][i]).subtract(oz.multiply(jacobian[0][i])).multiply(2);
  698.                 jacobian[8][i + 3] = oy.multiply(jacobian[0][i]).subtract(ox.multiply(jacobian[1][i])).multiply(2);
  699.             }

  700.             // dA1/dA0
  701.             System.arraycopy(jacobian[0], 0, jacobian[6], 6, 3);
  702.             System.arraycopy(jacobian[1], 0, jacobian[7], 6, 3);
  703.             System.arraycopy(jacobian[2], 0, jacobian[8], 6, 3);

  704.         }

  705.     }

  706.     /** Get the underlying elementary Cartesian part.
  707.      * <p>A transform can be uniquely represented as an elementary
  708.      * translation followed by an elementary rotation. This method
  709.      * returns this unique elementary translation with its derivative.</p>
  710.      * @return underlying elementary Cartesian part
  711.      * @see #getTranslation()
  712.      * @see #getVelocity()
  713.      */
  714.     public FieldPVCoordinates<T> getCartesian() {
  715.         return cartesian;
  716.     }

  717.     /** Get the underlying elementary translation.
  718.      * <p>A transform can be uniquely represented as an elementary
  719.      * translation followed by an elementary rotation. This method
  720.      * returns this unique elementary translation.</p>
  721.      * @return underlying elementary translation
  722.      * @see #getCartesian()
  723.      * @see #getVelocity()
  724.      * @see #getAcceleration()
  725.      */
  726.     public FieldVector3D<T> getTranslation() {
  727.         return cartesian.getPosition();
  728.     }

  729.     /** Get the first time derivative of the translation.
  730.      * @return first time derivative of the translation
  731.      * @see #getCartesian()
  732.      * @see #getTranslation()
  733.      * @see #getAcceleration()
  734.      */
  735.     public FieldVector3D<T> getVelocity() {
  736.         return cartesian.getVelocity();
  737.     }

  738.     /** Get the second time derivative of the translation.
  739.      * @return second time derivative of the translation
  740.      * @see #getCartesian()
  741.      * @see #getTranslation()
  742.      * @see #getVelocity()
  743.      */
  744.     public FieldVector3D<T> getAcceleration() {
  745.         return cartesian.getAcceleration();
  746.     }

  747.     /** Get the underlying elementary angular part.
  748.      * <p>A transform can be uniquely represented as an elementary
  749.      * translation followed by an elementary rotation. This method
  750.      * returns this unique elementary rotation with its derivative.</p>
  751.      * @return underlying elementary angular part
  752.      * @see #getRotation()
  753.      * @see #getRotationRate()
  754.      * @see #getRotationAcceleration()
  755.      */
  756.     public FieldAngularCoordinates<T> getAngular() {
  757.         return angular;
  758.     }

  759.     /** Get the underlying elementary rotation.
  760.      * <p>A transform can be uniquely represented as an elementary
  761.      * translation followed by an elementary rotation. This method
  762.      * returns this unique elementary rotation.</p>
  763.      * @return underlying elementary rotation
  764.      * @see #getAngular()
  765.      * @see #getRotationRate()
  766.      * @see #getRotationAcceleration()
  767.      */
  768.     public FieldRotation<T> getRotation() {
  769.         return angular.getRotation();
  770.     }

  771.     /** Get the first time derivative of the rotation.
  772.      * <p>The norm represents the angular rate.</p>
  773.      * @return First time derivative of the rotation
  774.      * @see #getAngular()
  775.      * @see #getRotation()
  776.      * @see #getRotationAcceleration()
  777.      */
  778.     public FieldVector3D<T> getRotationRate() {
  779.         return angular.getRotationRate();
  780.     }

  781.     /** Get the second time derivative of the rotation.
  782.      * @return Second time derivative of the rotation
  783.      * @see #getAngular()
  784.      * @see #getRotation()
  785.      * @see #getRotationRate()
  786.      */
  787.     public FieldVector3D<T> getRotationAcceleration() {
  788.         return angular.getRotationAcceleration();
  789.     }

  790.     /** Specialized class for identity transform. */
  791.     private static class FieldIdentityTransform<T extends CalculusFieldElement<T>> extends FieldTransform<T> {

  792.         /** Field. */
  793.         private final Field<T> field;

  794.         /** Simple constructor.
  795.          * @param field field for the components
  796.          */
  797.         FieldIdentityTransform(final Field<T> field) {
  798.             super(FieldAbsoluteDate.getArbitraryEpoch(field),
  799.                   FieldAbsoluteDate.getArbitraryEpoch(field).toAbsoluteDate(),
  800.                   FieldPVCoordinates.getZero(field),
  801.                   FieldAngularCoordinates.getIdentity(field));
  802.             this.field = field;
  803.         }

  804.         @Override
  805.         public FieldStaticTransform<T> staticShiftedBy(final T dt) {
  806.             return toStaticTransform();
  807.         }

  808.         @Override
  809.         public FieldTransform<T> shiftedBy(final T dt) {
  810.             return this;
  811.         }

  812.         /** {@inheritDoc} */
  813.         @Override
  814.         public FieldTransform<T> shiftedBy(final double dt) {
  815.             return this;
  816.         }

  817.         @Override
  818.         public FieldStaticTransform<T> getStaticInverse() {
  819.             return toStaticTransform();
  820.         }

  821.         /** {@inheritDoc} */
  822.         @Override
  823.         public FieldTransform<T> getInverse() {
  824.             return this;
  825.         }

  826.         @Override
  827.         public FieldStaticTransform<T> toStaticTransform() {
  828.             return FieldStaticTransform.getIdentity(field);
  829.         }

  830.         /** {@inheritDoc} */
  831.         @Override
  832.         public FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
  833.             return position;
  834.         }

  835.         @Override
  836.         public FieldVector3D<T> transformPosition(final Vector3D position) {
  837.             return transformVector(position);
  838.         }

  839.         /** {@inheritDoc} */
  840.         @Override
  841.         public FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
  842.             return vector;
  843.         }

  844.         @Override
  845.         public FieldVector3D<T> transformVector(final Vector3D vector) {
  846.             return new FieldVector3D<>(field, vector);
  847.         }

  848.         /** {@inheritDoc} */
  849.         @Override
  850.         public FieldLine<T> transformLine(final FieldLine<T> line) {
  851.             return line;
  852.         }

  853.         /** {@inheritDoc} */
  854.         @Override
  855.         public FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  856.             return pv;
  857.         }

  858.         @Override
  859.         public FieldPVCoordinates<T> transformPVCoordinates(final PVCoordinates pv) {
  860.             return new FieldPVCoordinates<>(field, pv);
  861.         }

  862.         @Override
  863.         public TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedPVCoordinates pv) {
  864.             return new TimeStampedFieldPVCoordinates<>(field, pv);
  865.         }

  866.         @Override
  867.         public TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {
  868.             return pv;
  869.         }

  870.         @Override
  871.         public FieldPVCoordinates<T> transformOnlyPV(final FieldPVCoordinates<T> pv) {
  872.             return new FieldPVCoordinates<>(pv.getPosition(), pv.getVelocity());
  873.         }

  874.         @Override
  875.         public TimeStampedFieldPVCoordinates<T> transformOnlyPV(final TimeStampedFieldPVCoordinates<T> pv) {
  876.             return new TimeStampedFieldPVCoordinates<>(pv.getDate(), pv.getPosition(), pv.getVelocity(), FieldVector3D.getZero(field));
  877.         }

  878.         /** {@inheritDoc} */
  879.         @Override
  880.         public FieldTransform<T> freeze() {
  881.             return this;
  882.         }

  883.         /** {@inheritDoc} */
  884.         @Override
  885.         public void getJacobian(final CartesianDerivativesFilter selector, final T[][] jacobian) {
  886.             final int n = 3 * (selector.getMaxOrder() + 1);
  887.             for (int i = 0; i < n; ++i) {
  888.                 Arrays.fill(jacobian[i], 0, n, getFieldDate().getField().getZero());
  889.                 jacobian[i][i] = getFieldDate().getField().getOne();
  890.             }
  891.         }

  892.     }

  893. }