1   /* Copyright 2002-2013 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  
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  
25  import org.apache.commons.math3.RealFieldElement;
26  import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
27  import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
28  import org.apache.commons.math3.geometry.euclidean.threed.Line;
29  import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
30  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
31  import org.apache.commons.math3.util.Pair;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.TimeInterpolable;
34  import org.orekit.time.TimeShiftable;
35  import org.orekit.time.TimeStamped;
36  import org.orekit.utils.AngularCoordinates;
37  import org.orekit.utils.PVCoordinates;
38  import org.orekit.utils.FieldPVCoordinates;
39  
40  
41  /** Transformation class in three dimensional space.
42   *
43   * <p>This class represents the transformation engine between {@link Frame frames}.
44   * It is used both to define the relationship between each frame and its
45   * parent frame and to gather all individual transforms into one
46   * operation when converting between frames far away from each other.</p>
47   * <p> The convention used in OREKIT is vectorial transformation. It means
48   * that a transformation is defined as a transform to apply to the
49   * coordinates of a vector expressed in the old frame to obtain the
50   * same vector expressed in the new frame. <p>
51   *
52   * <p>Instances of this class are guaranteed to be immutable.</p>
53   *
54   *  <h5> Example </h5>
55   *
56   * <pre>
57   *
58   * 1 ) Example of translation from R<sub>A</sub> to R<sub>B</sub>:
59   * We want to transform the {@link PVCoordinates} PV<sub>A</sub> to PV<sub>B</sub>.
60   *
61   * With :  PV<sub>A</sub> = ({1, 0, 0} , {1, 0, 0});
62   * and  :  PV<sub>B</sub> = ({0, 0, 0} , {0, 0, 0});
63   *
64   * The transform to apply then is defined as follows :
65   *
66   * Vector3D translation = new Vector3D(-1,0,0);
67   * Vector3D velocity = new Vector3D(-1,0,0);
68   *
69   * Transform R1toR2 = new Transform(translation, Velocity);
70   *
71   * PV<sub>B</sub> = R1toR2.transformPVCoordinates(PV<sub>A</sub>);
72   *
73   *
74   * 2 ) Example of rotation from R<sub>A</sub> to R<sub>B</sub>:
75   * We want to transform the {@link PVCoordinates} PV<sub>A</sub> to PV<sub>B</sub>.
76   *
77   * With :  PV<sub>A</sub> = ({1, 0, 0}, {1, 0, 0});
78   * and  :  PV<sub>B</sub> = ({0, 1, 0}, {-2, 1, 0});
79   *
80   * The transform to apply then is defined as follows :
81   *
82   * Rotation rotation = new Rotation(Vector3D.PLUS_K, FastMath.PI / 2);
83   * Vector3D rotationRate = new Vector3D(0, 0, -2);
84   *
85   * Transform R1toR2 = new Transform(rotation, rotationRate);
86   *
87   * PV<sub>B</sub> = R1toR2.transformPVCoordinates(PV<sub>A</sub>);
88   *
89   * </pre>
90   *
91   * @author Luc Maisonobe
92   * @author Fabien Maussion
93   */
94  public class Transform
95      implements TimeStamped, TimeShiftable<Transform>, TimeInterpolable<Transform>, Serializable {
96  
97      /** Identity transform. */
98      public static final Transform IDENTITY = new IdentityTransform();
99  
100     /** Serializable UID. */
101     private static final long serialVersionUID = -8809893979516295102L;
102 
103     /** Date of the transform. */
104     private final AbsoluteDate date;
105 
106     /** Cartesian coordinates of the target frame with respect to the original frame. */
107     private final PVCoordinates cartesian;
108 
109     /** Angular coordinates of the target frame with respect to the original frame. */
110     private final AngularCoordinates angular;
111 
112     /** Build a transform from its primitive operations.
113      * @param date date of the transform
114      * @param cartesian Cartesian coordinates of the target frame with respect to the original frame
115      * @param angular angular coordinates of the target frame with respect to the original frame
116      */
117     private Transform(final AbsoluteDate date,
118                       final PVCoordinates cartesian, final AngularCoordinates angular) {
119         this.date      = date;
120         this.cartesian = cartesian;
121         this.angular   = angular;
122     }
123 
124     /** Build a translation transform.
125      * @param date date of the transform
126      * @param translation translation to apply (i.e. coordinates of
127      * the transformed origin, or coordinates of the origin of the
128      * old frame in the new frame)
129      */
130     public Transform(final AbsoluteDate date, final Vector3D translation) {
131         this(date, new PVCoordinates(translation, Vector3D.ZERO), AngularCoordinates.IDENTITY);
132     }
133 
134     /** Build a rotation transform.
135      * @param date date of the transform
136      * @param rotation rotation to apply ( i.e. rotation to apply to the
137      * coordinates of a vector expressed in the old frame to obtain the
138      * same vector expressed in the new frame )
139      */
140     public Transform(final AbsoluteDate date, final Rotation rotation) {
141         this(date, PVCoordinates.ZERO, new AngularCoordinates(rotation, Vector3D.ZERO));
142     }
143 
144     /** Build a translation transform, with its first time derivative.
145      * @param date date of the transform
146      * @param translation translation to apply (i.e. coordinates of
147      * the transformed origin, or coordinates of the origin of the
148      * old frame in the new frame)
149      * @param velocity the velocity of the translation (i.e. origin
150      * of the old frame velocity in the new frame)
151      */
152     public Transform(final AbsoluteDate date, final Vector3D translation, final Vector3D velocity) {
153         this(date, new PVCoordinates(translation, velocity), AngularCoordinates.IDENTITY);
154     }
155 
156     /** Build a translation transform, with its first time derivative.
157      * @param date date of the transform
158      * @param cartesian cartesian part of the transformation to apply (i.e. coordinates of
159      * the transformed origin, or coordinates of the origin of the
160      * old frame in the new frame, with their derivatives)
161      */
162     public Transform(final AbsoluteDate date, final PVCoordinates cartesian) {
163         this(date, cartesian, AngularCoordinates.IDENTITY);
164     }
165 
166     /** Build a rotation transform.
167      * @param date date of the transform
168      * @param rotation rotation to apply ( i.e. rotation to apply to the
169      * coordinates of a vector expressed in the old frame to obtain the
170      * same vector expressed in the new frame )
171      * @param rotationRate the axis of the instant rotation
172      * expressed in the new frame. (norm representing angular rate)
173      */
174     public Transform(final AbsoluteDate date, final Rotation rotation, final Vector3D rotationRate) {
175         this(date, PVCoordinates.ZERO, new AngularCoordinates(rotation, rotationRate));
176     }
177 
178     /** Build a rotation transform.
179      * @param date date of the transform
180      * @param angular angular part of the transformation to apply (i.e. rotation to
181      * apply to the coordinates of a vector expressed in the old frame to obtain the
182      * same vector expressed in the new frame, with its rotation rate)
183      */
184     public Transform(final AbsoluteDate date, final AngularCoordinates angular) {
185         this(date, PVCoordinates.ZERO, angular);
186     }
187 
188     /** Build a transform by combining two existing ones.
189      * <p>
190      * Note that the dates of the two existing transformed are <em>ignored</em>,
191      * and the combined transform date is set to the date supplied in this constructor
192      * without any attempt to shift the raw transforms. This is a design choice allowing
193      * user full control of the combination.
194      * </p>
195      * @param date date of the transform
196      * @param first first transform applied
197      * @param second second transform applied
198      */
199     public Transform(final AbsoluteDate date, final Transform first, final Transform second) {
200         this(date,
201              new PVCoordinates(compositeTranslation(first, second),
202                                compositeVelocity(first, second)),
203              new AngularCoordinates(compositeRotation(first, second),
204                                     compositeRotationRate(first, second)));
205     }
206 
207     /** Compute a composite translation.
208      * @param first first applied transform
209      * @param second second applied transform
210      * @return translation part of the composite transform
211      */
212     private static Vector3D compositeTranslation(final Transform first, final Transform second) {
213 
214         final Vector3D p1 = first.cartesian.getPosition();
215         final Rotation r1 = first.angular.getRotation();
216         final Vector3D p2 = second.cartesian.getPosition();
217 
218         return p1.add(r1.applyInverseTo(p2));
219 
220     }
221 
222     /** Compute a composite velocity.
223      * @param first first applied transform
224      * @param second second applied transform
225      * @return velocity part of the composite transform
226      */
227     private static Vector3D compositeVelocity(final Transform first, final Transform second) {
228 
229         final Vector3D v1 = first.cartesian.getVelocity();
230         final Rotation r1 = first.angular.getRotation();
231         final Vector3D o1 = first.angular.getRotationRate();
232         final Vector3D p2 = second.cartesian.getPosition();
233         final Vector3D v2 = second.cartesian.getVelocity();
234 
235         return v1.add(r1.applyInverseTo(v2.add(Vector3D.crossProduct(o1, p2))));
236 
237     }
238 
239     /** Compute a composite rotation.
240      * @param first first applied transform
241      * @param second second applied transform
242      * @return rotation part of the composite transform
243      */
244     private static Rotation compositeRotation(final Transform first, final Transform second) {
245 
246         final Rotation r1 = first.angular.getRotation();
247         final Rotation r2 = second.angular.getRotation();
248 
249         return r2.applyTo(r1);
250 
251     }
252 
253     /** Compute a composite rotation rate.
254      * @param first first applied transform
255      * @param second second applied transform
256      * @return rotation rate part of the composite transform
257      */
258     private static Vector3D compositeRotationRate(final Transform first, final Transform second) {
259 
260         final Vector3D o1 = first.angular.getRotationRate();
261         final Rotation r2 = second.angular.getRotation();
262         final Vector3D o2 = second.angular.getRotationRate();
263 
264         return o2.add(r2.applyTo(o1));
265 
266     }
267 
268     /** {@inheritDoc} */
269     public AbsoluteDate getDate() {
270         return date;
271     }
272 
273     /** {@inheritDoc} */
274     public Transform shiftedBy(final double dt) {
275         return new Transform(date.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt));
276     };
277 
278     /** {@inheritDoc}
279      * <p>
280      * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate, boolean,
281      * boolean, Collection)} with both {@code useVelocities} and {@code useRotationRates}
282      * set to true.
283      * </p>
284      */
285     public Transform interpolate(final AbsoluteDate interpolationDate,
286                                  final Collection<Transform> sample) {
287         return interpolate(interpolationDate, true, true, sample);
288     }
289 
290     /** Interpolate a transform from a sample set of existing transforms.
291      * <p>
292      * Note that even if first time derivatives (velocities and rotation rates)
293      * from sample can be ignored, the interpolated instance always includes
294      * interpolated derivatives. This feature can be used explicitly to
295      * compute these derivatives when it would be too complex to compute them
296      * from an analytical formula: just compute a few sample points from the
297      * explicit formula and set the derivatives to zero in these sample points,
298      * then use interpolation to add derivatives consistent with the positions
299      * and rotations.
300      * </p>
301      * <p>
302      * As this implementation of interpolation is polynomial, it should be used only
303      * with small samples (about 10-20 points) in order to avoid <a
304      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
305      * and numerical problems (including NaN appearing).
306      * </p>
307      * @param date interpolation date
308      * @param useVelocities if true, use sample transforms velocities,
309      * otherwise ignore them and use only positions
310      * @param useRotationRates if true, use sample points rotation rates,
311      * otherwise ignore them and use only rotations
312      * @param sample sample points on which interpolation should be done
313      * @return a new instance, interpolated at specified date
314      */
315     public static Transform interpolate(final AbsoluteDate date,
316                                         final boolean useVelocities, final boolean useRotationRates,
317                                         final Collection<Transform> sample) {
318         final List<Pair<AbsoluteDate, PVCoordinates>> datedPV =
319                 new ArrayList<Pair<AbsoluteDate, PVCoordinates>>(sample.size());
320         final List<Pair<AbsoluteDate, AngularCoordinates>> datedAC =
321                 new ArrayList<Pair<AbsoluteDate, AngularCoordinates>>(sample.size());
322         for (final Transform transform : sample) {
323             datedPV.add(new Pair<AbsoluteDate, PVCoordinates>(transform.getDate(),
324                     transform.getCartesian()));
325             datedAC.add(new Pair<AbsoluteDate, AngularCoordinates>(transform.getDate(),
326                     transform.getAngular()));
327         }
328         final PVCoordinates      interpolatedPV = PVCoordinates.interpolate(date, useVelocities, datedPV);
329         final AngularCoordinates interpolatedAC = AngularCoordinates.interpolate(date, useRotationRates, datedAC);
330         return new Transform(date, interpolatedPV, interpolatedAC);
331     }
332 
333     /** Get the inverse transform of the instance.
334      * @return inverse transform of the instance
335      */
336     public Transform getInverse() {
337 
338         final Vector3D p = cartesian.getPosition();
339         final Vector3D v = cartesian.getVelocity();
340         final Rotation r = angular.getRotation();
341         final Vector3D o = angular.getRotationRate();
342 
343         final Vector3D rT = r.applyTo(p);
344         return new Transform(date,
345                              new PVCoordinates(rT.negate(),
346                                                Vector3D.crossProduct(o, rT).subtract(r.applyTo(v))),
347                              angular.revert());
348 
349     }
350 
351     /** Get a freezed transform.
352      * <p>
353      * This method creates a copy of the instance but frozen in time,
354      * i.e. with velocity and rotation rate forced to zero.
355      * </p>
356      * @return a new transform, without any time-dependent parts
357      */
358     public Transform freeze() {
359         return new Transform(date,
360                              new PVCoordinates(cartesian.getPosition(), Vector3D.ZERO),
361                              new AngularCoordinates(angular.getRotation(), Vector3D.ZERO));
362     }
363 
364     /** Transform a position vector (including translation effects).
365      * @param position vector to transform
366      * @return transformed position
367      */
368     public Vector3D transformPosition(final Vector3D position) {
369         return angular.getRotation().applyTo(cartesian.getPosition().add(position));
370     }
371 
372     /** Transform a position vector (including translation effects).
373      * @param position vector to transform
374      * @param <T> the type of the field elements
375      * @return transformed position
376      */
377     public <T extends RealFieldElement<T>> FieldVector3D<T> transformPosition(final FieldVector3D<T> position) {
378         return FieldRotation.applyTo(angular.getRotation(), position.add(cartesian.getPosition()));
379     }
380 
381     /** Transform a vector (ignoring translation effects).
382      * @param vector vector to transform
383      * @return transformed vector
384      */
385     public Vector3D transformVector(final Vector3D vector) {
386         return angular.getRotation().applyTo(vector);
387     }
388 
389     /** Transform a vector (ignoring translation effects).
390      * @param vector vector to transform
391      * @param <T> the type of the field elements
392      * @return transformed vector
393      */
394     public <T extends RealFieldElement<T>> FieldVector3D<T> transformVector(final FieldVector3D<T> vector) {
395         return FieldRotation.applyTo(angular.getRotation(), vector);
396     }
397 
398     /** Transform a line.
399      * @param line to transform
400      * @return transformed line
401      */
402     public Line transformLine(final Line line) {
403         final Vector3D transformedP0 = transformPosition(line.getOrigin());
404         final Vector3D transformedP1 = transformPosition(line.pointAt(1.0e6));
405         return new Line(transformedP0, transformedP1);
406     }
407 
408     /** Transform {@link PVCoordinates} including kinematic effects.
409      * @param pv the couple position-velocity to transform.
410      * @return transformed position/velocity
411      */
412     public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
413         final Vector3D p = pv.getPosition();
414         final Vector3D v = pv.getVelocity();
415         final Vector3D transformedP = angular.getRotation().applyTo(cartesian.getPosition().add(p));
416         final Vector3D cross = Vector3D.crossProduct(angular.getRotationRate(), transformedP);
417         return new PVCoordinates(transformedP,
418                                  angular.getRotation().applyTo(v.add(cartesian.getVelocity())).subtract(cross));
419     }
420 
421     /** Transform {@link FieldPVCoordinates} including kinematic effects.
422      * @param pv the couple position-velocity to transform.
423      * @param <T> the type of the field elements
424      * @return transformed position/velocity
425      */
426     public <T extends RealFieldElement<T>> FieldPVCoordinates<T> transformPVCoordinates(final FieldPVCoordinates<T> pv) {
427         final FieldVector3D<T> p = pv.getPosition();
428         final FieldVector3D<T> v = pv.getVelocity();
429         final FieldVector3D<T> transformedP = FieldRotation.applyTo(angular.getRotation(),
430                                                                     p.add(cartesian.getPosition()));
431         final FieldVector3D<T> cross = FieldVector3D.crossProduct(angular.getRotationRate(), transformedP);
432         return new FieldPVCoordinates<T>(transformedP,
433                                          FieldRotation.applyTo(angular.getRotation(),
434                                                                v.add(cartesian.getVelocity())).subtract(cross));
435     }
436 
437     /** Compute the Jacobian of the {@link #transformPVCoordinates(PVCoordinates)}
438      * method of the transform.
439      * <p>
440      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
441      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
442      * of the input {@link PVCoordinates} in method {@link #transformPVCoordinates(PVCoordinates)}.
443      * </p>
444      * <p>
445      * This definition implies that if we define position-velocity coordinates
446      * <pre>
447      * PV<sub>1</sub> = transform.transformPVCoordinates(PV<sub>0</sub>), then
448      * </pre>
449      * their differentials dPV<sub>1</sub> and dPV<sub>0</sub> will obey the following relation
450      * where J is the matrix computed by this method:<br/>
451      * <pre>
452      * dPV<sub>1</sub> = J &times; dPV<sub>0</sub>
453      * </pre>
454      * </p>
455      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
456      * is larger than 6x6, only the 6x6 upper left corner will be modified
457      */
458     public void getJacobian(final double[][] jacobian) {
459 
460         // elementary matrix for rotation
461         final double[][] mData = angular.getRotation().getMatrix();
462 
463         // dP1/dP0
464         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
465         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
466         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);
467 
468         // dP1/dV0
469         Arrays.fill(jacobian[0], 3, 6, 0.0);
470         Arrays.fill(jacobian[1], 3, 6, 0.0);
471         Arrays.fill(jacobian[2], 3, 6, 0.0);
472 
473         // dV1/dP0
474         final Vector3D o = angular.getRotationRate();
475         final double mOx = -o.getX();
476         final double mOy = -o.getY();
477         final double mOz = -o.getZ();
478         for (int i = 0; i < 3; ++i) {
479             jacobian[3][i] = mOy * mData[2][i] - mOz * mData[1][i];
480             jacobian[4][i] = mOz * mData[0][i] - mOx * mData[2][i];
481             jacobian[5][i] = mOx * mData[1][i] - mOy * mData[0][i];
482         }
483 
484         // dV1/dV0
485         System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
486         System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
487         System.arraycopy(mData[2], 0, jacobian[5], 3, 3);
488 
489     }
490 
491     /** Get the underlying elementary cartesian part.
492      * <p>A transform can be uniquely represented as an elementary
493      * translation followed by an elementary rotation. This method
494      * returns this unique elementary translation with its derivative.</p>
495      * @return underlying elementary cartesian part
496      * @see #getTranslation()
497      * @see #getVelocity()
498      */
499     public PVCoordinates getCartesian() {
500         return cartesian;
501     }
502 
503     /** Get the underlying elementary translation.
504      * <p>A transform can be uniquely represented as an elementary
505      * translation followed by an elementary rotation. This method
506      * returns this unique elementary translation.</p>
507      * @return underlying elementary translation
508      * @see #getCartesian()
509      * @see #getVelocity()
510      */
511     public Vector3D getTranslation() {
512         return cartesian.getPosition();
513     }
514 
515     /** Get the first time derivative of the translation.
516      * @return first time derivative of the translation
517      * @see #getCartesian()
518      * @see #getTranslation()
519      */
520     public Vector3D getVelocity() {
521         return cartesian.getVelocity();
522     }
523 
524     /** Get the underlying elementary angular part.
525      * <p>A transform can be uniquely represented as an elementary
526      * translation followed by an elementary rotation. This method
527      * returns this unique elementary rotation with its derivative.</p>
528      * @return underlying elementary angular part
529      * @see #getRotation()
530      * @see #getRotationRate()
531      */
532     public AngularCoordinates getAngular() {
533         return angular;
534     }
535 
536     /** Get the underlying elementary rotation.
537      * <p>A transform can be uniquely represented as an elementary
538      * translation followed by an elementary rotation. This method
539      * returns this unique elementary rotation.</p>
540      * @return underlying elementary rotation
541      * @see #getAngular()
542      * @see #getRotationRate()
543      */
544     public Rotation getRotation() {
545         return angular.getRotation();
546     }
547 
548     /** Get the first time derivative of the rotation.
549      * <p>The norm represents the angular rate.</p>
550      * @return First time derivative of the rotation
551      * @see #getAngular()
552      * @see #getRotation()
553      */
554     public Vector3D getRotationRate() {
555         return angular.getRotationRate();
556     }
557 
558     /** Specialized class for identity transform. */
559     private static class IdentityTransform extends Transform {
560 
561         /** Serializable UID. */
562         private static final long serialVersionUID = -9042082036141830517L;
563 
564         /** Simple constructor. */
565         public IdentityTransform() {
566             super(AbsoluteDate.J2000_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY);
567         }
568 
569         /** {@inheritDoc} */
570         @Override
571         public Transform shiftedBy(final double dt) {
572             return this;
573         }
574 
575         /** {@inheritDoc} */
576         @Override
577         public Transform getInverse() {
578             return this;
579         };
580 
581         /** {@inheritDoc} */
582         @Override
583         public Vector3D transformPosition(final Vector3D position) {
584             return position;
585         }
586 
587         /** {@inheritDoc} */
588         @Override
589         public Vector3D transformVector(final Vector3D vector) {
590             return vector;
591         }
592 
593         /** {@inheritDoc} */
594         @Override
595         public Line transformLine(final Line line) {
596             return line;
597         }
598 
599         /** {@inheritDoc} */
600         @Override
601         public PVCoordinates transformPVCoordinates(final PVCoordinates pv) {
602             return pv;
603         }
604 
605         /** {@inheritDoc} */
606         @Override
607         public void getJacobian(final double[][] jacobian) {
608             for (int i = 0; i < 6; ++i) {
609                 Arrays.fill(jacobian[i], 0, 6, 0.0);
610                 jacobian[i][i] = 1.0;
611             }
612         }
613 
614     }
615 
616 }