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