1   /* Copyright 2002-2018 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.attitudes;
18  
19  import java.io.Serializable;
20  import java.util.List;
21  import java.util.stream.Collectors;
22  import java.util.stream.Stream;
23  
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.frames.Frame;
29  import org.orekit.frames.Transform;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.TimeInterpolable;
32  import org.orekit.time.TimeShiftable;
33  import org.orekit.time.TimeStamped;
34  import org.orekit.utils.AngularCoordinates;
35  import org.orekit.utils.AngularDerivativesFilter;
36  import org.orekit.utils.TimeStampedAngularCoordinates;
37  
38  
39  /** This class handles attitude definition at a given date.
40  
41   * <p>This class represents the rotation between a reference frame and
42   * the satellite frame, as well as the spin of the satellite (axis and
43   * rotation rate).</p>
44   * <p>
45   * The state can be slightly shifted to close dates. This shift is based on
46   * a linear extrapolation for attitude taking the spin rate into account.
47   * It is <em>not</em> intended as a replacement for proper attitude propagation
48   * but should be sufficient for either small time shifts or coarse accuracy.
49   * </p>
50   * <p>The instance <code>Attitude</code> is guaranteed to be immutable.</p>
51   * @see     org.orekit.orbits.Orbit
52   * @see AttitudeProvider
53   * @author V&eacute;ronique Pommier-Maurussane
54   */
55  
56  public class Attitude
57      implements TimeStamped, TimeShiftable<Attitude>, TimeInterpolable<Attitude>, Serializable {
58  
59      /** Serializable UID. */
60      private static final long serialVersionUID = 20140611L;
61  
62      /** Reference frame. */
63      private final Frame referenceFrame;
64  
65       /** Attitude and spin.  */
66      private final TimeStampedAngularCoordinates orientation;
67  
68      /** Creates a new instance.
69       * @param referenceFrame reference frame from which attitude is defined
70       * @param orientation complete orientation between reference frame and satellite frame,
71       * including rotation rate
72       */
73      public Attitude(final Frame referenceFrame, final TimeStampedAngularCoordinates orientation) {
74          this.referenceFrame = referenceFrame;
75          this.orientation    = orientation;
76      }
77  
78      /** Creates a new instance.
79       * @param date date at which attitude is defined
80       * @param referenceFrame reference frame from which attitude is defined
81       * @param orientation complete orientation between reference frame and satellite frame,
82       * including rotation rate
83       */
84      public Attitude(final AbsoluteDate date, final Frame referenceFrame,
85                      final AngularCoordinates orientation) {
86          this(referenceFrame,
87               new TimeStampedAngularCoordinates(date,
88                                                 orientation.getRotation(),
89                                                 orientation.getRotationRate(),
90                                                 orientation.getRotationAcceleration()));
91      }
92  
93      /** Creates a new instance.
94       * @param date date at which attitude is defined
95       * @param referenceFrame reference frame from which attitude is defined
96       * @param attitude rotation between reference frame and satellite frame
97       * @param spin satellite spin (axis and velocity, in <strong>satellite</strong> frame)
98       * @param acceleration satellite rotation acceleration (in <strong>satellite</strong> frame)
99       */
100     public Attitude(final AbsoluteDate date, final Frame referenceFrame,
101                     final Rotation attitude, final Vector3D spin, final Vector3D acceleration) {
102         this(referenceFrame, new TimeStampedAngularCoordinates(date, attitude, spin, acceleration));
103     }
104 
105     /** Get a time-shifted attitude.
106      * <p>
107      * The state can be slightly shifted to close dates. This shift is based on
108      * a linear extrapolation for attitude taking the spin rate into account.
109      * It is <em>not</em> intended as a replacement for proper attitude propagation
110      * but should be sufficient for either small time shifts or coarse accuracy.
111      * </p>
112      * @param dt time shift in seconds
113      * @return a new attitude, shifted with respect to the instance (which is immutable)
114      */
115     public Attitude shiftedBy(final double dt) {
116         return new Attitude(referenceFrame, orientation.shiftedBy(dt));
117     }
118 
119     /** Get a similar attitude with a specific reference frame.
120      * <p>
121      * If the instance reference frame is already the specified one, the instance
122      * itself is returned without any object creation. Otherwise, a new instance
123      * will be created with the specified reference frame. In this case, the
124      * required intermediate rotation and spin between the specified and the
125      * original reference frame will be inserted.
126      * </p>
127      * @param newReferenceFrame desired reference frame for attitude
128      * @return an attitude that has the same orientation and motion as the instance,
129      * but guaranteed to have the specified reference frame
130      * @exception OrekitException if conversion between reference frames fails
131      */
132     public Attitude withReferenceFrame(final Frame newReferenceFrame)
133         throws OrekitException {
134 
135         if (newReferenceFrame == referenceFrame) {
136             // simple case, the instance is already compliant
137             return this;
138         }
139 
140         // we have to take an intermediate rotation into account
141         final Transform t = newReferenceFrame.getTransformTo(referenceFrame, orientation.getDate());
142         return new Attitude(orientation.getDate(), newReferenceFrame,
143                             orientation.getRotation().compose(t.getRotation(), RotationConvention.VECTOR_OPERATOR),
144                             orientation.getRotationRate().add(orientation.getRotation().applyTo(t.getRotationRate())),
145                             orientation.getRotationAcceleration().add(orientation.getRotation().applyTo(t.getRotationAcceleration())));
146 
147     }
148 
149     /** Get the date of attitude parameters.
150      * @return date of the attitude parameters
151      */
152     public AbsoluteDate getDate() {
153         return orientation.getDate();
154     }
155 
156     /** Get the reference frame.
157      * @return referenceFrame reference frame from which attitude is defined.
158      */
159     public Frame getReferenceFrame() {
160         return referenceFrame;
161     }
162 
163     /** Get the complete orientation including spin.
164      * @return complete orientation including spin
165      * @see #getRotation()
166      * @see #getSpin()
167      */
168     public TimeStampedAngularCoordinates getOrientation() {
169         return orientation;
170     }
171 
172     /** Get the attitude rotation.
173      * @return attitude satellite rotation from reference frame.
174      * @see #getOrientation()
175      * @see #getSpin()
176      */
177     public Rotation getRotation() {
178         return orientation.getRotation();
179     }
180 
181     /** Get the satellite spin.
182      * <p>The spin vector is defined in <strong>satellite</strong> frame.</p>
183      * @return spin satellite spin (axis and velocity).
184      * @see #getOrientation()
185      * @see #getRotation()
186      */
187     public Vector3D getSpin() {
188         return orientation.getRotationRate();
189     }
190 
191     /** Get the satellite rotation acceleration.
192      * <p>The rotation acceleration. vector is defined in <strong>satellite</strong> frame.</p>
193      * @return rotation acceleration
194      * @see #getOrientation()
195      * @see #getRotation()
196      */
197     public Vector3D getRotationAcceleration() {
198         return orientation.getRotationAcceleration();
199     }
200 
201     /** {@inheritDoc}
202      * <p>
203      * The interpolated instance is created by polynomial Hermite interpolation
204      * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation.
205      * </p>
206      * <p>
207      * As this implementation of interpolation is polynomial, it should be used only
208      * with small samples (about 10-20 points) in order to avoid <a
209      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
210      * and numerical problems (including NaN appearing).
211      * </p>
212      * @exception OrekitException if the number of point is too small for interpolating
213      */
214     public Attitude interpolate(final AbsoluteDate interpolationDate, final Stream<Attitude> sample)
215         throws OrekitException {
216         final List<TimeStampedAngularCoordinates> datedPV =
217              sample.map(attitude -> attitude.orientation).collect(Collectors.toList());
218         final TimeStampedAngularCoordinates interpolated =
219                 TimeStampedAngularCoordinates.interpolate(interpolationDate, AngularDerivativesFilter.USE_RR, datedPV);
220         return new Attitude(referenceFrame, interpolated);
221     }
222 
223 }