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.utils;
18  
19  import java.io.Serializable;
20  
21  import org.hipparchus.analysis.differentiation.Derivative;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.util.FastMath;
25  import org.orekit.annotation.DefaultDataContext;
26  import org.orekit.data.DataContext;
27  import org.orekit.frames.Frame;
28  import org.orekit.frames.StaticTransform;
29  import org.orekit.frames.Transform;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.TimeOffset;
32  import org.orekit.time.TimeScale;
33  import org.orekit.time.TimeStamped;
34  
35  /** {@link TimeStamped time-stamped} version of {@link PVCoordinates}.
36   * <p>Instances of this class are guaranteed to be immutable.</p>
37   * @author Luc Maisonobe
38   * @since 7.0
39   */
40  public class TimeStampedPVCoordinates extends PVCoordinates implements TimeStamped {
41  
42      /** Serializable UID. */
43      private static final long serialVersionUID = 20140723L;
44  
45      /** The date. */
46      private final AbsoluteDate date;
47  
48      /** Builds a TimeStampedPVCoordinates pair.
49       * @param date coordinates date
50       * @param position the position vector (m)
51       * @param velocity the velocity vector (m/s)
52       * @param acceleration the acceleration vector (m/s²)
53       */
54      public TimeStampedPVCoordinates(final AbsoluteDate date,
55                                      final Vector3D position, final Vector3D velocity, final Vector3D acceleration) {
56          super(position, velocity, acceleration);
57          this.date = date;
58      }
59  
60      /**
61       * Build from position and velocity. Acceleration is set to zero.
62       *
63       * @param date coordinates date
64       * @param position the position vector (m)
65       * @param velocity the velocity vector (m/s)
66       */
67      public TimeStampedPVCoordinates(final AbsoluteDate date,
68                                      final Vector3D position,
69                                      final Vector3D velocity) {
70          this(date, position, velocity, Vector3D.ZERO);
71      }
72  
73      /**
74       * Build from position velocity acceleration coordinates.
75       *
76       * @param date coordinates date
77       * @param pv position velocity, and acceleration coordinates, in meters and seconds.
78       */
79      public TimeStampedPVCoordinates(final AbsoluteDate date, final PVCoordinates pv) {
80          this(date, pv.getPosition(), pv.getVelocity(), pv.getAcceleration());
81      }
82  
83      /** Multiplicative constructor
84       * <p>Build a TimeStampedPVCoordinates from another one and a scale factor.</p>
85       * <p>The TimeStampedPVCoordinates built will be a * pv</p>
86       * @param date date of the built coordinates
87       * @param a scale factor
88       * @param pv base (unscaled) PVCoordinates
89       */
90      public TimeStampedPVCoordinates(final AbsoluteDate date,
91                                      final double a, final PVCoordinates pv) {
92          super(new Vector3D(a, pv.getPosition()),
93                new Vector3D(a, pv.getVelocity()),
94                new Vector3D(a, pv.getAcceleration()));
95          this.date = date;
96      }
97  
98      /** Subtractive constructor
99       * <p>Build a relative TimeStampedPVCoordinates from a start and an end position.</p>
100      * <p>The TimeStampedPVCoordinates built will be end - start.</p>
101      * @param date date of the built coordinates
102      * @param start Starting PVCoordinates
103      * @param end ending PVCoordinates
104      */
105     public TimeStampedPVCoordinates(final AbsoluteDate date,
106                                     final PVCoordinates start, final PVCoordinates end) {
107         super(end.getPosition().subtract(start.getPosition()),
108               end.getVelocity().subtract(start.getVelocity()),
109               end.getAcceleration().subtract(start.getAcceleration()));
110         this.date = date;
111     }
112 
113     /** Linear constructor
114      * <p>Build a TimeStampedPVCoordinates from two other ones and corresponding scale factors.</p>
115      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2</p>
116      * @param date date of the built coordinates
117      * @param a1 first scale factor
118      * @param pv1 first base (unscaled) PVCoordinates
119      * @param a2 second scale factor
120      * @param pv2 second base (unscaled) PVCoordinates
121      */
122     public TimeStampedPVCoordinates(final AbsoluteDate date,
123                                     final double a1, final PVCoordinates pv1,
124                                     final double a2, final PVCoordinates pv2) {
125         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition()),
126               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity()),
127               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration()));
128         this.date = date;
129     }
130 
131     /** Linear constructor
132      * <p>Build a TimeStampedPVCoordinates from three other ones and corresponding scale factors.</p>
133      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3</p>
134      * @param date date of the built coordinates
135      * @param a1 first scale factor
136      * @param pv1 first base (unscaled) PVCoordinates
137      * @param a2 second scale factor
138      * @param pv2 second base (unscaled) PVCoordinates
139      * @param a3 third scale factor
140      * @param pv3 third base (unscaled) PVCoordinates
141      */
142     public TimeStampedPVCoordinates(final AbsoluteDate date,
143                                     final double a1, final PVCoordinates pv1,
144                                     final double a2, final PVCoordinates pv2,
145                                     final double a3, final PVCoordinates pv3) {
146         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition(),     a3, pv3.getPosition()),
147               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity(),     a3, pv3.getVelocity()),
148               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration(), a3, pv3.getAcceleration()));
149         this.date = date;
150     }
151 
152     /** Linear constructor
153      * <p>Build a TimeStampedPVCoordinates from four other ones and corresponding scale factors.</p>
154      * <p>The TimeStampedPVCoordinates built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4</p>
155      * @param date date of the built coordinates
156      * @param a1 first scale factor
157      * @param pv1 first base (unscaled) PVCoordinates
158      * @param a2 second scale factor
159      * @param pv2 second base (unscaled) PVCoordinates
160      * @param a3 third scale factor
161      * @param pv3 third base (unscaled) PVCoordinates
162      * @param a4 fourth scale factor
163      * @param pv4 fourth base (unscaled) PVCoordinates
164      */
165     public TimeStampedPVCoordinates(final AbsoluteDate date,
166                                     final double a1, final PVCoordinates pv1,
167                                     final double a2, final PVCoordinates pv2,
168                                     final double a3, final PVCoordinates pv3,
169                                     final double a4, final PVCoordinates pv4) {
170         super(new Vector3D(a1, pv1.getPosition(),     a2, pv2.getPosition(),     a3, pv3.getPosition(),     a4, pv4.getPosition()),
171               new Vector3D(a1, pv1.getVelocity(),     a2, pv2.getVelocity(),     a3, pv3.getVelocity(),     a4, pv4.getVelocity()),
172               new Vector3D(a1, pv1.getAcceleration(), a2, pv2.getAcceleration(), a3, pv3.getAcceleration(), a4, pv4.getAcceleration()));
173         this.date = date;
174     }
175 
176     /** Builds a TimeStampedPVCoordinates triplet from  a {@link FieldVector3D}&lt;{@link Derivative}&gt;.
177      * <p>
178      * The vector components must have time as their only derivation parameter and
179      * have consistent derivation orders.
180      * </p>
181      * @param date date of the built coordinates
182      * @param p vector with time-derivatives embedded within the coordinates
183      * @param <U> type of the derivative
184      */
185     public <U extends Derivative<U>> TimeStampedPVCoordinates(final AbsoluteDate date, final FieldVector3D<U> p) {
186         super(p);
187         this.date = date;
188     }
189 
190     /** {@inheritDoc} */
191     public AbsoluteDate getDate() {
192         return date;
193     }
194 
195     /** Get a time-shifted state.
196      * <p>
197      * The state can be slightly shifted to close dates. This shift is based on
198      * a simple Taylor expansion. It is <em>not</em> intended as a replacement for
199      * proper orbit propagation (it is not even Keplerian!) but should be sufficient
200      * for either small time shifts or coarse accuracy.
201      * </p>
202      * @param dt time shift in seconds
203      * @return a new state, shifted with respect to the instance (which is immutable)
204      */
205     public TimeStampedPVCoordinates shiftedBy(final double dt) {
206         final PVCoordinates spv = super.shiftedBy(dt);
207         return new TimeStampedPVCoordinates(date.shiftedBy(dt),
208                                             spv.getPosition(), spv.getVelocity(), spv.getAcceleration());
209     }
210 
211     /** Get a time-shifted state.
212      * <p>
213      * The state can be slightly shifted to close dates. This shift is based on
214      * a simple Taylor expansion. It is <em>not</em> intended as a replacement for
215      * proper orbit propagation (it is not even Keplerian!) but should be sufficient
216      * for either small time shifts or coarse accuracy.
217      * </p>
218      * @param dt time shift
219      * @return a new state, shifted with respect to the instance (which is immutable)
220      * @since 13.0
221      */
222     public TimeStampedPVCoordinates shiftedBy(final TimeOffset dt) {
223         final PVCoordinates spv = super.shiftedBy(dt);
224         return new TimeStampedPVCoordinates(date.shiftedBy(dt),
225                                             spv.getPosition(), spv.getVelocity(), spv.getAcceleration());
226     }
227 
228     /** Create a local provider using simply Taylor expansion through {@link #shiftedBy(double)}.
229      * <p>
230      * The time evolution is based on a simple Taylor expansion. It is <em>not</em> intended as a
231      * replacement for proper orbit propagation (it is not even Keplerian!) but should be sufficient
232      * for either small time shifts or coarse accuracy.
233      * </p>
234      * @param instanceFrame frame in which the instance is defined
235      * @return provider based on Taylor expansion, for small time shifts around instance date
236      */
237     public PVCoordinatesProvider toTaylorProvider(final Frame instanceFrame) {
238         return new PVCoordinatesProvider() {
239             /** {@inheritDoc} */
240             public Vector3D getPosition(final AbsoluteDate d,  final Frame f) {
241                 final TimeStampedPVCoordinates shifted   = shiftedBy(d.durationFrom(getDate()));
242                 final StaticTransform          transform = instanceFrame.getStaticTransformTo(f, d);
243                 return transform.transformPosition(shifted.getPosition());
244             }
245             /** {@inheritDoc} */
246             public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d,  final Frame f) {
247                 final TimeStampedPVCoordinates shifted   = shiftedBy(d.durationFrom(date));
248                 final Transform                transform = instanceFrame.getTransformTo(f, d);
249                 return transform.transformPVCoordinates(shifted);
250             }
251         };
252     }
253 
254     /** Return a string representation of this date, position, velocity, and acceleration.
255      *
256      * <p>This method uses the {@link DataContext#getDefault() default data context}.
257      *
258      * @return string representation of this.
259      */
260     @Override
261     @DefaultDataContext
262     public String toString() {
263         return toString(DataContext.getDefault().getTimeScales().getUTC());
264     }
265 
266     /**
267      * Return a string representation of this date, position, velocity, and acceleration.
268      *
269      * @param utc time scale used to print the date.
270      * @return string representation of this.
271      */
272     public String toString(final TimeScale utc) {
273         final String comma = ", ";
274         return new StringBuilder().append('{').
275                                   append(date.toString(utc)).append(", P(").
276                                   append(getPosition().getX()).append(comma).
277                                   append(getPosition().getY()).append(comma).
278                                   append(getPosition().getZ()).append("), V(").
279                                   append(getVelocity().getX()).append(comma).
280                                   append(getVelocity().getY()).append(comma).
281                                   append(getVelocity().getZ()).append("), A(").
282                                   append(getAcceleration().getX()).append(comma).
283                                   append(getAcceleration().getY()).append(comma).
284                                   append(getAcceleration().getZ()).append(")}").toString();
285     }
286 
287     /** Replace the instance with a data transfer object for serialization.
288      * @return data transfer object that will be serialized
289      */
290     @DefaultDataContext
291     private Object writeReplace() {
292         return new DTO(this);
293     }
294 
295     /** Internal class used only for serialization. */
296     @DefaultDataContext
297     private static class DTO implements Serializable {
298 
299         /** Serializable UID. */
300         private static final long serialVersionUID = 20140723L;
301 
302         /** Double values. */
303         private final double[] d;
304 
305         /** Simple constructor.
306          * @param pv instance to serialize
307          */
308         private DTO(final TimeStampedPVCoordinates pv) {
309 
310             // decompose date
311             final AbsoluteDate j2000Epoch =
312                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
313             final double epoch  = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
314             final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));
315 
316             this.d = new double[] {
317                 epoch, offset,
318                 pv.getPosition().getX(),     pv.getPosition().getY(),     pv.getPosition().getZ(),
319                 pv.getVelocity().getX(),     pv.getVelocity().getY(),     pv.getVelocity().getZ(),
320                 pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ()
321             };
322 
323         }
324 
325         /** Replace the deserialized data transfer object with a {@link TimeStampedPVCoordinates}.
326          * @return replacement {@link TimeStampedPVCoordinates}
327          */
328         private Object readResolve() {
329             final AbsoluteDate j2000Epoch =
330                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
331             return new TimeStampedPVCoordinates(j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
332                                                 new Vector3D(d[2], d[3], d[ 4]),
333                                                 new Vector3D(d[5], d[6], d[ 7]),
334                                                 new Vector3D(d[8], d[9], d[10]));
335         }
336 
337     }
338 
339 }