FieldTransform.java

  1. /* Copyright 2002-2023 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.orekit.time.AbsoluteDate;
  29. import org.orekit.time.FieldAbsoluteDate;
  30. import org.orekit.time.FieldTimeInterpolator;
  31. import org.orekit.time.FieldTimeShiftable;
  32. import org.orekit.utils.AngularDerivativesFilter;
  33. import org.orekit.utils.CartesianDerivativesFilter;
  34. import org.orekit.utils.FieldAngularCoordinates;
  35. import org.orekit.utils.FieldPVCoordinates;
  36. import org.orekit.utils.PVCoordinates;
  37. import org.orekit.utils.TimeStampedFieldAngularCoordinates;
  38. import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator;
  39. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  40. import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator;
  41. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

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

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

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

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

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

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

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

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

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

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

  200.     /** Build a rotation transform.
  201.      * @param date date of the transform
  202.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  203.      * coordinates of a vector expressed in the old frame to obtain the
  204.      * same vector expressed in the new frame )
  205.      * @param rotationRate the axis of the instant rotation
  206.      * expressed in the new frame. (norm representing angular rate)
  207.      */
  208.     public FieldTransform(final FieldAbsoluteDate<T> date,
  209.                           final FieldRotation<T> rotation,
  210.                           final FieldVector3D<T> rotationRate) {
  211.         this(date,
  212.              new FieldAngularCoordinates<>(rotation,
  213.                                            rotationRate,
  214.                                            FieldVector3D.getZero(date.getField())));
  215.     }

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

  231.     /** Build a rotation transform.
  232.      * @param date date of the transform
  233.      * @param angular angular part of the transformation to apply (i.e. rotation to
  234.      * apply to the coordinates of a vector expressed in the old frame to obtain the
  235.      * same vector expressed in the new frame, with its rotation rate)
  236.      */
  237.     public FieldTransform(final FieldAbsoluteDate<T> date, final FieldAngularCoordinates<T> angular) {
  238.         this(date, date.toAbsoluteDate(),
  239.              FieldPVCoordinates.getZero(date.getField()),
  240.              angular);
  241.     }

  242.     /** Build a transform by combining two existing ones.
  243.      * <p>
  244.      * Note that the dates of the two existing transformed are <em>ignored</em>,
  245.      * and the combined transform date is set to the date supplied in this constructor
  246.      * without any attempt to shift the raw transforms. This is a design choice allowing
  247.      * user full control of the combination.
  248.      * </p>
  249.      * @param date date of the transform
  250.      * @param first first transform applied
  251.      * @param second second transform applied
  252.      */
  253.     public FieldTransform(final FieldAbsoluteDate<T> date,
  254.                           final FieldTransform<T> first,
  255.                           final FieldTransform<T> second) {
  256.         this(date, date.toAbsoluteDate(),
  257.              new FieldPVCoordinates<>(FieldStaticTransform.compositeTranslation(first, second),
  258.                                       compositeVelocity(first, second),
  259.                                       compositeAcceleration(first, second)),
  260.              new FieldAngularCoordinates<>(FieldStaticTransform.compositeRotation(first, second),
  261.                                            compositeRotationRate(first, second),
  262.                                            compositeRotationAcceleration(first, second)));
  263.     }

  264.     /** Get the identity transform.
  265.      * @param field field for the components
  266.      * @param <T> the type of the field elements
  267.      * @return identity transform
  268.      */
  269.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> getIdentity(final Field<T> field) {
  270.         return new FieldIdentityTransform<>(field);
  271.     }

  272.     /** Compute a composite velocity.
  273.      * @param first first applied transform
  274.      * @param second second applied transform
  275.      * @param <T> the type of the field elements
  276.      * @return velocity part of the composite transform
  277.      */
  278.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeVelocity(final FieldTransform<T> first, final FieldTransform<T> second) {

  279.         final FieldVector3D<T> v1 = first.cartesian.getVelocity();
  280.         final FieldRotation<T> r1 = first.angular.getRotation();
  281.         final FieldVector3D<T> o1 = first.angular.getRotationRate();
  282.         final FieldVector3D<T> p2 = second.cartesian.getPosition();
  283.         final FieldVector3D<T> v2 = second.cartesian.getVelocity();

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

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

  286.     }

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

  294.         final FieldVector3D<T> a1    = first.cartesian.getAcceleration();
  295.         final FieldRotation<T> r1    = first.angular.getRotation();
  296.         final FieldVector3D<T> o1    = first.angular.getRotationRate();
  297.         final FieldVector3D<T> oDot1 = first.angular.getRotationAcceleration();
  298.         final FieldVector3D<T> p2    = second.cartesian.getPosition();
  299.         final FieldVector3D<T> v2    = second.cartesian.getVelocity();
  300.         final FieldVector3D<T> a2    = second.cartesian.getAcceleration();

  301.         final FieldVector3D<T> crossCrossP = FieldVector3D.crossProduct(o1,    FieldVector3D.crossProduct(o1, p2));
  302.         final FieldVector3D<T> crossV      = FieldVector3D.crossProduct(o1,    v2);
  303.         final FieldVector3D<T> crossDotP   = FieldVector3D.crossProduct(oDot1, p2);

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

  305.     }

  306.     /** Compute a composite rotation rate.
  307.      * @param first first applied transform
  308.      * @param second second applied transform
  309.      * @param <T> the type of the field elements
  310.      * @return rotation rate part of the composite transform
  311.      */
  312.     private static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeRotationRate(final FieldTransform<T> first, final FieldTransform<T> second) {

  313.         final FieldVector3D<T> o1 = first.angular.getRotationRate();
  314.         final FieldRotation<T> r2 = second.angular.getRotation();
  315.         final FieldVector3D<T> o2 = second.angular.getRotationRate();

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

  317.     }

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

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

  330.         return new FieldVector3D<>( 1, oDot2,
  331.                                     1, r2.applyTo(oDot1),
  332.                                    -1, FieldVector3D.crossProduct(o2, r2.applyTo(o1)));

  333.     }

  334.     /** {@inheritDoc} */
  335.     @Override
  336.     public AbsoluteDate getDate() {
  337.         return aDate;
  338.     }

  339.     /** Get the date.
  340.      * @return date attached to the object
  341.      */
  342.     public FieldAbsoluteDate<T> getFieldDate() {
  343.         return date;
  344.     }

  345.     /** {@inheritDoc} */
  346.     @Override
  347.     public FieldTransform<T> shiftedBy(final double dt) {
  348.         return new FieldTransform<>(date.shiftedBy(dt), aDate.shiftedBy(dt),
  349.                                     cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  350.     }

  351.     /** Get a time-shifted instance.
  352.      * @param dt time shift in seconds
  353.      * @return a new instance, shifted with respect to instance (which is not changed)
  354.      */
  355.     public FieldTransform<T> shiftedBy(final T dt) {
  356.         return new FieldTransform<>(date.shiftedBy(dt), aDate.shiftedBy(dt.getReal()),
  357.                                     cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  358.     }

  359.     /**
  360.      * Shift the transform in time considering all rates, then return only the
  361.      * translation and rotation portion of the transform.
  362.      *
  363.      * @param dt time shift in seconds.
  364.      * @return shifted transform as a static transform. It is static in the
  365.      * sense that it can only be used to transform directions and positions, but
  366.      * not velocities or accelerations.
  367.      * @see #shiftedBy(double)
  368.      */
  369.     public FieldStaticTransform<T> staticShiftedBy(final T dt) {
  370.         return FieldStaticTransform.of(date.shiftedBy(dt),
  371.                                        cartesian.positionShiftedBy(dt),
  372.                                        angular.rotationShiftedBy(dt));
  373.     }

  374.     /**
  375.      * Create a so-called static transform from the instance.
  376.      *
  377.      * @return static part of the transform. It is static in the
  378.      * sense that it can only be used to transform directions and positions, but
  379.      * not velocities or accelerations.
  380.      * @see FieldStaticTransform
  381.      */
  382.     public FieldStaticTransform<T> toStaticTransform() {
  383.         return FieldStaticTransform.of(date, cartesian.getPosition(), angular.getRotation());
  384.     }

  385.     /** Interpolate a transform from a sample set of existing transforms.
  386.      * <p>
  387.      * Calling this method is equivalent to call {@link #interpolate(FieldAbsoluteDate,
  388.      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
  389.      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
  390.      * {@link AngularDerivativesFilter#USE_RRA}
  391.      * set to true.
  392.      * </p>
  393.      * @param interpolationDate interpolation date
  394.      * @param sample sample points on which interpolation should be done
  395.      * @param <T> the type of the field elements
  396.      * @return a new instance, interpolated at specified date
  397.      */
  398.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> interpolate(final FieldAbsoluteDate<T> interpolationDate,
  399.                                                                                 final Collection<FieldTransform<T>> sample) {
  400.         return interpolate(interpolationDate,
  401.                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
  402.                            sample);
  403.     }

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

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

  462.         // Create samples
  463.         final List<TimeStampedFieldPVCoordinates<T>>      datedPV = new ArrayList<>();
  464.         final List<TimeStampedFieldAngularCoordinates<T>> datedAC = new ArrayList<>();
  465.         sample.forEach(t -> {
  466.             datedPV.add(new TimeStampedFieldPVCoordinates<>(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
  467.             datedAC.add(new TimeStampedFieldAngularCoordinates<>(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
  468.         });

  469.         // Create interpolators
  470.         final FieldTimeInterpolator<TimeStampedFieldPVCoordinates<T>, T> pvInterpolator =
  471.                 new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(datedPV.size(), cFilter);

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

  474.         // Interpolate
  475.         final TimeStampedFieldPVCoordinates<T>      interpolatedPV = pvInterpolator.interpolate(date, datedPV);
  476.         final TimeStampedFieldAngularCoordinates<T> interpolatedAC = angularInterpolator.interpolate(date, datedAC);

  477.         return new FieldTransform<>(date, date.toAbsoluteDate(), interpolatedPV, interpolatedAC);
  478.     }

  479.     /** Get the inverse transform of the instance.
  480.      * @return inverse transform of the instance
  481.      */
  482.     @Override
  483.     public FieldTransform<T> getInverse() {

  484.         final FieldRotation<T> r    = angular.getRotation();
  485.         final FieldVector3D<T> o    = angular.getRotationRate();
  486.         final FieldVector3D<T> oDot = angular.getRotationAcceleration();
  487.         final FieldVector3D<T> rp   = r.applyTo(cartesian.getPosition());
  488.         final FieldVector3D<T> rv   = r.applyTo(cartesian.getVelocity());
  489.         final FieldVector3D<T> ra   = r.applyTo(cartesian.getAcceleration());

  490.         final FieldVector3D<T> pInv        = rp.negate();
  491.         final FieldVector3D<T> crossP      = FieldVector3D.crossProduct(o, rp);
  492.         final FieldVector3D<T> vInv        = crossP.subtract(rv);
  493.         final FieldVector3D<T> crossV      = FieldVector3D.crossProduct(o, rv);
  494.         final FieldVector3D<T> crossDotP   = FieldVector3D.crossProduct(oDot, rp);
  495.         final FieldVector3D<T> crossCrossP = FieldVector3D.crossProduct(o, crossP);
  496.         final FieldVector3D<T> aInv        = new FieldVector3D<>(-1, ra,
  497.                                                                   2, crossV,
  498.                                                                   1, crossDotP,
  499.                                                                  -1, crossCrossP);

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

  501.     }

  502.     /** Get a frozen transform.
  503.      * <p>
  504.      * This method creates a copy of the instance but frozen in time,
  505.      * i.e. with velocity, acceleration and rotation rate forced to zero.
  506.      * </p>
  507.      * @return a new transform, without any time-dependent parts
  508.      */
  509.     public FieldTransform<T> freeze() {
  510.         return new FieldTransform<>(date, aDate,
  511.                                     new FieldPVCoordinates<>(cartesian.getPosition(),
  512.                                                              FieldVector3D.getZero(date.getField()),
  513.                                                              FieldVector3D.getZero(date.getField())),
  514.                                     new FieldAngularCoordinates<>(angular.getRotation(),
  515.                                                                   FieldVector3D.getZero(date.getField()),
  516.                                                                   FieldVector3D.getZero(date.getField())));
  517.     }

  518.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  519.      * <p>
  520.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  521.      * consistency between the transform {@link #getDate() date} and the time-stamped
  522.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  523.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  524.      * the input argument, regardless of the instance {@link #getDate() date}.
  525.      * </p>
  526.      * @param pv time-stamped  position-velocity to transform.
  527.      * @return transformed time-stamped position-velocity
  528.      */
  529.     public FieldPVCoordinates<T> transformPVCoordinates(final PVCoordinates pv) {
  530.         return angular.applyTo(new FieldPVCoordinates<>(cartesian.getPosition().add(pv.getPosition()),
  531.                                                         cartesian.getVelocity().add(pv.getVelocity()),
  532.                                                         cartesian.getAcceleration().add(pv.getAcceleration())));
  533.     }

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

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

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

  618.     /** Compute the Jacobian of the {@link #transformPVCoordinates(FieldPVCoordinates)}
  619.      * method of the transform.
  620.      * <p>
  621.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  622.      * of the transformed {@link FieldPVCoordinates} with respect to Cartesian coordinate j
  623.      * of the input {@link FieldPVCoordinates} in method {@link #transformPVCoordinates(FieldPVCoordinates)}.
  624.      * </p>
  625.      * <p>
  626.      * This definition implies that if we define position-velocity coordinates
  627.      * <pre>PV₁ = transform.transformPVCoordinates(PV₀)</pre>
  628.      * then their differentials dPV₁ and dPV₀ will obey the following relation
  629.      * where J is the matrix computed by this method:
  630.      * <pre>dPV₁ = J &times; dPV₀</pre>
  631.      *
  632.      * @param selector selector specifying the size of the upper left corner that must be filled
  633.      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
  634.      * velocities and accelerations)
  635.      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
  636.      * the Jacobian, the rest of the matrix remaining untouched
  637.      */
  638.     public void getJacobian(final CartesianDerivativesFilter selector, final T[][] jacobian) {

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

  640.         // elementary matrix for rotation
  641.         final T[][] mData = angular.getRotation().getMatrix();

  642.         // dP1/dP0
  643.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  644.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  645.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

  646.         if (selector.getMaxOrder() >= 1) {

  647.             // dP1/dV0
  648.             Arrays.fill(jacobian[0], 3, 6, zero);
  649.             Arrays.fill(jacobian[1], 3, 6, zero);
  650.             Arrays.fill(jacobian[2], 3, 6, zero);

  651.             // dV1/dP0
  652.             final FieldVector3D<T> o = angular.getRotationRate();
  653.             final T ox = o.getX();
  654.             final T oy = o.getY();
  655.             final T oz = o.getZ();
  656.             for (int i = 0; i < 3; ++i) {
  657.                 jacobian[3][i] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i]));
  658.                 jacobian[4][i] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i]));
  659.                 jacobian[5][i] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i]));
  660.             }

  661.             // dV1/dV0
  662.             System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  663.             System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  664.             System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

  665.             if (selector.getMaxOrder() >= 2) {

  666.                 // dP1/dA0
  667.                 Arrays.fill(jacobian[0], 6, 9, zero);
  668.                 Arrays.fill(jacobian[1], 6, 9, zero);
  669.                 Arrays.fill(jacobian[2], 6, 9, zero);

  670.                 // dV1/dA0
  671.                 Arrays.fill(jacobian[3], 6, 9, zero);
  672.                 Arrays.fill(jacobian[4], 6, 9, zero);
  673.                 Arrays.fill(jacobian[5], 6, 9, zero);

  674.                 // dA1/dP0
  675.                 final FieldVector3D<T> oDot = angular.getRotationAcceleration();
  676.                 final T oDotx  = oDot.getX();
  677.                 final T oDoty  = oDot.getY();
  678.                 final T oDotz  = oDot.getZ();
  679.                 for (int i = 0; i < 3; ++i) {
  680.                     jacobian[6][i] = oDotz.multiply(mData[1][i]).subtract(oDoty.multiply(mData[2][i])).add(oz.multiply(jacobian[4][i]).subtract(oy.multiply(jacobian[5][i])));
  681.                     jacobian[7][i] = oDotx.multiply(mData[2][i]).subtract(oDotz.multiply(mData[0][i])).add(ox.multiply(jacobian[5][i]).subtract(oz.multiply(jacobian[3][i])));
  682.                     jacobian[8][i] = oDoty.multiply(mData[0][i]).subtract(oDotx.multiply(mData[1][i])).add(oy.multiply(jacobian[3][i]).subtract(ox.multiply(jacobian[4][i])));
  683.                 }

  684.                 // dA1/dV0
  685.                 for (int i = 0; i < 3; ++i) {
  686.                     jacobian[6][i + 3] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i])).multiply(2);
  687.                     jacobian[7][i + 3] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i])).multiply(2);
  688.                     jacobian[8][i + 3] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i])).multiply(2);
  689.                 }

  690.                 // dA1/dA0
  691.                 System.arraycopy(mData[0], 0, jacobian[6], 6, 3);
  692.                 System.arraycopy(mData[1], 0, jacobian[7], 6, 3);
  693.                 System.arraycopy(mData[2], 0, jacobian[8], 6, 3);

  694.             }

  695.         }

  696.     }

  697.     /** Get the underlying elementary Cartesian part.
  698.      * <p>A transform can be uniquely represented as an elementary
  699.      * translation followed by an elementary rotation. This method
  700.      * returns this unique elementary translation with its derivative.</p>
  701.      * @return underlying elementary Cartesian part
  702.      * @see #getTranslation()
  703.      * @see #getVelocity()
  704.      */
  705.     public FieldPVCoordinates<T> getCartesian() {
  706.         return cartesian;
  707.     }

  708.     /** Get the underlying elementary translation.
  709.      * <p>A transform can be uniquely represented as an elementary
  710.      * translation followed by an elementary rotation. This method
  711.      * returns this unique elementary translation.</p>
  712.      * @return underlying elementary translation
  713.      * @see #getCartesian()
  714.      * @see #getVelocity()
  715.      * @see #getAcceleration()
  716.      */
  717.     public FieldVector3D<T> getTranslation() {
  718.         return cartesian.getPosition();
  719.     }

  720.     /** Get the first time derivative of the translation.
  721.      * @return first time derivative of the translation
  722.      * @see #getCartesian()
  723.      * @see #getTranslation()
  724.      * @see #getAcceleration()
  725.      */
  726.     public FieldVector3D<T> getVelocity() {
  727.         return cartesian.getVelocity();
  728.     }

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

  738.     /** Get the underlying elementary angular part.
  739.      * <p>A transform can be uniquely represented as an elementary
  740.      * translation followed by an elementary rotation. This method
  741.      * returns this unique elementary rotation with its derivative.</p>
  742.      * @return underlying elementary angular part
  743.      * @see #getRotation()
  744.      * @see #getRotationRate()
  745.      * @see #getRotationAcceleration()
  746.      */
  747.     public FieldAngularCoordinates<T> getAngular() {
  748.         return angular;
  749.     }

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

  762.     /** Get the first time derivative of the rotation.
  763.      * <p>The norm represents the angular rate.</p>
  764.      * @return First time derivative of the rotation
  765.      * @see #getAngular()
  766.      * @see #getRotation()
  767.      * @see #getRotationAcceleration()
  768.      */
  769.     public FieldVector3D<T> getRotationRate() {
  770.         return angular.getRotationRate();
  771.     }

  772.     /** Get the second time derivative of the rotation.
  773.      * @return Second time derivative of the rotation
  774.      * @see #getAngular()
  775.      * @see #getRotation()
  776.      * @see #getRotationRate()
  777.      */
  778.     public FieldVector3D<T> getRotationAcceleration() {
  779.         return angular.getRotationAcceleration();
  780.     }

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

  783.         /** Simple constructor.
  784.          * @param field field for the components
  785.          */
  786.         FieldIdentityTransform(final Field<T> field) {
  787.             super(FieldAbsoluteDate.getArbitraryEpoch(field),
  788.                   FieldAbsoluteDate.getArbitraryEpoch(field).toAbsoluteDate(),
  789.                   FieldPVCoordinates.getZero(field),
  790.                   FieldAngularCoordinates.getIdentity(field));
  791.         }

  792.         /** {@inheritDoc} */
  793.         @Override
  794.         public FieldTransform<T> shiftedBy(final double dt) {
  795.             return this;
  796.         }

  797.         /** {@inheritDoc} */
  798.         @Override
  799.         public FieldTransform<T> getInverse() {
  800.             return this;
  801.         }

  802.         /** {@inheritDoc} */
  803.         @Override
  804.         public FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
  805.             return position;
  806.         }

  807.         /** {@inheritDoc} */
  808.         @Override
  809.         public FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
  810.             return vector;
  811.         }

  812.         /** {@inheritDoc} */
  813.         @Override
  814.         public FieldLine<T> transformLine(final FieldLine<T> line) {
  815.             return line;
  816.         }

  817.         /** {@inheritDoc} */
  818.         @Override
  819.         public FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  820.             return pv;
  821.         }

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

  827.         /** {@inheritDoc} */
  828.         @Override
  829.         public void getJacobian(final CartesianDerivativesFilter selector, final T[][] jacobian) {
  830.             final int n = 3 * (selector.getMaxOrder() + 1);
  831.             for (int i = 0; i < n; ++i) {
  832.                 Arrays.fill(jacobian[i], 0, n, getFieldDate().getField().getZero());
  833.                 jacobian[i][i] = getFieldDate().getField().getOne();
  834.             }
  835.         }

  836.     }

  837. }