1   /* Copyright 2002-2020 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.utils;
18  
19  import java.io.Serializable;
20  import java.util.stream.Stream;
21  
22  import org.hipparchus.analysis.differentiation.DerivativeStructure;
23  import org.hipparchus.analysis.interpolation.HermiteInterpolator;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.util.FastMath;
27  import org.orekit.annotation.DefaultDataContext;
28  import org.orekit.data.DataContext;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitIllegalArgumentException;
31  import org.orekit.errors.OrekitInternalError;
32  import org.orekit.errors.OrekitMessages;
33  import org.orekit.frames.Frame;
34  import org.orekit.frames.Transform;
35  import org.orekit.time.AbsoluteDate;
36  import org.orekit.time.TimeInterpolable;
37  import org.orekit.time.TimeStamped;
38  
39  /** Position - Velocity - Acceleration linked to a date and a frame.
40   */
41  public class AbsolutePVCoordinates extends TimeStampedPVCoordinates
42      implements TimeStamped, TimeInterpolable<AbsolutePVCoordinates>,
43                 Serializable, PVCoordinatesProvider {
44  
45      /** Serializable UID. */
46      private static final long serialVersionUID = 20150824L;
47  
48      /** Frame in which are defined the coordinates. */
49      private final Frame frame;
50  
51      /** Build from position, velocity, acceleration.
52       * @param frame the frame in which the coordinates are defined
53       * @param date coordinates date
54       * @param position the position vector (m)
55       * @param velocity the velocity vector (m/s)
56       * @param acceleration the acceleration vector (m/sÂý)
57       */
58      public AbsolutePVCoordinates(final Frame frame, final AbsoluteDate date,
59                                   final Vector3D position, final Vector3D velocity, final Vector3D acceleration) {
60          super(date, position, velocity, acceleration);
61          this.frame = frame;
62      }
63  
64      /** Build from position and velocity. Acceleration is set to zero.
65       * @param frame the frame in which the coordinates are defined
66       * @param date coordinates date
67       * @param position the position vector (m)
68       * @param velocity the velocity vector (m/s)
69       */
70      public AbsolutePVCoordinates(final Frame frame, final AbsoluteDate date,
71                                   final Vector3D position,
72                                   final Vector3D velocity) {
73          this(frame, date, position, velocity, Vector3D.ZERO);
74      }
75  
76      /** Build from frame, date and PVA coordinates.
77       * @param frame the frame in which the coordinates are defined
78       * @param date date of the coordinates
79       * @param pva TimeStampedPVCoordinates
80       */
81      public AbsolutePVCoordinates(final Frame frame, final AbsoluteDate date, final PVCoordinates pva) {
82          super(date, pva);
83          this.frame = frame;
84      }
85  
86      /** Build from frame and TimeStampedPVCoordinates.
87       * @param frame the frame in which the coordinates are defined
88       * @param pva TimeStampedPVCoordinates
89       */
90      public AbsolutePVCoordinates(final Frame frame, final TimeStampedPVCoordinates pva) {
91          super(pva.getDate(), pva);
92          this.frame = frame;
93      }
94  
95      /** Multiplicative constructor
96       * <p>Build a AbsolutePVCoordinates from another one and a scale factor.</p>
97       * <p>The TimeStampedPVCoordinates built will be a * AbsPva</p>
98       * @param date date of the built coordinates
99       * @param a scale factor
100      * @param AbsPva base (unscaled) AbsolutePVCoordinates
101      */
102     public AbsolutePVCoordinates(final AbsoluteDate date,
103                                  final double a, final AbsolutePVCoordinates AbsPva) {
104         super(date, a, AbsPva);
105         this.frame = AbsPva.frame;
106     }
107 
108     /** Subtractive constructor
109      * <p>Build a relative AbsolutePVCoordinates from a start and an end position.</p>
110      * <p>The AbsolutePVCoordinates built will be end - start.</p>
111      * <p>In case start and end use two different pseudo-inertial frames,
112      * the new AbsolutePVCoordinates arbitrarily be defined in the start frame. </p>
113      * @param date date of the built coordinates
114      * @param start Starting AbsolutePVCoordinates
115      * @param end ending AbsolutePVCoordinates
116      */
117     public AbsolutePVCoordinates(final AbsoluteDate date,
118                                  final AbsolutePVCoordinatessolutePVCoordinates">AbsolutePVCoordinates start, final AbsolutePVCoordinates end) {
119         super(date, start, end);
120         ensureIdenticalFrames(start, end);
121         this.frame = start.frame;
122     }
123 
124     /** Linear constructor
125      * <p>Build a AbsolutePVCoordinates from two other ones and corresponding scale factors.</p>
126      * <p>The AbsolutePVCoordinates built will be a1 * u1 + a2 * u2</p>
127      * <p>In case the AbsolutePVCoordinates use different pseudo-inertial frames,
128      * the new AbsolutePVCoordinates arbitrarily be defined in the first frame. </p>
129      * @param date date of the built coordinates
130      * @param a1 first scale factor
131      * @param absPv1 first base (unscaled) AbsolutePVCoordinates
132      * @param a2 second scale factor
133      * @param absPv2 second base (unscaled) AbsolutePVCoordinates
134      */
135     public AbsolutePVCoordinates(final AbsoluteDate date,
136                                  final double a1, final AbsolutePVCoordinates absPv1,
137                                  final double a2, final AbsolutePVCoordinates absPv2) {
138         super(date, a1, absPv1.getPVCoordinates(), a2, absPv2.getPVCoordinates());
139         ensureIdenticalFrames(absPv1, absPv2);
140         this.frame = absPv1.getFrame();
141     }
142 
143     /** Linear constructor
144      * <p>Build a AbsolutePVCoordinates from three other ones and corresponding scale factors.</p>
145      * <p>The AbsolutePVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3</p>
146      * <p>In case the AbsolutePVCoordinates use different pseudo-inertial frames,
147      * the new AbsolutePVCoordinates arbitrarily be defined in the first frame. </p>
148      * @param date date of the built coordinates
149      * @param a1 first scale factor
150      * @param absPv1 first base (unscaled) AbsolutePVCoordinates
151      * @param a2 second scale factor
152      * @param absPv2 second base (unscaled) AbsolutePVCoordinates
153      * @param a3 third scale factor
154      * @param absPv3 third base (unscaled) AbsolutePVCoordinates
155      */
156     public AbsolutePVCoordinates(final AbsoluteDate date,
157                                  final double a1, final AbsolutePVCoordinates absPv1,
158                                  final double a2, final AbsolutePVCoordinates absPv2,
159                                  final double a3, final AbsolutePVCoordinates absPv3) {
160         super(date, a1, absPv1.getPVCoordinates(), a2, absPv2.getPVCoordinates(),
161                 a3, absPv3.getPVCoordinates());
162         ensureIdenticalFrames(absPv1, absPv2);
163         ensureIdenticalFrames(absPv1, absPv3);
164         this.frame = absPv1.getFrame();
165     }
166 
167     /** Linear constructor
168      * <p>Build a AbsolutePVCoordinates from four other ones and corresponding scale factors.</p>
169      * <p>The AbsolutePVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4</p>
170      * <p>In case the AbsolutePVCoordinates use different pseudo-inertial frames,
171      * the new AbsolutePVCoordinates arbitrarily be defined in the first frame. </p>
172      * @param date date of the built coordinates
173      * @param a1 first scale factor
174      * @param absPv1 first base (unscaled) AbsolutePVCoordinates
175      * @param a2 second scale factor
176      * @param absPv2 second base (unscaled) AbsolutePVCoordinates
177      * @param a3 third scale factor
178      * @param absPv3 third base (unscaled) AbsolutePVCoordinates
179      * @param a4 fourth scale factor
180      * @param absPv4 fourth base (unscaled) AbsolutePVCoordinates
181      */
182     public AbsolutePVCoordinates(final AbsoluteDate date,
183                                  final double a1, final AbsolutePVCoordinates absPv1,
184                                  final double a2, final AbsolutePVCoordinates absPv2,
185                                  final double a3, final AbsolutePVCoordinates absPv3,
186                                  final double a4, final AbsolutePVCoordinates absPv4) {
187         super(date, a1, absPv1.getPVCoordinates(), a2, absPv2.getPVCoordinates(),
188                 a3, absPv3.getPVCoordinates(), a4, absPv4.getPVCoordinates());
189         ensureIdenticalFrames(absPv1, absPv2);
190         ensureIdenticalFrames(absPv1, absPv3);
191         ensureIdenticalFrames(absPv1, absPv4);
192         this.frame = absPv1.getFrame();
193     }
194 
195     /** Builds a AbsolutePVCoordinates triplet from  a {@link FieldVector3D}&lt;{@link DerivativeStructure}&gt;.
196      * <p>
197      * The vector components must have time as their only derivation parameter and
198      * have consistent derivation orders.
199      * </p>
200      * @param frame the frame in which the parameters are defined
201      * @param date date of the built coordinates
202      * @param p vector with time-derivatives embedded within the coordinates
203      */
204     public AbsolutePVCoordinates(final Frame frame, final AbsoluteDate date,
205             final FieldVector3D<DerivativeStructure> p) {
206         super(date, p);
207         this.frame = frame;
208     }
209 
210     /** Ensure that the frames from two AbsolutePVCoordinates are identical.
211      * @param absPv1 first AbsolutePVCoordinates
212      * @param absPv2 first AbsolutePVCoordinates
213      * @throws OrekitIllegalArgumentException if frames are different
214      */
215     private static void ensureIdenticalFrames(final AbsolutePVCoordinatesolutePVCoordinates">AbsolutePVCoordinates absPv1, final AbsolutePVCoordinates absPv2)
216         throws OrekitIllegalArgumentException {
217         if (!absPv1.frame.equals(absPv2.frame)) {
218             throw new OrekitIllegalArgumentException(OrekitMessages.INCOMPATIBLE_FRAMES,
219                                                      absPv1.frame.getName(), absPv2.frame.getName());
220         }
221     }
222 
223     /** Get a time-shifted state.
224      * <p>
225      * The state can be slightly shifted to close dates. This shift is based on
226      * a simple Taylor expansion. It is <em>not</em> intended as a replacement for
227      * proper orbit propagation (it is not even Keplerian!) but should be sufficient
228      * for either small time shifts or coarse accuracy.
229      * </p>
230      * @param dt time shift in seconds
231      * @return a new state, shifted with respect to the instance (which is immutable)
232      */
233     public AbsolutePVCoordinates shiftedBy(final double dt) {
234         final TimeStampedPVCoordinates spv = super.shiftedBy(dt);
235         return new AbsolutePVCoordinates(frame, spv);
236     }
237 
238     /** Create a local provider using simply Taylor expansion through {@link #shiftedBy(double)}.
239      * <p>
240      * The time evolution is based on a simple Taylor expansion. It is <em>not</em> intended as a
241      * replacement for proper orbit propagation (it is not even Keplerian!) but should be sufficient
242      * for either small time shifts or coarse accuracy.
243      * </p>
244      * @return provider based on Taylor expansion, for small time shifts around instance date
245      */
246     public PVCoordinatesProvider toTaylorProvider() {
247         return new PVCoordinatesProvider() {
248             /** {@inheritDoc} */
249             public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d,  final Frame f) {
250                 final TimeStampedPVCoordinates shifted   = shiftedBy(d.durationFrom(getDate()));
251                 final Transform                transform = frame.getTransformTo(f, d);
252                 return transform.transformPVCoordinates(shifted);
253             }
254         };
255     }
256 
257     /** Get the frame in which the coordinates are defined.
258      * @return frame in which the coordinates are defined
259      */
260     public Frame getFrame() {
261         return frame;
262     }
263 
264     /** Get the TimeStampedPVCoordinates.
265      * @return TimeStampedPVCoordinates
266      */
267     public TimeStampedPVCoordinates getPVCoordinates() {
268         return this;
269     }
270 
271     /** Get the TimeStampedPVCoordinates in a specified frame.
272      * @param outputFrame frame in which the position/velocity coordinates shall be computed
273      * @return TimeStampedPVCoordinates
274      * @exception OrekitException if transformation between frames cannot be computed
275      * @see #getPVCoordinates()
276      */
277     public TimeStampedPVCoordinates getPVCoordinates(final Frame outputFrame) {
278         // If output frame requested is the same as definition frame,
279         // PV coordinates are returned directly
280         if (outputFrame == frame) {
281             return getPVCoordinates();
282         }
283 
284         // Else, PV coordinates are transformed to output frame
285         final Transform t = frame.getTransformTo(outputFrame, getDate());
286         return t.transformPVCoordinates(getPVCoordinates());
287     }
288 
289     @Override
290     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate otherDate, final Frame outputFrame) {
291         return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(outputFrame);
292     }
293 
294     @Override
295     public AbsolutePVCoordinates interpolate(final AbsoluteDate date, final Stream<AbsolutePVCoordinates> sample) {
296         return interpolate(getFrame(), date, CartesianDerivativesFilter.USE_PVA, sample);
297     }
298 
299     /** Interpolate position-velocity.
300      * <p>
301      * The interpolated instance is created by polynomial Hermite interpolation
302      * ensuring velocity remains the exact derivative of position.
303      * </p>
304      * <p>
305      * Note that even if first time derivatives (velocities)
306      * from sample can be ignored, the interpolated instance always includes
307      * interpolated derivatives. This feature can be used explicitly to
308      * compute these derivatives when it would be too complex to compute them
309      * from an analytical formula: just compute a few sample points from the
310      * explicit formula and set the derivatives to zero in these sample points,
311      * then use interpolation to add derivatives consistent with the positions.
312      * </p>
313      * @param frame frame for the interpolted instance
314      * @param date interpolation date
315      * @param filter filter for derivatives from the sample to use in interpolation
316      * @param sample sample points on which interpolation should be done
317      * @return a new position-velocity, interpolated at specified date
318      * @exception OrekitIllegalArgumentException if some elements in the sample do not
319      * have the same defining frame as other
320      */
321     public static AbsolutePVCoordinates interpolate(final Frame frame, final AbsoluteDate date,
322                                                     final CartesianDerivativesFilter filter,
323                                                     final Stream<AbsolutePVCoordinates> sample) {
324 
325 
326         // set up an interpolator taking derivatives into account
327         final HermiteInterpolator interpolator = new HermiteInterpolator();
328 
329         // add sample points
330         switch (filter) {
331             case USE_P :
332                 // populate sample with position data, ignoring velocity
333                 sample.forEach(pv -> {
334                     final Vector3D position = pv.getPosition();
335                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
336                                                 position.toArray());
337                 });
338                 break;
339             case USE_PV :
340                 // populate sample with position and velocity data
341                 sample.forEach(pv -> {
342                     final Vector3D position = pv.getPosition();
343                     final Vector3D velocity = pv.getVelocity();
344                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
345                                                 position.toArray(), velocity.toArray());
346                 });
347                 break;
348             case USE_PVA :
349                 // populate sample with position, velocity and acceleration data
350                 sample.forEach(pv -> {
351                     final Vector3D position     = pv.getPosition();
352                     final Vector3D velocity     = pv.getVelocity();
353                     final Vector3D acceleration = pv.getAcceleration();
354                     interpolator.addSamplePoint(pv.getDate().durationFrom(date),
355                                                 position.toArray(), velocity.toArray(), acceleration.toArray());
356                 });
357                 break;
358             default :
359                 // this should never happen
360                 throw new OrekitInternalError(null);
361         }
362 
363         // interpolate
364         final double[][] p = interpolator.derivatives(0.0, 2);
365 
366         // build a new interpolated instance
367         return new AbsolutePVCoordinates(frame, date, new Vector3D(p[0]), new Vector3D(p[1]), new Vector3D(p[2]));
368 
369     }
370 
371     /** Replace the instance with a data transfer object for serialization.
372      * @return data transfer object that will be serialized
373      */
374     @DefaultDataContext
375     private Object writeReplace() {
376         return new DTO(this);
377     }
378 
379     /** Internal class used only for serialization. */
380     @DefaultDataContext
381     private static class DTO implements Serializable {
382 
383         /** Serializable UID. */
384         private static final long serialVersionUID = 20150916L;
385 
386         /** Double values. */
387         private double[] d;
388 
389         /** Frame in which acoordinates are defined. */
390         private final Frame frame;
391 
392         /** Simple constructor.
393          * @param absPva instance to serialize
394          */
395         private DTO(final AbsolutePVCoordinates absPva) {
396 
397             // decompose date
398             final AbsoluteDate j2000Epoch =
399                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
400             final double epoch  = FastMath.floor(absPva.getDate().durationFrom(j2000Epoch));
401             final double offset = absPva.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));
402 
403             this.d = new double[] {
404                 epoch, offset,
405                 absPva.getPosition().getX(),     absPva.getPosition().getY(),     absPva.getPosition().getZ(),
406                 absPva.getVelocity().getX(),     absPva.getVelocity().getY(),     absPva.getVelocity().getZ(),
407                 absPva.getAcceleration().getX(), absPva.getAcceleration().getY(), absPva.getAcceleration().getZ()
408             };
409             this.frame = absPva.frame;
410 
411         }
412 
413         /** Replace the deserialized data transfer object with a {@link AbsolutePVCoordinates}.
414          * @return replacement {@link AbsolutePVCoordinates}
415          */
416         private Object readResolve() {
417             final AbsoluteDate j2000Epoch =
418                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
419             return new AbsolutePVCoordinates(frame,
420                                              j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
421                                              new Vector3D(d[2], d[3], d[ 4]),
422                                              new Vector3D(d[5], d[6], d[ 7]),
423                                              new Vector3D(d[8], d[9], d[10]));
424         }
425 
426     }
427 
428 }
429 
430 
431