1   /* Copyright 2002-2024 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.frames;
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.stream.Collectors;
25  import java.util.stream.Stream;
26  
27  import org.hipparchus.CalculusFieldElement;
28  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
29  import org.hipparchus.geometry.euclidean.threed.Line;
30  import org.hipparchus.geometry.euclidean.threed.Rotation;
31  import org.hipparchus.geometry.euclidean.threed.Vector3D;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.TimeInterpolator;
34  import org.orekit.time.TimeShiftable;
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.TimeStampedAngularCoordinatesHermiteInterpolator;
42  import org.orekit.utils.TimeStampedFieldPVCoordinates;
43  import org.orekit.utils.TimeStampedPVCoordinates;
44  import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;
45  
46  
47  /** Transformation class in three dimensional space.
48   *
49   * <p>This class represents the transformation engine between {@link Frame frames}.
50   * It is used both to define the relationship between each frame and its
51   * parent frame and to gather all individual transforms into one
52   * operation when converting between frames far away from each other.</p>
53   * <p> The convention used in OREKIT is vectorial transformation. It means
54   * that a transformation is defined as a transform to apply to the
55   * coordinates of a vector expressed in the old frame to obtain the
56   * same vector expressed in the new frame.
57   *
58   * <p>Instances of this class are guaranteed to be immutable.</p>
59   *
60   * <h2> Examples </h2>
61   *
62   * <h3> Example of translation from R<sub>A</sub> to R<sub>B</sub> </h3>
63   *
64   * <p> We want to transform the {@link PVCoordinates} PV<sub>A</sub> to
65   * PV<sub>B</sub> with :
66   * <p> PV<sub>A</sub> = ({1, 0, 0}, {2, 0, 0}, {3, 0, 0}); <br>
67   *     PV<sub>B</sub> = ({0, 0, 0}, {0, 0, 0}, {0, 0, 0});
68   *
69   * <p> The transform to apply then is defined as follows :
70   *
71   * <pre><code>
72   * Vector3D translation  = new Vector3D(-1, 0, 0);
73   * Vector3D velocity     = new Vector3D(-2, 0, 0);
74   * Vector3D acceleration = new Vector3D(-3, 0, 0);
75   *
76   * Transform R1toR2 = new Transform(date, translation, velocity, acceleration);
77   *
78   * PVB = R1toR2.transformPVCoordinates(PVA);
79   * </code></pre>
80   *
81   * <h3> Example of rotation from R<sub>A</sub> to R<sub>B</sub> </h3>
82   * <p> We want to transform the {@link PVCoordinates} PV<sub>A</sub> to
83   * PV<sub>B</sub> with
84   *
85   * <p> PV<sub>A</sub> = ({1, 0, 0}, { 1, 0, 0}); <br>
86   *     PV<sub>B</sub> = ({0, 1, 0}, {-2, 1, 0});
87   *
88   * <p> The transform to apply then is defined as follows :
89   *
90   * <pre><code>
91   * Rotation rotation = new Rotation(Vector3D.PLUS_K, FastMath.PI / 2);
92   * Vector3D rotationRate = new Vector3D(0, 0, -2);
93   *
94   * Transform R1toR2 = new Transform(rotation, rotationRate);
95   *
96   * PVB = R1toR2.transformPVCoordinates(PVA);
97   * </code></pre>
98   *
99   * @author Luc Maisonobe
100  * @author Fabien Maussion
101  */
102 public class Transform implements
103         TimeShiftable<Transform>,
104         Serializable,
105         KinematicTransform {
106 
107     /** Identity transform. */
108     public static final Transform IDENTITY = new IdentityTransform();
109 
110     /** Serializable UID. */
111     private static final long serialVersionUID = 210140410L;
112 
113     /** Date of the transform. */
114     private final AbsoluteDate date;
115 
116     /** Cartesian coordinates of the target frame with respect to the original frame. */
117     private final PVCoordinates cartesian;
118 
119     /** Angular coordinates of the target frame with respect to the original frame. */
120     private final AngularCoordinates angular;
121 
122     /** Build a transform from its primitive operations.
123      * @param date date of the transform
124      * @param cartesian Cartesian coordinates of the target frame with respect to the original frame
125      * @param angular angular coordinates of the target frame with respect to the original frame
126      */
127     public Transform(final AbsoluteDate date, final PVCoordinates cartesian, final AngularCoordinates angular) {
128         this.date      = date;
129         this.cartesian = cartesian;
130         this.angular   = angular;
131     }
132 
133     /** Build a translation transform.
134      * @param date date of the transform
135      * @param translation translation to apply (i.e. coordinates of
136      * the transformed origin, or coordinates of the origin of the
137      * old frame in the new frame)
138      */
139     public Transform(final AbsoluteDate date, final Vector3D translation) {
140         this(date,
141              new PVCoordinates(translation),
142              AngularCoordinates.IDENTITY);
143     }
144 
145     /** Build a rotation transform.
146      * @param date date of the transform
147      * @param rotation rotation to apply ( i.e. rotation to apply to the
148      * coordinates of a vector expressed in the old frame to obtain the
149      * same vector expressed in the new frame )
150      */
151     public Transform(final AbsoluteDate date, final Rotation rotation) {
152         this(date,
153              PVCoordinates.ZERO,
154              new AngularCoordinates(rotation));
155     }
156 
157     /** Build a combined translation and rotation transform.
158      * @param date date of the transform
159      * @param translation translation to apply (i.e. coordinates of
160      * the transformed origin, or coordinates of the origin of the
161      * old frame in the new frame)
162      * @param rotation rotation to apply ( i.e. rotation to apply to the
163      * coordinates of a vector expressed in the old frame to obtain the
164      * same vector expressed in the new frame )
165      * @since 12.1
166      */
167     public Transform(final AbsoluteDate date, final Vector3D translation, final Rotation rotation) {
168         this(date, new PVCoordinates(translation), new AngularCoordinates(rotation));
169     }
170 
171     /** Build a translation transform, with its first time derivative.
172      * @param date date of the transform
173      * @param translation translation to apply (i.e. coordinates of
174      * the transformed origin, or coordinates of the origin of the
175      * old frame in the new frame)
176      * @param velocity the velocity of the translation (i.e. origin
177      * of the old frame velocity in the new frame)
178      */
179     public Transform(final AbsoluteDate date, final Vector3D translation,
180                      final Vector3D velocity) {
181         this(date,
182              new PVCoordinates(translation, velocity, Vector3D.ZERO),
183              AngularCoordinates.IDENTITY);
184     }
185 
186     /** Build a translation transform, with its first and second time derivatives.
187      * @param date date of the transform
188      * @param translation translation to apply (i.e. coordinates of
189      * the transformed origin, or coordinates of the origin of the
190      * old frame in the new frame)
191      * @param velocity the velocity of the translation (i.e. origin
192      * of the old frame velocity in the new frame)
193      * @param acceleration the acceleration of the translation (i.e. origin
194      * of the old frame acceleration in the new frame)
195      */
196     public Transform(final AbsoluteDate date, final Vector3D translation,
197                      final Vector3D velocity, final Vector3D acceleration) {
198         this(date,
199              new PVCoordinates(translation, velocity, acceleration),
200              AngularCoordinates.IDENTITY);
201     }
202 
203     /** Build a translation transform, with its first time derivative.
204      * @param date date of the transform
205      * @param cartesian Cartesian part of the transformation to apply (i.e. coordinates of
206      * the transformed origin, or coordinates of the origin of the
207      * old frame in the new frame, with their derivatives)
208      */
209     public Transform(final AbsoluteDate date, final PVCoordinates cartesian) {
210         this(date,
211              cartesian,
212              AngularCoordinates.IDENTITY);
213     }
214 
215     /** Build a rotation transform.
216      * @param date date of the transform
217      * @param rotation rotation to apply ( i.e. rotation to apply to the
218      * coordinates of a vector expressed in the old frame to obtain the
219      * same vector expressed in the new frame )
220      * @param rotationRate the axis of the instant rotation
221      * expressed in the new frame. (norm representing angular rate)
222      */
223     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate) {
224         this(date,
225              PVCoordinates.ZERO,
226              new AngularCoordinates(rotation, rotationRate, Vector3D.ZERO));
227     }
228 
229     /** Build a rotation transform.
230      * @param date date of the transform
231      * @param rotation rotation to apply ( i.e. rotation to apply to the
232      * coordinates of a vector expressed in the old frame to obtain the
233      * same vector expressed in the new frame )
234      * @param rotationRate the axis of the instant rotation
235      * @param rotationAcceleration the axis of the instant rotation
236      * expressed in the new frame. (norm representing angular rate)
237      */
238     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate,
239                      final Vector3D rotationAcceleration) {
240         this(date,
241              PVCoordinates.ZERO,
242              new AngularCoordinates(rotation, rotationRate, rotationAcceleration));
243     }
244 
245     /** Build a rotation transform.
246      * @param date date of the transform
247      * @param angular angular part of the transformation to apply (i.e. rotation to
248      * apply to the coordinates of a vector expressed in the old frame to obtain the
249      * same vector expressed in the new frame, with its rotation rate)
250      */
251     public Transform(final AbsoluteDate date, final AngularCoordinates angular) {
252         this(date, PVCoordinates.ZERO, angular);
253     }
254 
255     /** Build a transform by combining two existing ones.
256      * <p>
257      * Note that the dates of the two existing transformed are <em>ignored</em>,
258      * and the combined transform date is set to the date supplied in this constructor
259      * without any attempt to shift the raw transforms. This is a design choice allowing
260      * user full control of the combination.
261      * </p>
262      * @param date date of the transform
263      * @param first first transform applied
264      * @param second second transform applied
265      */
266     public Transform(final AbsoluteDate date, final Transform first, final Transform second) {
267         this(date,
268              new PVCoordinates(StaticTransform.compositeTranslation(first, second),
269                                KinematicTransform.compositeVelocity(first, second),
270                                compositeAcceleration(first, second)),
271              new AngularCoordinates(StaticTransform.compositeRotation(first, second),
272                                     KinematicTransform.compositeRotationRate(first, second),
273                                     compositeRotationAcceleration(first, second)));
274     }
275 
276     /** Compute a composite acceleration.
277      * @param first first applied transform
278      * @param second second applied transform
279      * @return acceleration part of the composite transform
280      */
281     private static Vector3D compositeAcceleration(final Transform first, final Transform second) {
282 
283         final Vector3D a1    = first.cartesian.getAcceleration();
284         final Rotation r1    = first.angular.getRotation();
285         final Vector3D o1    = first.angular.getRotationRate();
286         final Vector3D oDot1 = first.angular.getRotationAcceleration();
287         final Vector3D p2    = second.cartesian.getPosition();
288         final Vector3D v2    = second.cartesian.getVelocity();
289         final Vector3D a2    = second.cartesian.getAcceleration();
290 
291         final Vector3D crossCrossP = Vector3D.crossProduct(o1,    Vector3D.crossProduct(o1, p2));
292         final Vector3D crossV      = Vector3D.crossProduct(o1,    v2);
293         final Vector3D crossDotP   = Vector3D.crossProduct(oDot1, p2);
294 
295         return a1.add(r1.applyInverseTo(new Vector3D(1, a2, 2, crossV, 1, crossCrossP, 1, crossDotP)));
296 
297     }
298 
299     /** Compute a composite rotation acceleration.
300      * @param first first applied transform
301      * @param second second applied transform
302      * @return rotation acceleration part of the composite transform
303      */
304     private static Vector3D compositeRotationAcceleration(final Transform first, final Transform second) {
305 
306         final Vector3D o1    = first.angular.getRotationRate();
307         final Vector3D oDot1 = first.angular.getRotationAcceleration();
308         final Rotation r2    = second.angular.getRotation();
309         final Vector3D o2    = second.angular.getRotationRate();
310         final Vector3D oDot2 = second.angular.getRotationAcceleration();
311 
312         return new Vector3D( 1, oDot2,
313                              1, r2.applyTo(oDot1),
314                             -1, Vector3D.crossProduct(o2, r2.applyTo(o1)));
315 
316     }
317 
318     /** {@inheritDoc} */
319     public AbsoluteDate getDate() {
320         return date;
321     }
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 
328     /**
329      * Shift the transform in time considering all rates, then return only the
330      * translation and rotation portion of the transform.
331      *
332      * @param dt time shift in seconds.
333      * @return shifted transform as a static transform. It is static in the
334      * sense that it can only be used to transform directions and positions, but
335      * not velocities or accelerations.
336      * @see #shiftedBy(double)
337      */
338     public StaticTransform staticShiftedBy(final double dt) {
339         return StaticTransform.of(
340                 date.shiftedBy(dt),
341                 cartesian.positionShiftedBy(dt),
342                 angular.rotationShiftedBy(dt));
343     }
344 
345     /**
346      * Create a so-called static transform from the instance.
347      *
348      * @return static part of the transform. It is static in the
349      * sense that it can only be used to transform directions and positions, but
350      * not velocities or accelerations.
351      * @see StaticTransform
352      */
353     public StaticTransform toStaticTransform() {
354         return StaticTransform.of(date, cartesian.getPosition(), angular.getRotation());
355     }
356 
357     /** Interpolate a transform from a sample set of existing transforms.
358      * <p>
359      * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate,
360      * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter}
361      * set to {@link CartesianDerivativesFilter#USE_PVA} and {@code aFilter} set to
362      * {@link AngularDerivativesFilter#USE_RRA}
363      * set to true.
364      * </p>
365      * @param interpolationDate interpolation date
366      * @param sample sample points on which interpolation should be done
367      * @return a new instance, interpolated at specified date
368      */
369     public Transform interpolate(final AbsoluteDate interpolationDate, final Stream<Transform> sample) {
370         return interpolate(interpolationDate,
371                            CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RRA,
372                            sample.collect(Collectors.toList()));
373     }
374 
375     /** Interpolate a transform from a sample set of existing transforms.
376      * <p>
377      * Note that even if first time derivatives (velocities and rotation rates)
378      * from sample can be ignored, the interpolated instance always includes
379      * interpolated derivatives. This feature can be used explicitly to
380      * compute these derivatives when it would be too complex to compute them
381      * from an analytical formula: just compute a few sample points from the
382      * explicit formula and set the derivatives to zero in these sample points,
383      * then use interpolation to add derivatives consistent with the positions
384      * and rotations.
385      * </p>
386      * <p>
387      * As this implementation of interpolation is polynomial, it should be used only
388      * with small samples (about 10-20 points) in order to avoid <a
389      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
390      * and numerical problems (including NaN appearing).
391      * </p>
392      * @param date interpolation date
393      * @param cFilter filter for derivatives from the sample to use in interpolation
394      * @param aFilter filter for derivatives from the sample to use in interpolation
395      * @param sample sample points on which interpolation should be done
396      * @return a new instance, interpolated at specified date
397      * @since 7.0
398      */
399     public static Transform interpolate(final AbsoluteDate date,
400                                         final CartesianDerivativesFilter cFilter,
401                                         final AngularDerivativesFilter aFilter,
402                                         final Collection<Transform> sample) {
403 
404         // Create samples
405         final List<TimeStampedPVCoordinates>      datedPV = new ArrayList<>(sample.size());
406         final List<TimeStampedAngularCoordinates> datedAC = new ArrayList<>(sample.size());
407         for (final Transform t : sample) {
408             datedPV.add(new TimeStampedPVCoordinates(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration()));
409             datedAC.add(new TimeStampedAngularCoordinates(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()));
410         }
411 
412         // Create interpolators
413         final TimeInterpolator<TimeStampedPVCoordinates> pvInterpolator =
414                 new TimeStampedPVCoordinatesHermiteInterpolator(datedPV.size(), cFilter);
415 
416         final TimeInterpolator<TimeStampedAngularCoordinates> angularInterpolator =
417                 new TimeStampedAngularCoordinatesHermiteInterpolator(datedPV.size(), aFilter);
418 
419         // Interpolate
420         final TimeStampedPVCoordinates      interpolatedPV = pvInterpolator.interpolate(date, datedPV);
421         final TimeStampedAngularCoordinates interpolatedAC = angularInterpolator.interpolate(date, datedAC);
422         return new Transform(date, interpolatedPV, interpolatedAC);
423     }
424 
425     /** Get the inverse transform of the instance.
426      * @return inverse transform of the instance
427      */
428     @Override
429     public Transform getInverse() {
430 
431         final Rotation r    = angular.getRotation();
432         final Vector3D o    = angular.getRotationRate();
433         final Vector3D oDot = angular.getRotationAcceleration();
434         final Vector3D rp   = r.applyTo(cartesian.getPosition());
435         final Vector3D rv   = r.applyTo(cartesian.getVelocity());
436         final Vector3D ra   = r.applyTo(cartesian.getAcceleration());
437 
438         final Vector3D pInv        = rp.negate();
439         final Vector3D crossP      = Vector3D.crossProduct(o, rp);
440         final Vector3D vInv        = crossP.subtract(rv);
441         final Vector3D crossV      = Vector3D.crossProduct(o, rv);
442         final Vector3D crossDotP   = Vector3D.crossProduct(oDot, rp);
443         final Vector3D crossCrossP = Vector3D.crossProduct(o, crossP);
444         final Vector3D aInv        = new Vector3D(-1, ra,
445                                                    2, crossV,
446                                                    1, crossDotP,
447                                                   -1, crossCrossP);
448 
449         return new Transform(getDate(), new PVCoordinates(pInv, vInv, aInv), angular.revert());
450 
451     }
452 
453     /** Get a frozen transform.
454      * <p>
455      * This method creates a copy of the instance but frozen in time,
456      * i.e. with velocity, acceleration and rotation rate forced to zero.
457      * </p>
458      * @return a new transform, without any time-dependent parts
459      */
460     public Transform freeze() {
461         return new Transform(date,
462                              new PVCoordinates(cartesian.getPosition(), Vector3D.ZERO, Vector3D.ZERO),
463                              new AngularCoordinates(angular.getRotation(), Vector3D.ZERO, Vector3D.ZERO));
464     }
465 
466     /** Transform {@link PVCoordinates} including kinematic effects.
467      * @param pva the position-velocity-acceleration triplet to transform.
468      * @return transformed position-velocity-acceleration
469      */
470     public PVCoordinates transformPVCoordinates(final PVCoordinates pva) {
471         return angular.applyTo(new PVCoordinates(1, pva, 1, cartesian));
472     }
473 
474     /** Transform {@link TimeStampedPVCoordinates} including kinematic effects.
475      * <p>
476      * In order to allow the user more flexibility, this method does <em>not</em> check for
477      * consistency between the transform {@link #getDate() date} and the time-stamped
478      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
479      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
480      * the input argument, regardless of the instance {@link #getDate() date}.
481      * </p>
482      * @param pv time-stamped  position-velocity to transform.
483      * @return transformed time-stamped position-velocity
484      * @since 7.0
485      */
486     public TimeStampedPVCoordinates transformPVCoordinates(final TimeStampedPVCoordinates pv) {
487         return angular.applyTo(new TimeStampedPVCoordinates(pv.getDate(), 1, pv, 1, cartesian));
488     }
489 
490     /** Transform {@link FieldPVCoordinates} including kinematic effects.
491      * @param pv position-velocity to transform.
492      * @param <T> type of the field elements
493      * @return transformed position-velocity
494      */
495     public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
496         return angular.applyTo(new FieldPVCoordinates<>(pv.getPosition().add(cartesian.getPosition()),
497                                                         pv.getVelocity().add(cartesian.getVelocity()),
498                                                         pv.getAcceleration().add(cartesian.getAcceleration())));
499     }
500 
501     /** Transform {@link TimeStampedFieldPVCoordinates} including kinematic effects.
502      * <p>
503      * In order to allow the user more flexibility, this method does <em>not</em> check for
504      * consistency between the transform {@link #getDate() date} and the time-stamped
505      * position-velocity {@link TimeStampedFieldPVCoordinates#getDate() date}. The returned
506      * value will always have the same {@link TimeStampedFieldPVCoordinates#getDate() date} as
507      * the input argument, regardless of the instance {@link #getDate() date}.
508      * </p>
509      * @param pv time-stamped position-velocity to transform.
510      * @param <T> type of the field elements
511      * @return transformed time-stamped position-velocity
512      * @since 7.0
513      */
514     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> transformPVCoordinates(final TimeStampedFieldPVCoordinates<T> pv) {
515         return angular.applyTo(new TimeStampedFieldPVCoordinates<>(pv.getDate(),
516                                                                    pv.getPosition().add(cartesian.getPosition()),
517                                                                    pv.getVelocity().add(cartesian.getVelocity()),
518                                                                    pv.getAcceleration().add(cartesian.getAcceleration())));
519     }
520 
521     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
522      * method of the transform.
523      * <p>
524      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
525      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
526      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
527      * </p>
528      * <p>
529      * This definition implies that if we define position-velocity coordinates
530      * <pre>
531      * PV₁ = transform.transformPVCoordinates(PV₀), then
532      * </pre>
533      * <p> their differentials dPV₁ and dPV₀ will obey the following relation
534      * where J is the matrix computed by this method:
535      * <pre>
536      * dPV₁ = J &times; dPV₀
537      * </pre>
538      *
539      * @param selector selector specifying the size of the upper left corner that must be filled
540      * (either 3x3 for positions only, 6x6 for positions and velocities, 9x9 for positions,
541      * velocities and accelerations)
542      * @param jacobian placeholder matrix whose upper-left corner is to be filled with
543      * the Jacobian, the rest of the matrix remaining untouched
544      */
545     public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
546 
547         if (selector.getMaxOrder() == 0) {
548             // elementary matrix for rotation
549             final double[][] mData = angular.getRotation().getMatrix();
550 
551             // dP1/dP0
552             System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
553             System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
554             System.arraycopy(mData[2], 0, jacobian[2], 0, 3);
555         }
556 
557         else if (selector.getMaxOrder() == 1) {
558             // use KinematicTransform Jacobian
559             final double[][] mData = getPVJacobian();
560             for (int i = 0; i < mData.length; i++) {
561                 System.arraycopy(mData[i], 0, jacobian[i], 0, mData[i].length);
562             }
563         }
564 
565         else if (selector.getMaxOrder() >= 2) {
566             getJacobian(CartesianDerivativesFilter.USE_PV, jacobian);
567 
568             // dP1/dA0
569             Arrays.fill(jacobian[0], 6, 9, 0.0);
570             Arrays.fill(jacobian[1], 6, 9, 0.0);
571             Arrays.fill(jacobian[2], 6, 9, 0.0);
572 
573             // dV1/dA0
574             Arrays.fill(jacobian[3], 6, 9, 0.0);
575             Arrays.fill(jacobian[4], 6, 9, 0.0);
576             Arrays.fill(jacobian[5], 6, 9, 0.0);
577 
578             // dA1/dP0
579             final Vector3D o = angular.getRotationRate();
580             final double ox = o.getX();
581             final double oy = o.getY();
582             final double oz = o.getZ();
583             final Vector3D oDot = angular.getRotationAcceleration();
584             final double oDotx  = oDot.getX();
585             final double oDoty  = oDot.getY();
586             final double oDotz  = oDot.getZ();
587             for (int i = 0; i < 3; ++i) {
588                 jacobian[6][i] = -(oDoty * jacobian[2][i] - oDotz * jacobian[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]);
589                 jacobian[7][i] = -(oDotz * jacobian[0][i] - oDotx * jacobian[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]);
590                 jacobian[8][i] = -(oDotx * jacobian[1][i] - oDoty * jacobian[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]);
591             }
592 
593             // dA1/dV0
594             for (int i = 0; i < 3; ++i) {
595                 jacobian[6][i + 3] = -2 * (oy * jacobian[2][i] - oz * jacobian[1][i]);
596                 jacobian[7][i + 3] = -2 * (oz * jacobian[0][i] - ox * jacobian[2][i]);
597                 jacobian[8][i + 3] = -2 * (ox * jacobian[1][i] - oy * jacobian[0][i]);
598             }
599 
600             // dA1/dA0
601             System.arraycopy(jacobian[0], 0, jacobian[6], 6, 3);
602             System.arraycopy(jacobian[1], 0, jacobian[7], 6, 3);
603             System.arraycopy(jacobian[2], 0, jacobian[8], 6, 3);
604 
605         }
606     }
607 
608     /** Get the underlying elementary Cartesian part.
609      * <p>A transform can be uniquely represented as an elementary
610      * translation followed by an elementary rotation. This method
611      * returns this unique elementary translation with its derivative.</p>
612      * @return underlying elementary Cartesian part
613      * @see #getTranslation()
614      * @see #getVelocity()
615      */
616     public PVCoordinates getCartesian() {
617         return cartesian;
618     }
619 
620     /** Get the underlying elementary translation.
621      * <p>A transform can be uniquely represented as an elementary
622      * translation followed by an elementary rotation. This method
623      * returns this unique elementary translation.</p>
624      * @return underlying elementary translation
625      * @see #getCartesian()
626      * @see #getVelocity()
627      * @see #getAcceleration()
628      */
629     public Vector3D getTranslation() {
630         return cartesian.getPosition();
631     }
632 
633     /** Get the first time derivative of the translation.
634      * @return first time derivative of the translation
635      * @see #getCartesian()
636      * @see #getTranslation()
637      * @see #getAcceleration()
638      */
639     public Vector3D getVelocity() {
640         return cartesian.getVelocity();
641     }
642 
643     /** Get the second time derivative of the translation.
644      * @return second time derivative of the translation
645      * @see #getCartesian()
646      * @see #getTranslation()
647      * @see #getVelocity()
648      */
649     public Vector3D getAcceleration() {
650         return cartesian.getAcceleration();
651     }
652 
653     /** Get the underlying elementary angular part.
654      * <p>A transform can be uniquely represented as an elementary
655      * translation followed by an elementary rotation. This method
656      * returns this unique elementary rotation with its derivative.</p>
657      * @return underlying elementary angular part
658      * @see #getRotation()
659      * @see #getRotationRate()
660      * @see #getRotationAcceleration()
661      */
662     public AngularCoordinates getAngular() {
663         return angular;
664     }
665 
666     /** Get the underlying elementary rotation.
667      * <p>A transform can be uniquely represented as an elementary
668      * translation followed by an elementary rotation. This method
669      * returns this unique elementary rotation.</p>
670      * @return underlying elementary rotation
671      * @see #getAngular()
672      * @see #getRotationRate()
673      * @see #getRotationAcceleration()
674      */
675     public Rotation getRotation() {
676         return angular.getRotation();
677     }
678 
679     /** Get the first time derivative of the rotation.
680      * <p>The norm represents the angular rate.</p>
681      * @return First time derivative of the rotation
682      * @see #getAngular()
683      * @see #getRotation()
684      * @see #getRotationAcceleration()
685      */
686     public Vector3D getRotationRate() {
687         return angular.getRotationRate();
688     }
689 
690     /** Get the second time derivative of the rotation.
691      * @return Second time derivative of the rotation
692      * @see #getAngular()
693      * @see #getRotation()
694      * @see #getRotationRate()
695      */
696     public Vector3D getRotationAcceleration() {
697         return angular.getRotationAcceleration();
698     }
699 
700     /** Specialized class for identity transform. */
701     private static class IdentityTransform extends Transform {
702 
703         /** Serializable UID. */
704         private static final long serialVersionUID = -9042082036141830517L;
705 
706         /** Simple constructor. */
707         IdentityTransform() {
708             super(AbsoluteDate.ARBITRARY_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
709         }
710 
711         @Override
712         public StaticTransform staticShiftedBy(final double dt) {
713             return toStaticTransform();
714         }
715 
716         /** {@inheritDoc} */
717         @Override
718         public Transform shiftedBy(final double dt) {
719             return this;
720         }
721 
722         @Override
723         public StaticTransform getStaticInverse() {
724             return toStaticTransform();
725         }
726 
727         /** {@inheritDoc} */
728         @Override
729         public Transform getInverse() {
730             return this;
731         }
732 
733         @Override
734         public StaticTransform toStaticTransform() {
735             return StaticTransform.getIdentity();
736         }
737 
738         /** {@inheritDoc} */
739         @Override
740         public Vector3D transformPosition(final Vector3D position) {
741             return position;
742         }
743 
744         /** {@inheritDoc} */
745         @Override
746         public Vector3D transformVector(final Vector3D vector) {
747             return vector;
748         }
749 
750         @Override
751         public <T extends CalculusFieldElement<T>> FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
752             return transformVector(position);
753         }
754 
755         @Override
756         public <T extends CalculusFieldElement<T>> FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
757             return new FieldVector3D<>(vector.getX(), vector.getY(), vector.getZ());
758         }
759 
760         /** {@inheritDoc} */
761         @Override
762         public Line transformLine(final Line line) {
763             return line;
764         }
765 
766         /** {@inheritDoc} */
767         @Override
768         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
769             return pv;
770         }
771 
772         @Override
773         public PVCoordinates transformOnlyPV(final PVCoordinates pv) {
774             return new PVCoordinates(pv.getPosition(), pv.getVelocity());
775         }
776 
777         @Override
778         public TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) {
779             return new TimeStampedPVCoordinates(pv.getDate(), pv.getPosition(), pv.getVelocity());
780         }
781 
782         @Override
783         public Transform freeze() {
784             return this;
785         }
786 
787         @Override
788         public TimeStampedPVCoordinates transformPVCoordinates(
789                 final TimeStampedPVCoordinates pv) {
790             return pv;
791         }
792 
793         @Override
794         public <T extends CalculusFieldElement<T>> FieldPVCoordinates<T>
795             transformPVCoordinates(final FieldPVCoordinates<T> pv) {
796             return pv;
797         }
798 
799         @Override
800         public <T extends CalculusFieldElement<T>>
801             TimeStampedFieldPVCoordinates<T> transformPVCoordinates(
802                     final TimeStampedFieldPVCoordinates<T> pv) {
803             return pv;
804         }
805 
806         /** {@inheritDoc} */
807         @Override
808         public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) {
809             final int n = 3 * (selector.getMaxOrder() + 1);
810             for (int i = 0; i < n; ++i) {
811                 Arrays.fill(jacobian[i], 0, n, 0.0);
812                 jacobian[i][i] = 1.0;
813             }
814         }
815 
816     }
817 
818 }