Transform.java

  1. /* Copyright 2002-2016 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 org.apache.commons.math3.RealFieldElement;
  24. import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
  25. import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
  26. import org.apache.commons.math3.geometry.euclidean.threed.Line;
  27. import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
  28. import org.apache.commons.math3.geometry.euclidean.threed.RotationConvention;
  29. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.time.TimeInterpolable;
  33. import org.orekit.time.TimeShiftable;
  34. import org.orekit.time.TimeStamped;
  35. import org.orekit.utils.AngularCoordinates;
  36. import org.orekit.utils.AngularDerivativesFilter;
  37. import org.orekit.utils.CartesianDerivativesFilter;
  38. import org.orekit.utils.FieldPVCoordinates;
  39. import org.orekit.utils.PVCoordinates;
  40. import org.orekit.utils.TimeStampedAngularCoordinates;
  41. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  42. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

  101.     /** Serializable UID. */
  102.     private static final long serialVersionUID = 210140410L;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  246.         final Vector3D p1 = first.cartesian.getPosition();
  247.         final Rotation r1 = first.angular.getRotation();
  248.         final Vector3D p2 = second.cartesian.getPosition();

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

  250.     }

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

  257.         final Vector3D v1 = first.cartesian.getVelocity();
  258.         final Rotation r1 = first.angular.getRotation();
  259.         final Vector3D o1 = first.angular.getRotationRate();
  260.         final Vector3D p2 = second.cartesian.getPosition();
  261.         final Vector3D v2 = second.cartesian.getVelocity();

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

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

  264.     }

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

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

  278.         final Vector3D crossCrossP = Vector3D.crossProduct(o1,    Vector3D.crossProduct(o1, p2));
  279.         final Vector3D crossV      = Vector3D.crossProduct(o1,    v2);
  280.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot1, p2);

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

  282.     }

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

  289.         final Rotation r1 = first.angular.getRotation();
  290.         final Rotation r2 = second.angular.getRotation();

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

  292.     }

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

  299.         final Vector3D o1 = first.angular.getRotationRate();
  300.         final Rotation r2 = second.angular.getRotation();
  301.         final Vector3D o2 = second.angular.getRotationRate();

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

  303.     }

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

  310.         final Vector3D o1    = first.angular.getRotationRate();
  311.         final Vector3D oDot1 = first.angular.getRotationAcceleration();
  312.         final Rotation r2    = second.angular.getRotation();
  313.         final Vector3D o2    = second.angular.getRotationRate();
  314.         final Vector3D oDot2 = second.angular.getRotationAcceleration();

  315.         return new Vector3D( 1, oDot2,
  316.                              1, r2.applyTo(oDot1),
  317.                             -1, Vector3D.crossProduct(o2, r2.applyTo(o1)));

  318.     }

  319.     /** {@inheritDoc} */
  320.     public AbsoluteDate getDate() {
  321.         return date;
  322.     }

  323.     /** {@inheritDoc} */
  324.     public Transform shiftedBy(final double dt) {
  325.         return new Transform(date.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt));
  326.     };

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

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

  380.     /** Interpolate a transform from a sample set of existing transforms.
  381.      * <p>
  382.      * Note that even if first time derivatives (velocities and rotation rates)
  383.      * from sample can be ignored, the interpolated instance always includes
  384.      * interpolated derivatives. This feature can be used explicitly to
  385.      * compute these derivatives when it would be too complex to compute them
  386.      * from an analytical formula: just compute a few sample points from the
  387.      * explicit formula and set the derivatives to zero in these sample points,
  388.      * then use interpolation to add derivatives consistent with the positions
  389.      * and rotations.
  390.      * </p>
  391.      * <p>
  392.      * As this implementation of interpolation is polynomial, it should be used only
  393.      * with small samples (about 10-20 points) in order to avoid <a
  394.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  395.      * and numerical problems (including NaN appearing).
  396.      * </p>
  397.      * @param date interpolation date
  398.      * @param cFilter filter for derivatives from the sample to use in interpolation
  399.      * @param aFilter filter for derivatives from the sample to use in interpolation
  400.      * @param sample sample points on which interpolation should be done
  401.      * @return a new instance, interpolated at specified date
  402.      * @exception OrekitException if the number of point is too small for interpolating
  403.      * @since 7.0
  404.      */
  405.     public static Transform interpolate(final AbsoluteDate date,
  406.                                         final CartesianDerivativesFilter cFilter,
  407.                                         final AngularDerivativesFilter aFilter,
  408.                                         final Collection<Transform> sample)
  409.         throws OrekitException {
  410.         final List<TimeStampedPVCoordinates>      datedPV = new ArrayList<TimeStampedPVCoordinates>(sample.size());
  411.         final List<TimeStampedAngularCoordinates> datedAC = new ArrayList<TimeStampedAngularCoordinates>(sample.size());
  412.         for (final Transform t : sample) {
  413.             datedPV.add(new TimeStampedPVCoordinates(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
  414.             datedAC.add(new TimeStampedAngularCoordinates(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
  415.         }
  416.         final TimeStampedPVCoordinates      interpolatedPV = TimeStampedPVCoordinates.interpolate(date, cFilter, datedPV);
  417.         final TimeStampedAngularCoordinates interpolatedAC = TimeStampedAngularCoordinates.interpolate(date, aFilter, datedAC);
  418.         return new Transform(date, interpolatedPV, interpolatedAC);
  419.     }

  420.     /** Get the inverse transform of the instance.
  421.      * @return inverse transform of the instance
  422.      */
  423.     public Transform getInverse() {

  424.         final Rotation r    = angular.getRotation();
  425.         final Vector3D o    = angular.getRotationRate();
  426.         final Vector3D oDot = angular.getRotationAcceleration();
  427.         final Vector3D rp   = r.applyTo(cartesian.getPosition());
  428.         final Vector3D rv   = r.applyTo(cartesian.getVelocity());
  429.         final Vector3D ra   = r.applyTo(cartesian.getAcceleration());

  430.         final Vector3D pInv        = rp.negate();
  431.         final Vector3D crossP      = Vector3D.crossProduct(o, rp);
  432.         final Vector3D vInv        = crossP.subtract(rv);
  433.         final Vector3D crossV      = Vector3D.crossProduct(o, rv);
  434.         final Vector3D crossDotP   = Vector3D.crossProduct(oDot, rp);
  435.         final Vector3D crossCrossP = Vector3D.crossProduct(o, crossP);
  436.         final Vector3D aInv        = new Vector3D(-1, ra,
  437.                                                    2, crossV,
  438.                                                    1, crossDotP,
  439.                                                   -1, crossCrossP);

  440.         return new Transform(date, new PVCoordinates(pInv, vInv, aInv), angular.revert());

  441.     }

  442.     /** Get a frozen transform.
  443.      * <p>
  444.      * This method creates a copy of the instance but frozen in time,
  445.      * i.e. with velocity, acceleration and rotation rate forced to zero.
  446.      * </p>
  447.      * @return a new transform, without any time-dependent parts
  448.      */
  449.     public Transform freeze() {
  450.         return new Transform(date,
  451.                              new PVCoordinates(cartesian.getPosition(), Vector3D.ZERO, Vector3D.ZERO),
  452.                              new AngularCoordinates(angular.getRotation(), Vector3D.ZERO, Vector3D.ZERO));
  453.     }

  454.     /** Transform a position vector (including translation effects).
  455.      * @param position vector to transform
  456.      * @return transformed position
  457.      */
  458.     public Vector3D transformPosition(final Vector3D position) {
  459.         return angular.getRotation().applyTo(cartesian.getPosition().add(position));
  460.     }

  461.     /** Transform a position vector (including translation effects).
  462.      * @param position vector to transform
  463.      * @param <T> the type of the field elements
  464.      * @return transformed position
  465.      */
  466.     public <T extends RealFieldElement<T>> FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
  467.         return FieldRotation.applyTo(angular.getRotation(), position.add(cartesian.getPosition()));
  468.     }

  469.     /** Transform a vector (ignoring translation effects).
  470.      * @param vector vector to transform
  471.      * @return transformed vector
  472.      */
  473.     public Vector3D transformVector(final Vector3D vector) {
  474.         return angular.getRotation().applyTo(vector);
  475.     }

  476.     /** Transform a vector (ignoring translation effects).
  477.      * @param vector vector to transform
  478.      * @param <T> the type of the field elements
  479.      * @return transformed vector
  480.      */
  481.     public <T extends RealFieldElement<T>> FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
  482.         return FieldRotation.applyTo(angular.getRotation(), vector);
  483.     }

  484.     /** Transform a line.
  485.      * @param line to transform
  486.      * @return transformed line
  487.      */
  488.     public Line transformLine(final Line line) {
  489.         final Vector3D transformedP0 = transformPosition(line.getOrigin());
  490.         final Vector3D transformedP1 = transformPosition(line.pointAt(1.0e6));
  491.         return new Line(transformedP0, transformedP1, 1.0e-10);
  492.     }

  493.     /** Transform {@link PVCoordinates} including kinematic effects.
  494.      * @param pva the position-velocity-acceleration triplet to transform.
  495.      * @return transformed position-velocity-acceleration
  496.      */
  497.     public PVCoordinates transformPVCoordinates(final PVCoordinates pva) {
  498.         return angular.applyTo(new PVCoordinates(1, pva, 1, cartesian));
  499.     }

  500.     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
  501.      * <p>
  502.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  503.      * consistency between the transform {@link #getDate() date} and the time-stamped
  504.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  505.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  506.      * the input argument, regardless of the instance {@link #getDate() date}.
  507.      * </p>
  508.      * @param pv time-stamped  position-velocity to transform.
  509.      * @return transformed time-stamped position-velocity
  510.      * @since 7.0
  511.      */
  512.     public TimeStampedPVCoordinates transformPVCoordinates(final TimeStampedPVCoordinates pv) {
  513.         return angular.applyTo(new TimeStampedPVCoordinates(pv.getDate(), 1, pv, 1, cartesian));
  514.     }

  515.     /** Transform {@link FieldPVCoordinates} including kinematic effects.
  516.      * @param pv position-velocity to transform.
  517.      * @param <T> type of the field elements
  518.      * @return transformed position-velocity
  519.      */
  520.     public <T extends RealFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {

  521.         // apply translation
  522.         final FieldVector3D<T> intermediateP = pv.getPosition().add(cartesian.getPosition());
  523.         final FieldVector3D<T> intermediateV = pv.getVelocity().add(cartesian.getVelocity());
  524.         final FieldVector3D<T> intermediateA = pv.getAcceleration().add(cartesian.getAcceleration());

  525.         // apply rotation
  526.         final FieldVector3D<T> transformedP = FieldRotation.applyTo(angular.getRotation(), intermediateP);
  527.         final FieldVector3D<T> crossP       = FieldVector3D.crossProduct(angular.getRotationRate(), transformedP);
  528.         final FieldVector3D<T> transformedV = FieldRotation.applyTo(angular.getRotation(), intermediateV).subtract(crossP);
  529.         final FieldVector3D<T> crossV       = FieldVector3D.crossProduct(angular.getRotationRate(), transformedV);
  530.         final FieldVector3D<T> crossCrossP  = FieldVector3D.crossProduct(angular.getRotationRate(), crossP);
  531.         final FieldVector3D<T> crossDotP    = FieldVector3D.crossProduct(angular.getRotationAcceleration(), transformedP);
  532.         final FieldVector3D<T> transformedA =
  533.                 new FieldVector3D<T>( 1, FieldRotation.applyTo(angular.getRotation(), intermediateA),
  534.                                      -2, crossV,
  535.                                      -1, crossCrossP,
  536.                                      -1, crossDotP);

  537.         // build transformed object
  538.         return new FieldPVCoordinates<T>(transformedP, transformedV, transformedA);

  539.     }

  540.     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
  541.      * <p>
  542.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  543.      * consistency between the transform {@link #getDate() date} and the time-stamped
  544.      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
  545.      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
  546.      * the input argument, regardless of the instance {@link #getDate() date}.
  547.      * </p>
  548.      * @param pv time-stamped position-velocity to transform.
  549.      * @param <T> type of the field elements
  550.      * @return transformed time-stamped position-velocity
  551.      * @since 7.0
  552.      */
  553.     public <T extends RealFieldElement<T>> TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {

  554.         // apply translation
  555.         final FieldVector3D<T> intermediateP = pv.getPosition().add(cartesian.getPosition());
  556.         final FieldVector3D<T> intermediateV = pv.getVelocity().add(cartesian.getVelocity());
  557.         final FieldVector3D<T> intermediateA = pv.getAcceleration().add(cartesian.getAcceleration());

  558.         // apply rotation
  559.         final FieldVector3D<T> transformedP = FieldRotation.applyTo(angular.getRotation(), intermediateP);
  560.         final FieldVector3D<T> crossP       = FieldVector3D.crossProduct(angular.getRotationRate(), transformedP);
  561.         final FieldVector3D<T> transformedV = FieldRotation.applyTo(angular.getRotation(), intermediateV).subtract(crossP);
  562.         final FieldVector3D<T> crossV       = FieldVector3D.crossProduct(angular.getRotationRate(), transformedV);
  563.         final FieldVector3D<T> crossCrossP  = FieldVector3D.crossProduct(angular.getRotationRate(), crossP);
  564.         final FieldVector3D<T> crossDotP    = FieldVector3D.crossProduct(angular.getRotationAcceleration(), transformedP);
  565.         final FieldVector3D<T> transformedA =
  566.                 new FieldVector3D<T>( 1, FieldRotation.applyTo(angular.getRotation(), intermediateA),
  567.                                      -2, crossV,
  568.                                      -1, crossCrossP,
  569.                                      -1, crossDotP);

  570.         // build transformed object
  571.         return new TimeStampedFieldPVCoordinates<T>(pv.getDate(), transformedP, transformedV, transformedA);

  572.     }

  573.     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
  574.      * method of the transform.
  575.      * <p>
  576.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  577.      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
  578.      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
  579.      * </p>
  580.      * <p>
  581.      * This definition implies that if we define position-velocity coordinates
  582.      * <pre>
  583.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  584.      * </pre>
  585.      * their differentials dPV₁ and dPV₀ will obey the following relation
  586.      * where J is the matrix computed by this method:<br/>
  587.      * <pre>
  588.      * dPV₁ = J &times; dPV₀
  589.      * </pre>
  590.      * </p>
  591.      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with
  592.      * the Jacobian, only the upper left 6x6 corner will be modified
  593.      * @deprecated as of 7.0, replaced with {@link #getJacobian(CartesianDerivativesFilter, double[][])}
  594.      */
  595.     @Deprecated
  596.     public void getJacobian(final double[][] jacobian) {
  597.         getJacobian(CartesianDerivativesFilter.USE_PV, jacobian);
  598.     }

  599.     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
  600.      * method of the transform.
  601.      * <p>
  602.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  603.      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
  604.      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
  605.      * </p>
  606.      * <p>
  607.      * This definition implies that if we define position-velocity coordinates
  608.      * <pre>
  609.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  610.      * </pre>
  611.      * their differentials dPV₁ and dPV₀ will obey the following relation
  612.      * where J is the matrix computed by this method:<br/>
  613.      * <pre>
  614.      * dPV₁ = J &times; dPV₀
  615.      * </pre>
  616.      * </p>
  617.      * @param selector selector specifying the size of the upper left corner that must be filled
  618.      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
  619.      * velocities and accelerations)
  620.      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
  621.      * the Jacobian, the rest of the matrix remaining untouched
  622.      */
  623.     public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {

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

  626.         // dP1/dP0
  627.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  628.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  629.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

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

  631.             // dP1/dV0
  632.             Arrays.fill(jacobian[0], 3, 6, 0.0);
  633.             Arrays.fill(jacobian[1], 3, 6, 0.0);
  634.             Arrays.fill(jacobian[2], 3, 6, 0.0);

  635.             // dV1/dP0
  636.             final Vector3D o = angular.getRotationRate();
  637.             final double ox = o.getX();
  638.             final double oy = o.getY();
  639.             final double oz = o.getZ();
  640.             for (int i = 0; i < 3; ++i) {
  641.                 jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]);
  642.                 jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]);
  643.                 jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]);
  644.             }

  645.             // dV1/dV0
  646.             System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  647.             System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  648.             System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

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

  650.                 // dP1/dA0
  651.                 Arrays.fill(jacobian[0], 6, 9, 0.0);
  652.                 Arrays.fill(jacobian[1], 6, 9, 0.0);
  653.                 Arrays.fill(jacobian[2], 6, 9, 0.0);

  654.                 // dV1/dA0
  655.                 Arrays.fill(jacobian[3], 6, 9, 0.0);
  656.                 Arrays.fill(jacobian[4], 6, 9, 0.0);
  657.                 Arrays.fill(jacobian[5], 6, 9, 0.0);

  658.                 // dA1/dP0
  659.                 final Vector3D oDot = angular.getRotationAcceleration();
  660.                 final double oDotx  = oDot.getX();
  661.                 final double oDoty  = oDot.getY();
  662.                 final double oDotz  = oDot.getZ();
  663.                 for (int i = 0; i < 3; ++i) {
  664.                     jacobian[6][i] = -(oDoty * mData[2][i] - oDotz * mData[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]);
  665.                     jacobian[7][i] = -(oDotz * mData[0][i] - oDotx * mData[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]);
  666.                     jacobian[8][i] = -(oDotx * mData[1][i] - oDoty * mData[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]);
  667.                 }

  668.                 // dA1/dV0
  669.                 for (int i = 0; i < 3; ++i) {
  670.                     jacobian[6][i + 3] = -2 * (oy * mData[2][i] - oz * mData[1][i]);
  671.                     jacobian[7][i + 3] = -2 * (oz * mData[0][i] - ox * mData[2][i]);
  672.                     jacobian[8][i + 3] = -2 * (ox * mData[1][i] - oy * mData[0][i]);
  673.                 }

  674.                 // dA1/dA0
  675.                 System.arraycopy(mData[0], 0, jacobian[6], 6, 3);
  676.                 System.arraycopy(mData[1], 0, jacobian[7], 6, 3);
  677.                 System.arraycopy(mData[2], 0, jacobian[8], 6, 3);

  678.             }

  679.         }

  680.     }

  681.     /** Get the underlying elementary cartesian part.
  682.      * <p>A transform can be uniquely represented as an elementary
  683.      * translation followed by an elementary rotation. This method
  684.      * returns this unique elementary translation with its derivative.</p>
  685.      * @return underlying elementary cartesian part
  686.      * @see #getTranslation()
  687.      * @see #getVelocity()
  688.      */
  689.     public PVCoordinates getCartesian() {
  690.         return cartesian;
  691.     }

  692.     /** Get the underlying elementary translation.
  693.      * <p>A transform can be uniquely represented as an elementary
  694.      * translation followed by an elementary rotation. This method
  695.      * returns this unique elementary translation.</p>
  696.      * @return underlying elementary translation
  697.      * @see #getCartesian()
  698.      * @see #getVelocity()
  699.      * @see #getAcceleration()
  700.      */
  701.     public Vector3D getTranslation() {
  702.         return cartesian.getPosition();
  703.     }

  704.     /** Get the first time derivative of the translation.
  705.      * @return first time derivative of the translation
  706.      * @see #getCartesian()
  707.      * @see #getTranslation()
  708.      * @see #getAcceleration()
  709.      */
  710.     public Vector3D getVelocity() {
  711.         return cartesian.getVelocity();
  712.     }

  713.     /** Get the second time derivative of the translation.
  714.      * @return second time derivative of the translation
  715.      * @see #getCartesian()
  716.      * @see #getTranslation()
  717.      * @see #getVelocity()
  718.      */
  719.     public Vector3D getAcceleration() {
  720.         return cartesian.getAcceleration();
  721.     }

  722.     /** Get the underlying elementary angular part.
  723.      * <p>A transform can be uniquely represented as an elementary
  724.      * translation followed by an elementary rotation. This method
  725.      * returns this unique elementary rotation with its derivative.</p>
  726.      * @return underlying elementary angular part
  727.      * @see #getRotation()
  728.      * @see #getRotationRate()
  729.      * @see #getRotationAcceleration()
  730.      */
  731.     public AngularCoordinates getAngular() {
  732.         return angular;
  733.     }

  734.     /** Get the underlying elementary rotation.
  735.      * <p>A transform can be uniquely represented as an elementary
  736.      * translation followed by an elementary rotation. This method
  737.      * returns this unique elementary rotation.</p>
  738.      * @return underlying elementary rotation
  739.      * @see #getAngular()
  740.      * @see #getRotationRate()
  741.      * @see #getRotationAcceleration()
  742.      */
  743.     public Rotation getRotation() {
  744.         return angular.getRotation();
  745.     }

  746.     /** Get the first time derivative of the rotation.
  747.      * <p>The norm represents the angular rate.</p>
  748.      * @return First time derivative of the rotation
  749.      * @see #getAngular()
  750.      * @see #getRotation()
  751.      * @see #getRotationAcceleration()
  752.      */
  753.     public Vector3D getRotationRate() {
  754.         return angular.getRotationRate();
  755.     }

  756.     /** Get the second time derivative of the rotation.
  757.      * @return Second time derivative of the rotation
  758.      * @see #getAngular()
  759.      * @see #getRotation()
  760.      * @see #getRotationRate()
  761.      */
  762.     public Vector3D getRotationAcceleration() {
  763.         return angular.getRotationAcceleration();
  764.     }

  765.     /** Specialized class for identity transform. */
  766.     private static class IdentityTransform extends Transform {

  767.         /** Serializable UID. */
  768.         private static final long serialVersionUID = -9042082036141830517L;

  769.         /** Simple constructor. */
  770.         IdentityTransform() {
  771.             super(AbsoluteDate.J2000_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
  772.         }

  773.         /** {@inheritDoc} */
  774.         @Override
  775.         public Transform shiftedBy(final double dt) {
  776.             return this;
  777.         }

  778.         /** {@inheritDoc} */
  779.         @Override
  780.         public Transform getInverse() {
  781.             return this;
  782.         };

  783.         /** {@inheritDoc} */
  784.         @Override
  785.         public Vector3D transformPosition(final Vector3D position) {
  786.             return position;
  787.         }

  788.         /** {@inheritDoc} */
  789.         @Override
  790.         public Vector3D transformVector(final Vector3D vector) {
  791.             return vector;
  792.         }

  793.         /** {@inheritDoc} */
  794.         @Override
  795.         public Line transformLine(final Line line) {
  796.             return line;
  797.         }

  798.         /** {@inheritDoc} */
  799.         @Override
  800.         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
  801.             return pv;
  802.         }

  803.         /** {@inheritDoc} */
  804.         @Override
  805.         public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
  806.             final int n = 3 * (selector.getMaxOrder() + 1);
  807.             for (int i = 0; i < n; ++i) {
  808.                 Arrays.fill(jacobian[i], 0, n, 0.0);
  809.                 jacobian[i][i] = 1.0;
  810.             }
  811.         }

  812.     }

  813. }