Transform.java

  1. /* Copyright 2002-2024 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.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collection;
  22. import java.util.List;
  23. import java.util.stream.Collectors;
  24. import java.util.stream.Stream;

  25. import org.hipparchus.CalculusFieldElement;
  26. import org.hipparchus.geometry.euclidean.threed.Line;
  27. import org.hipparchus.geometry.euclidean.threed.Rotation;
  28. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.TimeInterpolator;
  31. import org.orekit.time.TimeShiftable;
  32. import org.orekit.utils.AngularCoordinates;
  33. import org.orekit.utils.AngularDerivativesFilter;
  34. import org.orekit.utils.CartesianDerivativesFilter;
  35. import org.orekit.utils.FieldPVCoordinates;
  36. import org.orekit.utils.PVCoordinates;
  37. import org.orekit.utils.TimeStampedAngularCoordinates;
  38. import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator;
  39. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  40. import org.orekit.utils.TimeStampedPVCoordinates;
  41. import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;


  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 PVCoordinates} 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><code>
  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.transformPVCoordinates(PVA);
  74.  * </code></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 PVCoordinates} 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><code>
  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.  * </code></pre>
  93.  *
  94.  * @author Luc Maisonobe
  95.  * @author Fabien Maussion
  96.  */
  97. public class Transform implements
  98.         TimeShiftable<Transform>,
  99.         Serializable,
  100.         StaticTransform {

  101.     /** Identity transform. */
  102.     public static final Transform IDENTITY = new IdentityTransform();

  103.     /** Serializable UID. */
  104.     private static final long serialVersionUID = 210140410L;

  105.     /** Date of the transform. */
  106.     private final AbsoluteDate date;

  107.     /** Cartesian coordinates of the target frame with respect to the original frame. */
  108.     private final PVCoordinates cartesian;

  109.     /** Angular coordinates of the target frame with respect to the original frame. */
  110.     private final AngularCoordinates angular;

  111.     /** Build a transform from its primitive operations.
  112.      * @param date 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.     public Transform(final AbsoluteDate date, final PVCoordinates cartesian, final AngularCoordinates angular) {
  117.         this.date      = date;
  118.         this.cartesian = cartesian;
  119.         this.angular   = angular;
  120.     }

  121.     /** Build a translation transform.
  122.      * @param date date of the transform
  123.      * @param translation translation to apply (i.e. coordinates of
  124.      * the transformed origin, or coordinates of the origin of the
  125.      * old frame in the new frame)
  126.      */
  127.     public Transform(final AbsoluteDate date, final Vector3D translation) {
  128.         this(date,
  129.              new PVCoordinates(translation),
  130.              AngularCoordinates.IDENTITY);
  131.     }

  132.     /** Build a rotation transform.
  133.      * @param date date of the transform
  134.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  135.      * coordinates of a vector expressed in the old frame to obtain the
  136.      * same vector expressed in the new frame )
  137.      */
  138.     public Transform(final AbsoluteDate date, final Rotation rotation) {
  139.         this(date,
  140.              PVCoordinates.ZERO,
  141.              new AngularCoordinates(rotation));
  142.     }

  143.     /** Build a translation transform, with its first time derivative.
  144.      * @param date date of the transform
  145.      * @param translation translation to apply (i.e. coordinates of
  146.      * the transformed origin, or coordinates of the origin of the
  147.      * old frame in the new frame)
  148.      * @param velocity the velocity of the translation (i.e. origin
  149.      * of the old frame velocity in the new frame)
  150.      */
  151.     public Transform(final AbsoluteDate date, final Vector3D translation,
  152.                      final Vector3D velocity) {
  153.         this(date,
  154.              new PVCoordinates(translation, velocity, Vector3D.ZERO),
  155.              AngularCoordinates.IDENTITY);
  156.     }

  157.     /** Build a translation transform, with its first and second time derivatives.
  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.      * @param acceleration the acceleration of the translation (i.e. origin
  165.      * of the old frame acceleration in the new frame)
  166.      */
  167.     public Transform(final AbsoluteDate date, final Vector3D translation,
  168.                      final Vector3D velocity, final Vector3D acceleration) {
  169.         this(date,
  170.              new PVCoordinates(translation, velocity, acceleration),
  171.              AngularCoordinates.IDENTITY);
  172.     }

  173.     /** Build a translation transform, with its first time derivative.
  174.      * @param date date of the transform
  175.      * @param cartesian Cartesian part of the transformation to apply (i.e. coordinates of
  176.      * the transformed origin, or coordinates of the origin of the
  177.      * old frame in the new frame, with their derivatives)
  178.      */
  179.     public Transform(final AbsoluteDate date, final PVCoordinates cartesian) {
  180.         this(date,
  181.              cartesian,
  182.              AngularCoordinates.IDENTITY);
  183.     }

  184.     /** Build a rotation transform.
  185.      * @param date date of the transform
  186.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  187.      * coordinates of a vector expressed in the old frame to obtain the
  188.      * same vector expressed in the new frame )
  189.      * @param rotationRate the axis of the instant rotation
  190.      * expressed in the new frame. (norm representing angular rate)
  191.      */
  192.     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate) {
  193.         this(date,
  194.              PVCoordinates.ZERO,
  195.              new AngularCoordinates(rotation, rotationRate, Vector3D.ZERO));
  196.     }

  197.     /** Build a rotation transform.
  198.      * @param date date of the transform
  199.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  200.      * coordinates of a vector expressed in the old frame to obtain the
  201.      * same vector expressed in the new frame )
  202.      * @param rotationRate the axis of the instant rotation
  203.      * @param rotationAcceleration the axis of the instant rotation
  204.      * expressed in the new frame. (norm representing angular rate)
  205.      */
  206.     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate,
  207.                      final Vector3D rotationAcceleration) {
  208.         this(date,
  209.              PVCoordinates.ZERO,
  210.              new AngularCoordinates(rotation, rotationRate, rotationAcceleration));
  211.     }

  212.     /** Build a rotation transform.
  213.      * @param date date of the transform
  214.      * @param angular angular part of the transformation to apply (i.e. rotation to
  215.      * apply to the coordinates of a vector expressed in the old frame to obtain the
  216.      * same vector expressed in the new frame, with its rotation rate)
  217.      */
  218.     public Transform(final AbsoluteDate date, final AngularCoordinates angular) {
  219.         this(date, PVCoordinates.ZERO, angular);
  220.     }

  221.     /** Build a transform by combining two existing ones.
  222.      * <p>
  223.      * Note that the dates of the two existing transformed are <em>ignored</em>,
  224.      * and the combined transform date is set to the date supplied in this constructor
  225.      * without any attempt to shift the raw transforms. This is a design choice allowing
  226.      * user full control of the combination.
  227.      * </p>
  228.      * @param date date of the transform
  229.      * @param first first transform applied
  230.      * @param second second transform applied
  231.      */
  232.     public Transform(final AbsoluteDate date, final Transform first, final Transform second) {
  233.         this(date,
  234.              new PVCoordinates(StaticTransform.compositeTranslation(first, second),
  235.                                compositeVelocity(first, second),
  236.                                compositeAcceleration(first, second)),
  237.              new AngularCoordinates(StaticTransform.compositeRotation(first, second),
  238.                                     compositeRotationRate(first, second),
  239.                                     compositeRotationAcceleration(first, second)));
  240.     }

  241.     /** Compute a composite velocity.
  242.      * @param first first applied transform
  243.      * @param second second applied transform
  244.      * @return velocity part of the composite transform
  245.      */
  246.     private static Vector3D compositeVelocity(final Transform first, final Transform second) {

  247.         final Vector3D v1 = first.cartesian.getVelocity();
  248.         final Rotation r1 = first.angular.getRotation();
  249.         final Vector3D o1 = first.angular.getRotationRate();
  250.         final Vector3D p2 = second.cartesian.getPosition();
  251.         final Vector3D v2 = second.cartesian.getVelocity();

  252.         final Vector3D crossP = Vector3D.crossProduct(o1, p2);

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

  254.     }

  255.     /** Compute a composite acceleration.
  256.      * @param first first applied transform
  257.      * @param second second applied transform
  258.      * @return acceleration part of the composite transform
  259.      */
  260.     private static Vector3D compositeAcceleration(final Transform first, final Transform second) {

  261.         final Vector3D a1    = first.cartesian.getAcceleration();
  262.         final Rotation r1    = first.angular.getRotation();
  263.         final Vector3D o1    = first.angular.getRotationRate();
  264.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  265.         final Vector3D p2    = second.cartesian.getPosition();
  266.         final Vector3D v2    = second.cartesian.getVelocity();
  267.         final Vector3D a2    = second.cartesian.getAcceleration();

  268.         final Vector3D crossCrossP = Vector3D.crossProduct(o1,    Vector3D.crossProduct(o1, p2));
  269.         final Vector3D crossV      = Vector3D.crossProduct(o1,    v2);
  270.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot1, p2);

  271.         return a1.add(r1.applyInverseTo(new Vector3D(1, a2, 2, crossV, 1, crossCrossP, 1, crossDotP)));

  272.     }

  273.     /** Compute a composite rotation rate.
  274.      * @param first first applied transform
  275.      * @param second second applied transform
  276.      * @return rotation rate part of the composite transform
  277.      */
  278.     private static Vector3D compositeRotationRate(final Transform first, final Transform second) {

  279.         final Vector3D o1 = first.angular.getRotationRate();
  280.         final Rotation r2 = second.angular.getRotation();
  281.         final Vector3D o2 = second.angular.getRotationRate();

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

  283.     }

  284.     /** Compute a composite rotation acceleration.
  285.      * @param first first applied transform
  286.      * @param second second applied transform
  287.      * @return rotation acceleration part of the composite transform
  288.      */
  289.     private static Vector3D compositeRotationAcceleration(final Transform first, final Transform second) {

  290.         final Vector3D o1    = first.angular.getRotationRate();
  291.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  292.         final Rotation r2    = second.angular.getRotation();
  293.         final Vector3D o2    = second.angular.getRotationRate();
  294.         final Vector3D oDot2 = second.angular.getRotationAcceleration();

  295.         return new Vector3D( 1, oDot2,
  296.                              1, r2.applyTo(oDot1),
  297.                             -1, Vector3D.crossProduct(o2, r2.applyTo(o1)));

  298.     }

  299.     /** {@inheritDoc} */
  300.     public AbsoluteDate getDate() {
  301.         return date;
  302.     }

  303.     /** {@inheritDoc} */
  304.     public Transform shiftedBy(final double dt) {
  305.         return new Transform(date.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  306.     }

  307.     /**
  308.      * Shift the transform in time considering all rates, then return only the
  309.      * translation and rotation portion of the transform.
  310.      *
  311.      * @param dt time shift in seconds.
  312.      * @return shifted transform as a static transform. It is static in the
  313.      * sense that it can only be used to transform directions and positions, but
  314.      * not velocities or accelerations.
  315.      * @see #shiftedBy(double)
  316.      */
  317.     public StaticTransform staticShiftedBy(final double dt) {
  318.         return StaticTransform.of(
  319.                 date.shiftedBy(dt),
  320.                 cartesian.positionShiftedBy(dt),
  321.                 angular.rotationShiftedBy(dt));
  322.     }

  323.     /**
  324.      * Create a so-called static transform from the instance.
  325.      *
  326.      * @return static part of the transform. It is static in the
  327.      * sense that it can only be used to transform directions and positions, but
  328.      * not velocities or accelerations.
  329.      * @see StaticTransform
  330.      */
  331.     public StaticTransform toStaticTransform() {
  332.         return StaticTransform.of(date, cartesian.getPosition(), angular.getRotation());
  333.     }

  334.     /** Interpolate a transform from a sample set of existing transforms.
  335.      * <p>
  336.      * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate,
  337.      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
  338.      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
  339.      * {@link AngularDerivativesFilter#USE_RRA}
  340.      * set to true.
  341.      * </p>
  342.      * @param interpolationDate interpolation date
  343.      * @param sample sample points on which interpolation should be done
  344.      * @return a new instance, interpolated at specified date
  345.      */
  346.     public Transform interpolate(final AbsoluteDate interpolationDate, final Stream<Transform> sample) {
  347.         return interpolate(interpolationDate,
  348.                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
  349.                            sample.collect(Collectors.toList()));
  350.     }

  351.     /** Interpolate a transform from a sample set of existing transforms.
  352.      * <p>
  353.      * Note that even if first time derivatives (velocities and rotation rates)
  354.      * from sample can be ignored, the interpolated instance always includes
  355.      * interpolated derivatives. This feature can be used explicitly to
  356.      * compute these derivatives when it would be too complex to compute them
  357.      * from an analytical formula: just compute a few sample points from the
  358.      * explicit formula and set the derivatives to zero in these sample points,
  359.      * then use interpolation to add derivatives consistent with the positions
  360.      * and rotations.
  361.      * </p>
  362.      * <p>
  363.      * As this implementation of interpolation is polynomial, it should be used only
  364.      * with small samples (about 10-20 points) in order to avoid <a
  365.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  366.      * and numerical problems (including NaN appearing).
  367.      * </p>
  368.      * @param date interpolation date
  369.      * @param cFilter filter for derivatives from the sample to use in interpolation
  370.      * @param aFilter filter for derivatives from the sample to use in interpolation
  371.      * @param sample sample points on which interpolation should be done
  372.      * @return a new instance, interpolated at specified date
  373.      * @since 7.0
  374.      */
  375.     public static Transform interpolate(final AbsoluteDate date,
  376.                                         final CartesianDerivativesFilter cFilter,
  377.                                         final AngularDerivativesFilter aFilter,
  378.                                         final Collection<Transform> sample) {

  379.         // Create samples
  380.         final List<TimeStampedPVCoordinates>      datedPV = new ArrayList<>(sample.size());
  381.         final List<TimeStampedAngularCoordinates> datedAC = new ArrayList<>(sample.size());
  382.         for (final Transform t : sample) {
  383.             datedPV.add(new TimeStampedPVCoordinates(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
  384.             datedAC.add(new TimeStampedAngularCoordinates(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
  385.         }

  386.         // Create interpolators
  387.         final TimeInterpolator<TimeStampedPVCoordinates> pvInterpolator =
  388.                 new TimeStampedPVCoordinatesHermiteInterpolator(datedPV.size(), cFilter);

  389.         final TimeInterpolator<TimeStampedAngularCoordinates> angularInterpolator =
  390.                 new TimeStampedAngularCoordinatesHermiteInterpolator(datedPV.size(), aFilter);

  391.         // Interpolate
  392.         final TimeStampedPVCoordinates      interpolatedPV = pvInterpolator.interpolate(date, datedPV);
  393.         final TimeStampedAngularCoordinates interpolatedAC = angularInterpolator.interpolate(date, datedAC);
  394.         return new Transform(date, interpolatedPV, interpolatedAC);
  395.     }

  396.     /** Get the inverse transform of the instance.
  397.      * @return inverse transform of the instance
  398.      */
  399.     @Override
  400.     public Transform getInverse() {

  401.         final Rotation r    = angular.getRotation();
  402.         final Vector3D o    = angular.getRotationRate();
  403.         final Vector3D oDot = angular.getRotationAcceleration();
  404.         final Vector3D rp   = r.applyTo(cartesian.getPosition());
  405.         final Vector3D rv   = r.applyTo(cartesian.getVelocity());
  406.         final Vector3D ra   = r.applyTo(cartesian.getAcceleration());

  407.         final Vector3D pInv        = rp.negate();
  408.         final Vector3D crossP      = Vector3D.crossProduct(o, rp);
  409.         final Vector3D vInv        = crossP.subtract(rv);
  410.         final Vector3D crossV      = Vector3D.crossProduct(o, rv);
  411.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot, rp);
  412.         final Vector3D crossCrossP = Vector3D.crossProduct(o, crossP);
  413.         final Vector3D aInv        = new Vector3D(-1, ra,
  414.                                                    2, crossV,
  415.                                                    1, crossDotP,
  416.                                                   -1, crossCrossP);

  417.         return new Transform(getDate(), new PVCoordinates(pInv, vInv, aInv), angular.revert());

  418.     }

  419.     /** Get a frozen transform.
  420.      * <p>
  421.      * This method creates a copy of the instance but frozen in time,
  422.      * i.e. with velocity, acceleration and rotation rate forced to zero.
  423.      * </p>
  424.      * @return a new transform, without any time-dependent parts
  425.      */
  426.     public Transform freeze() {
  427.         return new Transform(date,
  428.                              new PVCoordinates(cartesian.getPosition(), Vector3D.ZERO, Vector3D.ZERO),
  429.                              new AngularCoordinates(angular.getRotation(), Vector3D.ZERO, Vector3D.ZERO));
  430.     }

  431.     /** Transform {@link PVCoordinates} including kinematic effects.
  432.      * @param pva the position-velocity-acceleration triplet to transform.
  433.      * @return transformed position-velocity-acceleration
  434.      */
  435.     public PVCoordinates transformPVCoordinates(final PVCoordinates pva) {
  436.         return angular.applyTo(new PVCoordinates(1, pva, 1, cartesian));
  437.     }

  438.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  439.      * <p>
  440.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  441.      * consistency between the transform {@link #getDate() date} and the time-stamped
  442.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  443.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  444.      * the input argument, regardless of the instance {@link #getDate() date}.
  445.      * </p>
  446.      * @param pv time-stamped  position-velocity to transform.
  447.      * @return transformed time-stamped position-velocity
  448.      * @since 7.0
  449.      */
  450.     public TimeStampedPVCoordinates transformPVCoordinates(final TimeStampedPVCoordinates pv) {
  451.         return angular.applyTo(new TimeStampedPVCoordinates(pv.getDate(), 1, pv, 1, cartesian));
  452.     }

  453.     /** Transform {@link FieldPVCoordinates} including kinematic effects.
  454.      * @param pv position-velocity to transform.
  455.      * @param <T> type of the field elements
  456.      * @return transformed position-velocity
  457.      */
  458.     public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  459.         return angular.applyTo(new FieldPVCoordinates<>(pv.getPosition().add(cartesian.getPosition()),
  460.                                                         pv.getVelocity().add(cartesian.getVelocity()),
  461.                                                         pv.getAcceleration().add(cartesian.getAcceleration())));
  462.     }

  463.     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
  464.      * <p>
  465.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  466.      * consistency between the transform {@link #getDate() date} and the time-stamped
  467.      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
  468.      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
  469.      * the input argument, regardless of the instance {@link #getDate() date}.
  470.      * </p>
  471.      * @param pv time-stamped position-velocity to transform.
  472.      * @param <T> type of the field elements
  473.      * @return transformed time-stamped position-velocity
  474.      * @since 7.0
  475.      */
  476.     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {
  477.         return angular.applyTo(new TimeStampedFieldPVCoordinates<>(pv.getDate(),
  478.                                                                    pv.getPosition().add(cartesian.getPosition()),
  479.                                                                    pv.getVelocity().add(cartesian.getVelocity()),
  480.                                                                    pv.getAcceleration().add(cartesian.getAcceleration())));
  481.     }

  482.     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
  483.      * method of the transform.
  484.      * <p>
  485.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  486.      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
  487.      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
  488.      * </p>
  489.      * <p>
  490.      * This definition implies that if we define position-velocity coordinates
  491.      * <pre>
  492.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  493.      * </pre>
  494.      * <p> their differentials dPV₁ and dPV₀ will obey the following relation
  495.      * where J is the matrix computed by this method:
  496.      * <pre>
  497.      * dPV₁ = J &times; dPV₀
  498.      * </pre>
  499.      *
  500.      * @param selector selector specifying the size of the upper left corner that must be filled
  501.      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
  502.      * velocities and accelerations)
  503.      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
  504.      * the Jacobian, the rest of the matrix remaining untouched
  505.      */
  506.     public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {

  507.         // elementary matrix for rotation
  508.         final double[][] mData = angular.getRotation().getMatrix();

  509.         // dP1/dP0
  510.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  511.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  512.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

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

  514.             // dP1/dV0
  515.             Arrays.fill(jacobian[0], 3, 6, 0.0);
  516.             Arrays.fill(jacobian[1], 3, 6, 0.0);
  517.             Arrays.fill(jacobian[2], 3, 6, 0.0);

  518.             // dV1/dP0
  519.             final Vector3D o = angular.getRotationRate();
  520.             final double ox = o.getX();
  521.             final double oy = o.getY();
  522.             final double oz = o.getZ();
  523.             for (int i = 0; i < 3; ++i) {
  524.                 jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]);
  525.                 jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]);
  526.                 jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]);
  527.             }

  528.             // dV1/dV0
  529.             System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  530.             System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  531.             System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

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

  533.                 // dP1/dA0
  534.                 Arrays.fill(jacobian[0], 6, 9, 0.0);
  535.                 Arrays.fill(jacobian[1], 6, 9, 0.0);
  536.                 Arrays.fill(jacobian[2], 6, 9, 0.0);

  537.                 // dV1/dA0
  538.                 Arrays.fill(jacobian[3], 6, 9, 0.0);
  539.                 Arrays.fill(jacobian[4], 6, 9, 0.0);
  540.                 Arrays.fill(jacobian[5], 6, 9, 0.0);

  541.                 // dA1/dP0
  542.                 final Vector3D oDot = angular.getRotationAcceleration();
  543.                 final double oDotx  = oDot.getX();
  544.                 final double oDoty  = oDot.getY();
  545.                 final double oDotz  = oDot.getZ();
  546.                 for (int i = 0; i < 3; ++i) {
  547.                     jacobian[6][i] = -(oDoty * mData[2][i] - oDotz * mData[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]);
  548.                     jacobian[7][i] = -(oDotz * mData[0][i] - oDotx * mData[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]);
  549.                     jacobian[8][i] = -(oDotx * mData[1][i] - oDoty * mData[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]);
  550.                 }

  551.                 // dA1/dV0
  552.                 for (int i = 0; i < 3; ++i) {
  553.                     jacobian[6][i + 3] = -2 * (oy * mData[2][i] - oz * mData[1][i]);
  554.                     jacobian[7][i + 3] = -2 * (oz * mData[0][i] - ox * mData[2][i]);
  555.                     jacobian[8][i + 3] = -2 * (ox * mData[1][i] - oy * mData[0][i]);
  556.                 }

  557.                 // dA1/dA0
  558.                 System.arraycopy(mData[0], 0, jacobian[6], 6, 3);
  559.                 System.arraycopy(mData[1], 0, jacobian[7], 6, 3);
  560.                 System.arraycopy(mData[2], 0, jacobian[8], 6, 3);

  561.             }

  562.         }

  563.     }

  564.     /** Get the underlying elementary Cartesian part.
  565.      * <p>A transform can be uniquely represented as an elementary
  566.      * translation followed by an elementary rotation. This method
  567.      * returns this unique elementary translation with its derivative.</p>
  568.      * @return underlying elementary Cartesian part
  569.      * @see #getTranslation()
  570.      * @see #getVelocity()
  571.      */
  572.     public PVCoordinates getCartesian() {
  573.         return cartesian;
  574.     }

  575.     /** Get the underlying elementary translation.
  576.      * <p>A transform can be uniquely represented as an elementary
  577.      * translation followed by an elementary rotation. This method
  578.      * returns this unique elementary translation.</p>
  579.      * @return underlying elementary translation
  580.      * @see #getCartesian()
  581.      * @see #getVelocity()
  582.      * @see #getAcceleration()
  583.      */
  584.     public Vector3D getTranslation() {
  585.         return cartesian.getPosition();
  586.     }

  587.     /** Get the first time derivative of the translation.
  588.      * @return first time derivative of the translation
  589.      * @see #getCartesian()
  590.      * @see #getTranslation()
  591.      * @see #getAcceleration()
  592.      */
  593.     public Vector3D getVelocity() {
  594.         return cartesian.getVelocity();
  595.     }

  596.     /** Get the second time derivative of the translation.
  597.      * @return second time derivative of the translation
  598.      * @see #getCartesian()
  599.      * @see #getTranslation()
  600.      * @see #getVelocity()
  601.      */
  602.     public Vector3D getAcceleration() {
  603.         return cartesian.getAcceleration();
  604.     }

  605.     /** Get the underlying elementary angular part.
  606.      * <p>A transform can be uniquely represented as an elementary
  607.      * translation followed by an elementary rotation. This method
  608.      * returns this unique elementary rotation with its derivative.</p>
  609.      * @return underlying elementary angular part
  610.      * @see #getRotation()
  611.      * @see #getRotationRate()
  612.      * @see #getRotationAcceleration()
  613.      */
  614.     public AngularCoordinates getAngular() {
  615.         return angular;
  616.     }

  617.     /** Get the underlying elementary rotation.
  618.      * <p>A transform can be uniquely represented as an elementary
  619.      * translation followed by an elementary rotation. This method
  620.      * returns this unique elementary rotation.</p>
  621.      * @return underlying elementary rotation
  622.      * @see #getAngular()
  623.      * @see #getRotationRate()
  624.      * @see #getRotationAcceleration()
  625.      */
  626.     public Rotation getRotation() {
  627.         return angular.getRotation();
  628.     }

  629.     /** Get the first time derivative of the rotation.
  630.      * <p>The norm represents the angular rate.</p>
  631.      * @return First time derivative of the rotation
  632.      * @see #getAngular()
  633.      * @see #getRotation()
  634.      * @see #getRotationAcceleration()
  635.      */
  636.     public Vector3D getRotationRate() {
  637.         return angular.getRotationRate();
  638.     }

  639.     /** Get the second time derivative of the rotation.
  640.      * @return Second time derivative of the rotation
  641.      * @see #getAngular()
  642.      * @see #getRotation()
  643.      * @see #getRotationRate()
  644.      */
  645.     public Vector3D getRotationAcceleration() {
  646.         return angular.getRotationAcceleration();
  647.     }

  648.     /** Specialized class for identity transform. */
  649.     private static class IdentityTransform extends Transform {

  650.         /** Serializable UID. */
  651.         private static final long serialVersionUID = -9042082036141830517L;

  652.         /** Simple constructor. */
  653.         IdentityTransform() {
  654.             super(AbsoluteDate.ARBITRARY_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
  655.         }

  656.         /** {@inheritDoc} */
  657.         @Override
  658.         public Transform shiftedBy(final double dt) {
  659.             return this;
  660.         }

  661.         /** {@inheritDoc} */
  662.         @Override
  663.         public Transform getInverse() {
  664.             return this;
  665.         }

  666.         /** {@inheritDoc} */
  667.         @Override
  668.         public Vector3D transformPosition(final Vector3D position) {
  669.             return position;
  670.         }

  671.         /** {@inheritDoc} */
  672.         @Override
  673.         public Vector3D transformVector(final Vector3D vector) {
  674.             return vector;
  675.         }

  676.         /** {@inheritDoc} */
  677.         @Override
  678.         public Line transformLine(final Line line) {
  679.             return line;
  680.         }

  681.         /** {@inheritDoc} */
  682.         @Override
  683.         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
  684.             return pv;
  685.         }

  686.         @Override
  687.         public Transform freeze() {
  688.             return this;
  689.         }

  690.         @Override
  691.         public TimeStampedPVCoordinates transformPVCoordinates(
  692.                 final TimeStampedPVCoordinates pv) {
  693.             return pv;
  694.         }

  695.         @Override
  696.         public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T>
  697.             transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  698.             return pv;
  699.         }

  700.         @Override
  701.         public <T extends CalculusFieldElement<T>>
  702.             TimeStampedFieldPVCoordinates<T> transformPVCoordinates(
  703.                     final TimeStampedFieldPVCoordinates<T> pv) {
  704.             return pv;
  705.         }

  706.         /** {@inheritDoc} */
  707.         @Override
  708.         public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
  709.             final int n = 3 * (selector.getMaxOrder() + 1);
  710.             for (int i = 0; i < n; ++i) {
  711.                 Arrays.fill(jacobian[i], 0, n, 0.0);
  712.                 jacobian[i][i] = 1.0;
  713.             }
  714.         }

  715.     }

  716. }