Transform.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.Collectors;
  23. import java.util.stream.Stream;

  24. import org.hipparchus.CalculusFieldElement;
  25. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  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.TimeOffset;
  31. import org.orekit.time.TimeInterpolator;
  32. import org.orekit.time.TimeShiftable;
  33. import org.orekit.utils.AngularCoordinates;
  34. import org.orekit.utils.AngularDerivativesFilter;
  35. import org.orekit.utils.CartesianDerivativesFilter;
  36. import org.orekit.utils.FieldPVCoordinates;
  37. import org.orekit.utils.PVCoordinates;
  38. import org.orekit.utils.TimeStampedAngularCoordinates;
  39. import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator;
  40. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  41. import org.orekit.utils.TimeStampedPVCoordinates;
  42. import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;


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

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

  101.     /** Date of the transform. */
  102.     private final AbsoluteDate date;

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

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

  107.     /** Build a transform from its primitive operations.
  108.      * @param date date of the transform
  109.      * @param cartesian Cartesian coordinates of the target frame with respect to the original frame
  110.      * @param angular angular coordinates of the target frame with respect to the original frame
  111.      */
  112.     public Transform(final AbsoluteDate date, final PVCoordinates cartesian, final AngularCoordinates angular) {
  113.         this.date      = date;
  114.         this.cartesian = cartesian;
  115.         this.angular   = angular;
  116.     }

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

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

  139.     /** Build a combined translation and rotation transform.
  140.      * @param date date of the transform
  141.      * @param translation translation to apply (i.e. coordinates of
  142.      * the transformed origin, or coordinates of the origin of the
  143.      * old frame in the new frame)
  144.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  145.      * coordinates of a vector expressed in the old frame to obtain the
  146.      * same vector expressed in the new frame )
  147.      * @since 12.1
  148.      */
  149.     public Transform(final AbsoluteDate date, final Vector3D translation, final Rotation rotation) {
  150.         this(date, new PVCoordinates(translation), new AngularCoordinates(rotation));
  151.     }

  152.     /** Build a translation transform, with its first time derivative.
  153.      * @param date date of the transform
  154.      * @param translation translation to apply (i.e. coordinates of
  155.      * the transformed origin, or coordinates of the origin of the
  156.      * old frame in the new frame)
  157.      * @param velocity the velocity of the translation (i.e. origin
  158.      * of the old frame velocity in the new frame)
  159.      */
  160.     public Transform(final AbsoluteDate date, final Vector3D translation,
  161.                      final Vector3D velocity) {
  162.         this(date,
  163.              new PVCoordinates(translation, velocity, Vector3D.ZERO),
  164.              AngularCoordinates.IDENTITY);
  165.     }

  166.     /** Build a translation transform, with its first and second time derivatives.
  167.      * @param date date of the transform
  168.      * @param translation translation to apply (i.e. coordinates of
  169.      * the transformed origin, or coordinates of the origin of the
  170.      * old frame in the new frame)
  171.      * @param velocity the velocity of the translation (i.e. origin
  172.      * of the old frame velocity in the new frame)
  173.      * @param acceleration the acceleration of the translation (i.e. origin
  174.      * of the old frame acceleration in the new frame)
  175.      */
  176.     public Transform(final AbsoluteDate date, final Vector3D translation,
  177.                      final Vector3D velocity, final Vector3D acceleration) {
  178.         this(date,
  179.              new PVCoordinates(translation, velocity, acceleration),
  180.              AngularCoordinates.IDENTITY);
  181.     }

  182.     /** Build a translation transform, with its first time derivative.
  183.      * @param date date of the transform
  184.      * @param cartesian Cartesian part of the transformation to apply (i.e. coordinates of
  185.      * the transformed origin, or coordinates of the origin of the
  186.      * old frame in the new frame, with their derivatives)
  187.      */
  188.     public Transform(final AbsoluteDate date, final PVCoordinates cartesian) {
  189.         this(date,
  190.              cartesian,
  191.              AngularCoordinates.IDENTITY);
  192.     }

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

  206.     /** Build a rotation transform.
  207.      * @param date date of the transform
  208.      * @param rotation rotation to apply ( i.e. rotation to apply to the
  209.      * coordinates of a vector expressed in the old frame to obtain the
  210.      * same vector expressed in the new frame )
  211.      * @param rotationRate the axis of the instant rotation
  212.      * @param rotationAcceleration the axis of the instant rotation
  213.      * expressed in the new frame. (norm representing angular rate)
  214.      */
  215.     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate,
  216.                      final Vector3D rotationAcceleration) {
  217.         this(date,
  218.              PVCoordinates.ZERO,
  219.              new AngularCoordinates(rotation, rotationRate, rotationAcceleration));
  220.     }

  221.     /** Build a rotation transform.
  222.      * @param date date of the transform
  223.      * @param angular angular part of the transformation to apply (i.e. rotation to
  224.      * apply to the coordinates of a vector expressed in the old frame to obtain the
  225.      * same vector expressed in the new frame, with its rotation rate)
  226.      */
  227.     public Transform(final AbsoluteDate date, final AngularCoordinates angular) {
  228.         this(date, PVCoordinates.ZERO, angular);
  229.     }

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

  250.     /** Compute a composite acceleration.
  251.      * @param first first applied transform
  252.      * @param second second applied transform
  253.      * @return acceleration part of the composite transform
  254.      */
  255.     private static Vector3D compositeAcceleration(final Transform first, final Transform second) {

  256.         final Vector3D a1    = first.cartesian.getAcceleration();
  257.         final Rotation r1    = first.angular.getRotation();
  258.         final Vector3D o1    = first.angular.getRotationRate();
  259.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  260.         final Vector3D p2    = second.cartesian.getPosition();
  261.         final Vector3D v2    = second.cartesian.getVelocity();
  262.         final Vector3D a2    = second.cartesian.getAcceleration();

  263.         final Vector3D crossCrossP = Vector3D.crossProduct(o1,    Vector3D.crossProduct(o1, p2));
  264.         final Vector3D crossV      = Vector3D.crossProduct(o1,    v2);
  265.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot1, p2);

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

  267.     }

  268.     /** Compute a composite rotation acceleration.
  269.      * @param first first applied transform
  270.      * @param second second applied transform
  271.      * @return rotation acceleration part of the composite transform
  272.      */
  273.     private static Vector3D compositeRotationAcceleration(final Transform first, final Transform second) {

  274.         final Vector3D o1    = first.angular.getRotationRate();
  275.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  276.         final Rotation r2    = second.angular.getRotation();
  277.         final Vector3D o2    = second.angular.getRotationRate();
  278.         final Vector3D oDot2 = second.angular.getRotationAcceleration();

  279.         return new Vector3D( 1, oDot2,
  280.                              1, r2.applyTo(oDot1),
  281.                             -1, Vector3D.crossProduct(o2, r2.applyTo(o1)));

  282.     }

  283.     /** {@inheritDoc} */
  284.     public AbsoluteDate getDate() {
  285.         return date;
  286.     }

  287.     /** {@inheritDoc} */
  288.     public Transform shiftedBy(final double dt) {
  289.         return shiftedBy(new TimeOffset(dt));
  290.     }

  291.     /** {@inheritDoc} */
  292.     @Override
  293.     public Transform shiftedBy(final TimeOffset dt) {
  294.         return new Transform(date.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  295.     }

  296.     /**
  297.      * Shift the transform in time considering all rates, then return only the
  298.      * translation and rotation portion of the transform.
  299.      *
  300.      * @param dt time shift in seconds.
  301.      * @return shifted transform as a static transform. It is static in the
  302.      * sense that it can only be used to transform directions and positions, but
  303.      * not velocities or accelerations.
  304.      * @see #shiftedBy(double)
  305.      */
  306.     public StaticTransform staticShiftedBy(final double dt) {
  307.         return StaticTransform.of(
  308.                 date.shiftedBy(dt),
  309.                 cartesian.positionShiftedBy(dt),
  310.                 angular.rotationShiftedBy(dt));
  311.     }

  312.     /**
  313.      * Create a so-called static transform from the instance.
  314.      *
  315.      * @return static part of the transform. It is static in the
  316.      * sense that it can only be used to transform directions and positions, but
  317.      * not velocities or accelerations.
  318.      * @see StaticTransform
  319.      */
  320.     public StaticTransform toStaticTransform() {
  321.         return StaticTransform.of(date, cartesian.getPosition(), angular.getRotation());
  322.     }

  323.     /** Interpolate a transform from a sample set of existing transforms.
  324.      * <p>
  325.      * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate,
  326.      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
  327.      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
  328.      * {@link AngularDerivativesFilter#USE_RRA}
  329.      * set to true.
  330.      * </p>
  331.      * @param interpolationDate interpolation date
  332.      * @param sample sample points on which interpolation should be done
  333.      * @return a new instance, interpolated at specified date
  334.      */
  335.     public Transform interpolate(final AbsoluteDate interpolationDate, final Stream<Transform> sample) {
  336.         return interpolate(interpolationDate,
  337.                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
  338.                            sample.collect(Collectors.toList()));
  339.     }

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

  368.         // Create samples
  369.         final List<TimeStampedPVCoordinates>      datedPV = new ArrayList<>(sample.size());
  370.         final List<TimeStampedAngularCoordinates> datedAC = new ArrayList<>(sample.size());
  371.         for (final Transform t : sample) {
  372.             datedPV.add(new TimeStampedPVCoordinates(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
  373.             datedAC.add(new TimeStampedAngularCoordinates(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
  374.         }

  375.         // Create interpolators
  376.         final TimeInterpolator<TimeStampedPVCoordinates> pvInterpolator =
  377.                 new TimeStampedPVCoordinatesHermiteInterpolator(datedPV.size(), cFilter);

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

  380.         // Interpolate
  381.         final TimeStampedPVCoordinates      interpolatedPV = pvInterpolator.interpolate(date, datedPV);
  382.         final TimeStampedAngularCoordinates interpolatedAC = angularInterpolator.interpolate(date, datedAC);
  383.         return new Transform(date, interpolatedPV, interpolatedAC);
  384.     }

  385.     /** Get the inverse transform of the instance.
  386.      * @return inverse transform of the instance
  387.      */
  388.     @Override
  389.     public Transform getInverse() {

  390.         final Rotation r    = angular.getRotation();
  391.         final Vector3D o    = angular.getRotationRate();
  392.         final Vector3D oDot = angular.getRotationAcceleration();
  393.         final Vector3D rp   = r.applyTo(cartesian.getPosition());
  394.         final Vector3D rv   = r.applyTo(cartesian.getVelocity());
  395.         final Vector3D ra   = r.applyTo(cartesian.getAcceleration());

  396.         final Vector3D pInv        = rp.negate();
  397.         final Vector3D crossP      = Vector3D.crossProduct(o, rp);
  398.         final Vector3D vInv        = crossP.subtract(rv);
  399.         final Vector3D crossV      = Vector3D.crossProduct(o, rv);
  400.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot, rp);
  401.         final Vector3D crossCrossP = Vector3D.crossProduct(o, crossP);
  402.         final Vector3D aInv        = new Vector3D(-1, ra,
  403.                                                    2, crossV,
  404.                                                    1, crossDotP,
  405.                                                   -1, crossCrossP);

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

  407.     }

  408.     /** Get a frozen transform.
  409.      * <p>
  410.      * This method creates a copy of the instance but frozen in time,
  411.      * i.e. with velocity, acceleration and rotation rate forced to zero.
  412.      * </p>
  413.      * @return a new transform, without any time-dependent parts
  414.      */
  415.     public Transform freeze() {
  416.         return new Transform(date,
  417.                              new PVCoordinates(cartesian.getPosition(), Vector3D.ZERO, Vector3D.ZERO),
  418.                              new AngularCoordinates(angular.getRotation(), Vector3D.ZERO, Vector3D.ZERO));
  419.     }

  420.     /** Transform {@link PVCoordinates} including kinematic effects.
  421.      * @param pva the position-velocity-acceleration triplet to transform.
  422.      * @return transformed position-velocity-acceleration
  423.      */
  424.     public PVCoordinates transformPVCoordinates(final PVCoordinates pva) {
  425.         return angular.applyTo(new PVCoordinates(1, pva, 1, cartesian));
  426.     }

  427.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  428.      * <p>
  429.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  430.      * consistency between the transform {@link #getDate() date} and the time-stamped
  431.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  432.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  433.      * the input argument, regardless of the instance {@link #getDate() date}.
  434.      * </p>
  435.      * @param pv time-stamped  position-velocity to transform.
  436.      * @return transformed time-stamped position-velocity
  437.      * @since 7.0
  438.      */
  439.     public TimeStampedPVCoordinates transformPVCoordinates(final TimeStampedPVCoordinates pv) {
  440.         return angular.applyTo(new TimeStampedPVCoordinates(pv.getDate(), 1, pv, 1, cartesian));
  441.     }

  442.     /** Transform {@link FieldPVCoordinates} including kinematic effects.
  443.      * @param pv position-velocity to transform.
  444.      * @param <T> type of the field elements
  445.      * @return transformed position-velocity
  446.      */
  447.     public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  448.         return angular.applyTo(new FieldPVCoordinates<>(pv.getPosition().add(cartesian.getPosition()),
  449.                                                         pv.getVelocity().add(cartesian.getVelocity()),
  450.                                                         pv.getAcceleration().add(cartesian.getAcceleration())));
  451.     }

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

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

  496.         if (selector.getMaxOrder() == 0) {
  497.             // elementary matrix for rotation
  498.             final double[][] mData = angular.getRotation().getMatrix();

  499.             // dP1/dP0
  500.             System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  501.             System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  502.             System.arraycopy(mData[2], 0, jacobian[2], 0, 3);
  503.         }

  504.         else if (selector.getMaxOrder() == 1) {
  505.             // use KinematicTransform Jacobian
  506.             final double[][] mData = getPVJacobian();
  507.             for (int i = 0; i < mData.length; i++) {
  508.                 System.arraycopy(mData[i], 0, jacobian[i], 0, mData[i].length);
  509.             }
  510.         }

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

  513.             // dP1/dA0
  514.             Arrays.fill(jacobian[0], 6, 9, 0.0);
  515.             Arrays.fill(jacobian[1], 6, 9, 0.0);
  516.             Arrays.fill(jacobian[2], 6, 9, 0.0);

  517.             // dV1/dA0
  518.             Arrays.fill(jacobian[3], 6, 9, 0.0);
  519.             Arrays.fill(jacobian[4], 6, 9, 0.0);
  520.             Arrays.fill(jacobian[5], 6, 9, 0.0);

  521.             // dA1/dP0
  522.             final Vector3D o = angular.getRotationRate();
  523.             final double ox = o.getX();
  524.             final double oy = o.getY();
  525.             final double oz = o.getZ();
  526.             final Vector3D oDot = angular.getRotationAcceleration();
  527.             final double oDotx  = oDot.getX();
  528.             final double oDoty  = oDot.getY();
  529.             final double oDotz  = oDot.getZ();
  530.             for (int i = 0; i < 3; ++i) {
  531.                 jacobian[6][i] = -(oDoty * jacobian[2][i] - oDotz * jacobian[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]);
  532.                 jacobian[7][i] = -(oDotz * jacobian[0][i] - oDotx * jacobian[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]);
  533.                 jacobian[8][i] = -(oDotx * jacobian[1][i] - oDoty * jacobian[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]);
  534.             }

  535.             // dA1/dV0
  536.             for (int i = 0; i < 3; ++i) {
  537.                 jacobian[6][i + 3] = -2 * (oy * jacobian[2][i] - oz * jacobian[1][i]);
  538.                 jacobian[7][i + 3] = -2 * (oz * jacobian[0][i] - ox * jacobian[2][i]);
  539.                 jacobian[8][i + 3] = -2 * (ox * jacobian[1][i] - oy * jacobian[0][i]);
  540.             }

  541.             // dA1/dA0
  542.             System.arraycopy(jacobian[0], 0, jacobian[6], 6, 3);
  543.             System.arraycopy(jacobian[1], 0, jacobian[7], 6, 3);
  544.             System.arraycopy(jacobian[2], 0, jacobian[8], 6, 3);

  545.         }
  546.     }

  547.     /** Get the underlying elementary Cartesian part.
  548.      * <p>A transform can be uniquely represented as an elementary
  549.      * translation followed by an elementary rotation. This method
  550.      * returns this unique elementary translation with its derivative.</p>
  551.      * @return underlying elementary Cartesian part
  552.      * @see #getTranslation()
  553.      * @see #getVelocity()
  554.      */
  555.     public PVCoordinates getCartesian() {
  556.         return cartesian;
  557.     }

  558.     /** Get the underlying elementary translation.
  559.      * <p>A transform can be uniquely represented as an elementary
  560.      * translation followed by an elementary rotation. This method
  561.      * returns this unique elementary translation.</p>
  562.      * @return underlying elementary translation
  563.      * @see #getCartesian()
  564.      * @see #getVelocity()
  565.      * @see #getAcceleration()
  566.      */
  567.     public Vector3D getTranslation() {
  568.         return cartesian.getPosition();
  569.     }

  570.     /** Get the first time derivative of the translation.
  571.      * @return first time derivative of the translation
  572.      * @see #getCartesian()
  573.      * @see #getTranslation()
  574.      * @see #getAcceleration()
  575.      */
  576.     public Vector3D getVelocity() {
  577.         return cartesian.getVelocity();
  578.     }

  579.     /** Get the second time derivative of the translation.
  580.      * @return second time derivative of the translation
  581.      * @see #getCartesian()
  582.      * @see #getTranslation()
  583.      * @see #getVelocity()
  584.      */
  585.     public Vector3D getAcceleration() {
  586.         return cartesian.getAcceleration();
  587.     }

  588.     /** Get the underlying elementary angular part.
  589.      * <p>A transform can be uniquely represented as an elementary
  590.      * translation followed by an elementary rotation. This method
  591.      * returns this unique elementary rotation with its derivative.</p>
  592.      * @return underlying elementary angular part
  593.      * @see #getRotation()
  594.      * @see #getRotationRate()
  595.      * @see #getRotationAcceleration()
  596.      */
  597.     public AngularCoordinates getAngular() {
  598.         return angular;
  599.     }

  600.     /** Get the underlying elementary rotation.
  601.      * <p>A transform can be uniquely represented as an elementary
  602.      * translation followed by an elementary rotation. This method
  603.      * returns this unique elementary rotation.</p>
  604.      * @return underlying elementary rotation
  605.      * @see #getAngular()
  606.      * @see #getRotationRate()
  607.      * @see #getRotationAcceleration()
  608.      */
  609.     public Rotation getRotation() {
  610.         return angular.getRotation();
  611.     }

  612.     /** Get the first time derivative of the rotation.
  613.      * <p>The norm represents the angular rate.</p>
  614.      * @return First time derivative of the rotation
  615.      * @see #getAngular()
  616.      * @see #getRotation()
  617.      * @see #getRotationAcceleration()
  618.      */
  619.     public Vector3D getRotationRate() {
  620.         return angular.getRotationRate();
  621.     }

  622.     /** Get the second time derivative of the rotation.
  623.      * @return Second time derivative of the rotation
  624.      * @see #getAngular()
  625.      * @see #getRotation()
  626.      * @see #getRotationRate()
  627.      */
  628.     public Vector3D getRotationAcceleration() {
  629.         return angular.getRotationAcceleration();
  630.     }

  631.     /** Specialized class for identity transform. */
  632.     private static class IdentityTransform extends Transform {

  633.         /** Simple constructor. */
  634.         IdentityTransform() {
  635.             super(AbsoluteDate.ARBITRARY_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
  636.         }

  637.         @Override
  638.         public StaticTransform staticShiftedBy(final double dt) {
  639.             return toStaticTransform();
  640.         }

  641.         /** {@inheritDoc} */
  642.         @Override
  643.         public Transform shiftedBy(final double dt) {
  644.             return this;
  645.         }

  646.         @Override
  647.         public StaticTransform getStaticInverse() {
  648.             return toStaticTransform();
  649.         }

  650.         /** {@inheritDoc} */
  651.         @Override
  652.         public Transform shiftedBy(final TimeOffset dt) {
  653.             return this;
  654.         }

  655.         /** {@inheritDoc} */
  656.         @Override
  657.         public Transform getInverse() {
  658.             return this;
  659.         }

  660.         @Override
  661.         public StaticTransform toStaticTransform() {
  662.             return StaticTransform.getIdentity();
  663.         }

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

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

  674.         @Override
  675.         public <T extends CalculusFieldElement<T>> FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
  676.             return transformVector(position);
  677.         }

  678.         @Override
  679.         public <T extends CalculusFieldElement<T>> FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
  680.             return new FieldVector3D<>(vector.getX(), vector.getY(), vector.getZ());
  681.         }

  682.         /** {@inheritDoc} */
  683.         @Override
  684.         public Line transformLine(final Line line) {
  685.             return line;
  686.         }

  687.         /** {@inheritDoc} */
  688.         @Override
  689.         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
  690.             return pv;
  691.         }

  692.         @Override
  693.         public PVCoordinates transformOnlyPV(final PVCoordinates pv) {
  694.             return new PVCoordinates(pv.getPosition(), pv.getVelocity());
  695.         }

  696.         @Override
  697.         public TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) {
  698.             return new TimeStampedPVCoordinates(pv.getDate(), pv.getPosition(), pv.getVelocity());
  699.         }

  700.         @Override
  701.         public Transform freeze() {
  702.             return this;
  703.         }

  704.         @Override
  705.         public TimeStampedPVCoordinates transformPVCoordinates(
  706.                 final TimeStampedPVCoordinates pv) {
  707.             return pv;
  708.         }

  709.         @Override
  710.         public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T>
  711.             transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  712.             return pv;
  713.         }

  714.         @Override
  715.         public <T extends CalculusFieldElement<T>>
  716.             TimeStampedFieldPVCoordinates<T> transformPVCoordinates(
  717.                     final TimeStampedFieldPVCoordinates<T> pv) {
  718.             return pv;
  719.         }

  720.         /** {@inheritDoc} */
  721.         @Override
  722.         public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
  723.             final int n = 3 * (selector.getMaxOrder() + 1);
  724.             for (int i = 0; i < n; ++i) {
  725.                 Arrays.fill(jacobian[i], 0, n, 0.0);
  726.                 jacobian[i][i] = 1.0;
  727.             }
  728.         }

  729.     }

  730. }