Transform.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.RealFieldElement;
  26. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  27. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  28. import org.hipparchus.geometry.euclidean.threed.Line;
  29. import org.hipparchus.geometry.euclidean.threed.Rotation;
  30. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  31. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  32. import org.orekit.errors.OrekitException;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.time.TimeInterpolable;
  35. import org.orekit.time.TimeShiftable;
  36. import org.orekit.time.TimeStamped;
  37. import org.orekit.utils.AngularCoordinates;
  38. import org.orekit.utils.AngularDerivativesFilter;
  39. import org.orekit.utils.CartesianDerivativesFilter;
  40. import org.orekit.utils.FieldPVCoordinates;
  41. import org.orekit.utils.PVCoordinates;
  42. import org.orekit.utils.TimeStampedAngularCoordinates;
  43. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  44. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

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

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

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

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

  112.     /** Build a transform from its primitive operations.
  113.      * @param date date of the transform
  114.      * @param cartesian Cartesian coordinates of the target frame with respect to the original frame
  115.      * @param angular angular coordinates of the target frame with respect to the original frame
  116.      */
  117.     private Transform(final AbsoluteDate date,
  118.                       final PVCoordinates cartesian, final AngularCoordinates angular) {
  119.         this.date      = date;
  120.         this.cartesian = cartesian;
  121.         this.angular   = angular;
  122.     }

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

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

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

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

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

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

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

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

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

  243.     /** Compute a composite translation.
  244.      * @param first first applied transform
  245.      * @param second second applied transform
  246.      * @return translation part of the composite transform
  247.      */
  248.     private static Vector3D compositeTranslation(final Transform first, final Transform second) {

  249.         final Vector3D p1 = first.cartesian.getPosition();
  250.         final Rotation r1 = first.angular.getRotation();
  251.         final Vector3D p2 = second.cartesian.getPosition();

  252.         return p1.add(r1.applyInverseTo(p2));

  253.     }

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

  260.         final Vector3D v1 = first.cartesian.getVelocity();
  261.         final Rotation r1 = first.angular.getRotation();
  262.         final Vector3D o1 = first.angular.getRotationRate();
  263.         final Vector3D p2 = second.cartesian.getPosition();
  264.         final Vector3D v2 = second.cartesian.getVelocity();

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

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

  267.     }

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

  274.         final Vector3D a1    = first.cartesian.getAcceleration();
  275.         final Rotation r1    = first.angular.getRotation();
  276.         final Vector3D o1    = first.angular.getRotationRate();
  277.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  278.         final Vector3D p2    = second.cartesian.getPosition();
  279.         final Vector3D v2    = second.cartesian.getVelocity();
  280.         final Vector3D a2    = second.cartesian.getAcceleration();

  281.         final Vector3D crossCrossP = Vector3D.crossProduct(o1,    Vector3D.crossProduct(o1, p2));
  282.         final Vector3D crossV      = Vector3D.crossProduct(o1,    v2);
  283.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot1, p2);

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

  285.     }

  286.     /** Compute a composite rotation.
  287.      * @param first first applied transform
  288.      * @param second second applied transform
  289.      * @return rotation part of the composite transform
  290.      */
  291.     private static Rotation compositeRotation(final Transform first, final Transform second) {

  292.         final Rotation r1 = first.angular.getRotation();
  293.         final Rotation r2 = second.angular.getRotation();

  294.         return r1.compose(r2, RotationConvention.FRAME_TRANSFORM);

  295.     }

  296.     /** Compute a composite rotation rate.
  297.      * @param first first applied transform
  298.      * @param second second applied transform
  299.      * @return rotation rate part of the composite transform
  300.      */
  301.     private static Vector3D compositeRotationRate(final Transform first, final Transform second) {

  302.         final Vector3D o1 = first.angular.getRotationRate();
  303.         final Rotation r2 = second.angular.getRotation();
  304.         final Vector3D o2 = second.angular.getRotationRate();

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

  306.     }

  307.     /** Compute a composite rotation acceleration.
  308.      * @param first first applied transform
  309.      * @param second second applied transform
  310.      * @return rotation acceleration part of the composite transform
  311.      */
  312.     private static Vector3D compositeRotationAcceleration(final Transform first, final Transform second) {

  313.         final Vector3D o1    = first.angular.getRotationRate();
  314.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  315.         final Rotation r2    = second.angular.getRotation();
  316.         final Vector3D o2    = second.angular.getRotationRate();
  317.         final Vector3D oDot2 = second.angular.getRotationAcceleration();

  318.         return new Vector3D( 1, oDot2,
  319.                              1, r2.applyTo(oDot1),
  320.                             -1, Vector3D.crossProduct(o2, r2.applyTo(o1)));

  321.     }

  322.     /** {@inheritDoc} */
  323.     public AbsoluteDate getDate() {
  324.         return date;
  325.     }

  326.     /** {@inheritDoc} */
  327.     public Transform shiftedBy(final double dt) {
  328.         return new Transform(date.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  329.     }

  330.     /** {@inheritDoc}
  331.      * <p>
  332.      * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate,
  333.      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
  334.      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
  335.      * {@link AngularDerivativesFilter#USE_RRA}
  336.      * set to true.
  337.      * </p>
  338.      * @exception OrekitException if the number of point is too small for interpolating
  339.      */
  340.     public Transform interpolate(final AbsoluteDate interpolationDate, final Stream<Transform> sample)
  341.         throws OrekitException {
  342.         return interpolate(interpolationDate,
  343.                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
  344.                            sample.collect(Collectors.toList()));
  345.     }

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

  386.     /** Get the inverse transform of the instance.
  387.      * @return inverse transform of the instance
  388.      */
  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(date, 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 a position vector (including translation effects).
  421.      * @param position vector to transform
  422.      * @return transformed position
  423.      */
  424.     public Vector3D transformPosition(final Vector3D position) {
  425.         return angular.getRotation().applyTo(cartesian.getPosition().add(position));
  426.     }

  427.     /** Transform a position vector (including translation effects).
  428.      * @param position vector to transform
  429.      * @param <T> the type of the field elements
  430.      * @return transformed position
  431.      */
  432.     public <T extends RealFieldElement<T>> FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
  433.         return FieldRotation.applyTo(angular.getRotation(), position.add(cartesian.getPosition()));
  434.     }

  435.     /** Transform a vector (ignoring translation effects).
  436.      * @param vector vector to transform
  437.      * @return transformed vector
  438.      */
  439.     public Vector3D transformVector(final Vector3D vector) {
  440.         return angular.getRotation().applyTo(vector);
  441.     }

  442.     /** Transform a vector (ignoring translation effects).
  443.      * @param vector vector to transform
  444.      * @param <T> the type of the field elements
  445.      * @return transformed vector
  446.      */
  447.     public <T extends RealFieldElement<T>> FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
  448.         return FieldRotation.applyTo(angular.getRotation(), vector);
  449.     }

  450.     /** Transform a line.
  451.      * @param line to transform
  452.      * @return transformed line
  453.      */
  454.     public Line transformLine(final Line line) {
  455.         final Vector3D transformedP0 = transformPosition(line.getOrigin());
  456.         final Vector3D transformedP1 = transformPosition(line.pointAt(1.0e6));
  457.         return new Line(transformedP0, transformedP1, 1.0e-10);
  458.     }

  459.     /** Transform {@link PVCoordinates} including kinematic effects.
  460.      * @param pva the position-velocity-acceleration triplet to transform.
  461.      * @return transformed position-velocity-acceleration
  462.      */
  463.     public PVCoordinates transformPVCoordinates(final PVCoordinates pva) {
  464.         return angular.applyTo(new PVCoordinates(1, pva, 1, cartesian));
  465.     }

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

  481.     /** Transform {@link FieldPVCoordinates} including kinematic effects.
  482.      * @param pv position-velocity to transform.
  483.      * @param <T> type of the field elements
  484.      * @return transformed position-velocity
  485.      */
  486.     public <T extends RealFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
  487.         return angular.applyTo(new FieldPVCoordinates<>(pv.getPosition().add(cartesian.getPosition()),
  488.                                                         pv.getVelocity().add(cartesian.getVelocity()),
  489.                                                         pv.getAcceleration().add(cartesian.getAcceleration())));
  490.     }

  491.     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
  492.      * <p>
  493.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  494.      * consistency between the transform {@link #getDate() date} and the time-stamped
  495.      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
  496.      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
  497.      * the input argument, regardless of the instance {@link #getDate() date}.
  498.      * </p>
  499.      * @param pv time-stamped position-velocity to transform.
  500.      * @param <T> type of the field elements
  501.      * @return transformed time-stamped position-velocity
  502.      * @since 7.0
  503.      */
  504.     public <T extends RealFieldElement<T>> TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {
  505.         return angular.applyTo(new TimeStampedFieldPVCoordinates<>(pv.getDate(),
  506.                                                                    pv.getPosition().add(cartesian.getPosition()),
  507.                                                                    pv.getVelocity().add(cartesian.getVelocity()),
  508.                                                                    pv.getAcceleration().add(cartesian.getAcceleration())));
  509.     }

  510.     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
  511.      * method of the transform.
  512.      * <p>
  513.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  514.      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
  515.      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
  516.      * </p>
  517.      * <p>
  518.      * This definition implies that if we define position-velocity coordinates
  519.      * <pre>
  520.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  521.      * </pre>
  522.      * <p> their differentials dPV₁ and dPV₀ will obey the following relation
  523.      * where J is the matrix computed by this method:
  524.      * <pre>
  525.      * dPV₁ = J &times; dPV₀
  526.      * </pre>
  527.      *
  528.      * @param selector selector specifying the size of the upper left corner that must be filled
  529.      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
  530.      * velocities and accelerations)
  531.      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
  532.      * the Jacobian, the rest of the matrix remaining untouched
  533.      */
  534.     public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {

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

  537.         // dP1/dP0
  538.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  539.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  540.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

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

  542.             // dP1/dV0
  543.             Arrays.fill(jacobian[0], 3, 6, 0.0);
  544.             Arrays.fill(jacobian[1], 3, 6, 0.0);
  545.             Arrays.fill(jacobian[2], 3, 6, 0.0);

  546.             // dV1/dP0
  547.             final Vector3D o = angular.getRotationRate();
  548.             final double ox = o.getX();
  549.             final double oy = o.getY();
  550.             final double oz = o.getZ();
  551.             for (int i = 0; i < 3; ++i) {
  552.                 jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]);
  553.                 jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]);
  554.                 jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]);
  555.             }

  556.             // dV1/dV0
  557.             System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  558.             System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  559.             System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

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

  561.                 // dP1/dA0
  562.                 Arrays.fill(jacobian[0], 6, 9, 0.0);
  563.                 Arrays.fill(jacobian[1], 6, 9, 0.0);
  564.                 Arrays.fill(jacobian[2], 6, 9, 0.0);

  565.                 // dV1/dA0
  566.                 Arrays.fill(jacobian[3], 6, 9, 0.0);
  567.                 Arrays.fill(jacobian[4], 6, 9, 0.0);
  568.                 Arrays.fill(jacobian[5], 6, 9, 0.0);

  569.                 // dA1/dP0
  570.                 final Vector3D oDot = angular.getRotationAcceleration();
  571.                 final double oDotx  = oDot.getX();
  572.                 final double oDoty  = oDot.getY();
  573.                 final double oDotz  = oDot.getZ();
  574.                 for (int i = 0; i < 3; ++i) {
  575.                     jacobian[6][i] = -(oDoty * mData[2][i] - oDotz * mData[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]);
  576.                     jacobian[7][i] = -(oDotz * mData[0][i] - oDotx * mData[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]);
  577.                     jacobian[8][i] = -(oDotx * mData[1][i] - oDoty * mData[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]);
  578.                 }

  579.                 // dA1/dV0
  580.                 for (int i = 0; i < 3; ++i) {
  581.                     jacobian[6][i + 3] = -2 * (oy * mData[2][i] - oz * mData[1][i]);
  582.                     jacobian[7][i + 3] = -2 * (oz * mData[0][i] - ox * mData[2][i]);
  583.                     jacobian[8][i + 3] = -2 * (ox * mData[1][i] - oy * mData[0][i]);
  584.                 }

  585.                 // dA1/dA0
  586.                 System.arraycopy(mData[0], 0, jacobian[6], 6, 3);
  587.                 System.arraycopy(mData[1], 0, jacobian[7], 6, 3);
  588.                 System.arraycopy(mData[2], 0, jacobian[8], 6, 3);

  589.             }

  590.         }

  591.     }

  592.     /** Get the underlying elementary Cartesian part.
  593.      * <p>A transform can be uniquely represented as an elementary
  594.      * translation followed by an elementary rotation. This method
  595.      * returns this unique elementary translation with its derivative.</p>
  596.      * @return underlying elementary Cartesian part
  597.      * @see #getTranslation()
  598.      * @see #getVelocity()
  599.      */
  600.     public PVCoordinates getCartesian() {
  601.         return cartesian;
  602.     }

  603.     /** Get the underlying elementary translation.
  604.      * <p>A transform can be uniquely represented as an elementary
  605.      * translation followed by an elementary rotation. This method
  606.      * returns this unique elementary translation.</p>
  607.      * @return underlying elementary translation
  608.      * @see #getCartesian()
  609.      * @see #getVelocity()
  610.      * @see #getAcceleration()
  611.      */
  612.     public Vector3D getTranslation() {
  613.         return cartesian.getPosition();
  614.     }

  615.     /** Get the first time derivative of the translation.
  616.      * @return first time derivative of the translation
  617.      * @see #getCartesian()
  618.      * @see #getTranslation()
  619.      * @see #getAcceleration()
  620.      */
  621.     public Vector3D getVelocity() {
  622.         return cartesian.getVelocity();
  623.     }

  624.     /** Get the second time derivative of the translation.
  625.      * @return second time derivative of the translation
  626.      * @see #getCartesian()
  627.      * @see #getTranslation()
  628.      * @see #getVelocity()
  629.      */
  630.     public Vector3D getAcceleration() {
  631.         return cartesian.getAcceleration();
  632.     }

  633.     /** Get the underlying elementary angular part.
  634.      * <p>A transform can be uniquely represented as an elementary
  635.      * translation followed by an elementary rotation. This method
  636.      * returns this unique elementary rotation with its derivative.</p>
  637.      * @return underlying elementary angular part
  638.      * @see #getRotation()
  639.      * @see #getRotationRate()
  640.      * @see #getRotationAcceleration()
  641.      */
  642.     public AngularCoordinates getAngular() {
  643.         return angular;
  644.     }

  645.     /** Get the underlying elementary rotation.
  646.      * <p>A transform can be uniquely represented as an elementary
  647.      * translation followed by an elementary rotation. This method
  648.      * returns this unique elementary rotation.</p>
  649.      * @return underlying elementary rotation
  650.      * @see #getAngular()
  651.      * @see #getRotationRate()
  652.      * @see #getRotationAcceleration()
  653.      */
  654.     public Rotation getRotation() {
  655.         return angular.getRotation();
  656.     }

  657.     /** Get the first time derivative of the rotation.
  658.      * <p>The norm represents the angular rate.</p>
  659.      * @return First time derivative of the rotation
  660.      * @see #getAngular()
  661.      * @see #getRotation()
  662.      * @see #getRotationAcceleration()
  663.      */
  664.     public Vector3D getRotationRate() {
  665.         return angular.getRotationRate();
  666.     }

  667.     /** Get the second time derivative of the rotation.
  668.      * @return Second time derivative of the rotation
  669.      * @see #getAngular()
  670.      * @see #getRotation()
  671.      * @see #getRotationRate()
  672.      */
  673.     public Vector3D getRotationAcceleration() {
  674.         return angular.getRotationAcceleration();
  675.     }

  676.     /** Specialized class for identity transform. */
  677.     private static class IdentityTransform extends Transform {

  678.         /** Serializable UID. */
  679.         private static final long serialVersionUID = -9042082036141830517L;

  680.         /** Simple constructor. */
  681.         IdentityTransform() {
  682.             super(AbsoluteDate.J2000_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
  683.         }

  684.         /** {@inheritDoc} */
  685.         @Override
  686.         public Transform shiftedBy(final double dt) {
  687.             return this;
  688.         }

  689.         /** {@inheritDoc} */
  690.         @Override
  691.         public Transform getInverse() {
  692.             return this;
  693.         }

  694.         /** {@inheritDoc} */
  695.         @Override
  696.         public Vector3D transformPosition(final Vector3D position) {
  697.             return position;
  698.         }

  699.         /** {@inheritDoc} */
  700.         @Override
  701.         public Vector3D transformVector(final Vector3D vector) {
  702.             return vector;
  703.         }

  704.         /** {@inheritDoc} */
  705.         @Override
  706.         public Line transformLine(final Line line) {
  707.             return line;
  708.         }

  709.         /** {@inheritDoc} */
  710.         @Override
  711.         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
  712.             return pv;
  713.         }

  714.         /** {@inheritDoc} */
  715.         @Override
  716.         public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
  717.             final int n = 3 * (selector.getMaxOrder() + 1);
  718.             for (int i = 0; i < n; ++i) {
  719.                 Arrays.fill(jacobian[i], 0, n, 0.0);
  720.                 jacobian[i][i] = 1.0;
  721.             }
  722.         }

  723.     }

  724. }