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.propagation;
18  
19  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.Field;
22  import org.hipparchus.exception.LocalizedCoreFormats;
23  import org.hipparchus.exception.MathIllegalArgumentException;
24  import org.hipparchus.exception.MathIllegalStateException;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.util.FastMath;
27  import org.orekit.attitudes.FieldAttitude;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.errors.OrekitIllegalArgumentException;
30  import org.orekit.errors.OrekitIllegalStateException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.frames.FieldStaticTransform;
33  import org.orekit.frames.FieldTransform;
34  import org.orekit.frames.Frame;
35  import org.orekit.orbits.FieldOrbit;
36  import org.orekit.orbits.Orbit;
37  import org.orekit.time.FieldAbsoluteDate;
38  import org.orekit.time.FieldTimeShiftable;
39  import org.orekit.time.FieldTimeStamped;
40  import org.orekit.utils.DoubleArrayDictionary;
41  import org.orekit.utils.FieldArrayDictionary;
42  import org.orekit.utils.FieldAbsolutePVCoordinates;
43  import org.orekit.utils.FieldPVCoordinates;
44  import org.orekit.utils.TimeStampedFieldPVCoordinates;
45  import org.orekit.utils.TimeStampedPVCoordinates;
46  
47  /** This class is the representation of a complete state holding orbit, attitude
48   * and mass information at a given date, meant primarily for propagation.
49   *
50   * <p>It contains an {@link FieldOrbit}, or a {@link FieldAbsolutePVCoordinates} if there
51   * is no definite central body, plus the current mass and attitude at the intrinsic
52   * {@link FieldAbsoluteDate}. Quantities are guaranteed to be consistent in terms
53   * of date and reference frame. The spacecraft state may also contain additional
54   * states, which are simply named double arrays which can hold any user-defined
55   * data.
56   * </p>
57   * <p>
58   * The state can be slightly shifted to close dates. This actual shift varies
59   * between {@link FieldOrbit} and {@link FieldAbsolutePVCoordinates}.
60   * For attitude it is a linear extrapolation taking the spin rate into account
61   * and no mass change. It is <em>not</em> intended as a replacement for proper
62   * orbit and attitude propagation but should be sufficient for either small
63   * time shifts or coarse accuracy.
64   * </p>
65   * <p>
66   * The instance {@code FieldSpacecraftState} is guaranteed to be immutable.
67   * </p>
68   * @see org.orekit.propagation.numerical.NumericalPropagator
69   * @see SpacecraftState
70   * @author Fabien Maussion
71   * @author V&eacute;ronique Pommier-Maurussane
72   * @author Luc Maisonobe
73   * @author Vincent Mouraux
74   * @param <T> type of the field elements
75   */
76  public class FieldSpacecraftState <T extends CalculusFieldElement<T>>
77      implements FieldTimeStamped<T>, FieldTimeShiftable<FieldSpacecraftState<T>, T> {
78  
79      /** Default mass. */
80      private static final double DEFAULT_MASS = 1000.0;
81  
82      /**
83       * tolerance on date comparison in {@link #checkConsistency(FieldOrbit, FieldAttitude)}. 100 ns
84       * corresponds to sub-mm accuracy at LEO orbital velocities.
85       */
86      private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9;
87  
88      /** Orbital state. */
89      private final FieldOrbit<T> orbit;
90  
91      /** Trajectory state, when it is not an orbit. */
92      private final FieldAbsolutePVCoordinates<T> absPva;
93  
94      /** FieldAttitude<T>. */
95      private final FieldAttitude<T> attitude;
96  
97      /** Current mass (kg). */
98      private final T mass;
99  
100     /** Additional states. */
101     private final FieldArrayDictionary<T> additional;
102 
103     /** Additional states derivatives.
104      * @since 11.1
105      */
106     private final FieldArrayDictionary<T> additionalDot;
107 
108     /** Build a spacecraft state from orbit only.
109      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
110      * @param orbit the orbit
111      */
112     public FieldSpacecraftState(final FieldOrbit<T> orbit) {
113         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
114                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
115              orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
116     }
117 
118     /** Build a spacecraft state from orbit and attitude.
119      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
120      * @param orbit the orbit
121      * @param attitude attitude
122      * @exception IllegalArgumentException if orbit and attitude dates
123      * or frames are not equal
124      */
125     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude)
126         throws IllegalArgumentException {
127         this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
128     }
129 
130     /** Create a new instance from orbit and mass.
131      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
132      * @param orbit the orbit
133      * @param mass the mass (kg)
134      */
135     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass) {
136         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
137                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
138              mass, (FieldArrayDictionary<T>) null);
139     }
140 
141     /** Build a spacecraft state from orbit, attitude and mass.
142      * @param orbit the orbit
143      * @param attitude attitude
144      * @param mass the mass (kg)
145      * @exception IllegalArgumentException if orbit and attitude dates
146      * or frames are not equal
147      */
148     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass)
149         throws IllegalArgumentException {
150         this(orbit, attitude, mass, (FieldArrayDictionary<T>) null);
151     }
152 
153     /** Build a spacecraft state from orbit and additional states.
154      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
155      * @param orbit the orbit
156      * @param additional additional states
157      * @since 11.1
158      */
159     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldArrayDictionary<T> additional) {
160         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
161                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
162              orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional);
163     }
164 
165     /** Build a spacecraft state from orbit attitude and additional states.
166      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
167      * @param orbit the orbit
168      * @param attitude attitude
169      * @param additional additional states
170      * @exception IllegalArgumentException if orbit and attitude dates
171      * or frames are not equal
172      * @since 11.1
173      */
174     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final FieldArrayDictionary<T> additional)
175         throws IllegalArgumentException {
176         this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional);
177     }
178 
179     /** Create a new instance from orbit, mass and additional states.
180      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
181      * @param orbit the orbit
182      * @param mass the mass (kg)
183      * @param additional additional states
184      * @since 11.1
185      */
186     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass, final FieldArrayDictionary<T> additional) {
187         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
188                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
189              mass, additional);
190     }
191 
192     /** Build a spacecraft state from orbit, attitude, mass and additional states.
193      * @param orbit the orbit
194      * @param attitude attitude
195      * @param mass the mass (kg)
196      * @param additional additional states (may be null if no additional states are available)
197      * @since 11.1
198      */
199     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude,
200                                 final T mass, final FieldArrayDictionary<T> additional) {
201         this(orbit, attitude, mass, additional, null);
202     }
203 
204     /** Build a spacecraft state from orbit, attitude, mass, additional states and derivatives.
205      * @param orbit the orbit
206      * @param attitude attitude
207      * @param mass the mass (kg)
208      * @param additional additional states (may be null if no additional states are available)
209      * @param additionalDot additional states derivatives(may be null if no additional states derivative sare available)
210      * @exception IllegalArgumentException if orbit and attitude dates
211      * or frames are not equal
212      * @since 11.1
213      */
214     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass,
215                                 final FieldArrayDictionary<T> additional,
216                                 final FieldArrayDictionary<T> additionalDot)
217         throws IllegalArgumentException {
218         checkConsistency(orbit, attitude);
219         this.orbit      = orbit;
220         this.attitude   = attitude;
221         this.mass       = mass;
222         this.absPva     = null;
223 
224         if (additional == null) {
225             this.additional = new FieldArrayDictionary<>(orbit.getDate().getField());
226         } else {
227             this.additional = new FieldArrayDictionary<>(additional);
228         }
229 
230         if (additionalDot == null) {
231             this.additionalDot = new FieldArrayDictionary<>(orbit.getDate().getField());
232         } else {
233 
234             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
235         }
236 
237     }
238 
239     /** Convert a {@link FieldSpacecraftState}.
240      * @param field field to which the elements belong
241      * @param state state to convert
242      */
243     public FieldSpacecraftState(final Field<T> field, final SpacecraftState state) {
244 
245         if (state.isOrbitDefined()) {
246 
247             final Orbit nonFieldOrbit = state.getOrbit();
248             this.orbit    = nonFieldOrbit.getType().convertToFieldOrbit(field, nonFieldOrbit);
249             this.absPva   = null;
250 
251         } else {
252             final TimeStampedPVCoordinates tspva = state.getPVCoordinates();
253             final FieldVector3D<T> position = new FieldVector3D<>(field, tspva.getPosition());
254             final FieldVector3D<T> velocity = new FieldVector3D<>(field, tspva.getVelocity());
255             final FieldVector3D<T> acceleration = new FieldVector3D<>(field, tspva.getAcceleration());
256             final FieldPVCoordinates<T> pva = new FieldPVCoordinates<>(position, velocity, acceleration);
257             final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, state.getDate());
258             this.orbit  = null;
259             this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva);
260         }
261 
262         this.attitude = new FieldAttitude<>(field, state.getAttitude());
263         this.mass     = field.getZero().newInstance(state.getMass());
264 
265         final DoubleArrayDictionary additionalD = state.getAdditionalStatesValues();
266         if (additionalD.size() == 0) {
267             this.additional = new FieldArrayDictionary<>(field);
268         } else {
269             this.additional = new FieldArrayDictionary<>(field, additionalD.size());
270             for (final DoubleArrayDictionary.Entry entry : additionalD.getData()) {
271                 this.additional.put(entry.getKey(), entry.getValue());
272             }
273         }
274         final DoubleArrayDictionary additionalDotD = state.getAdditionalStatesDerivatives();
275         if (additionalDotD.size() == 0) {
276             this.additionalDot = new FieldArrayDictionary<>(field);
277         } else {
278             this.additionalDot = new FieldArrayDictionary<>(field, additionalDotD.size());
279             for (final DoubleArrayDictionary.Entry entry : additionalDotD.getData()) {
280                 this.additionalDot.put(entry.getKey(), entry.getValue());
281             }
282         }
283 
284     }
285 
286     /** Build a spacecraft state from orbit only.
287      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
288      * @param absPva position-velocity-acceleration
289      */
290     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva) {
291         this(absPva,
292              SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()).
293                      getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
294              absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
295     }
296 
297     /** Build a spacecraft state from orbit and attitude.
298      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
299      * @param absPva position-velocity-acceleration
300      * @param attitude attitude
301      * @exception IllegalArgumentException if orbit and attitude dates
302      * or frames are not equal
303      */
304     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
305         throws IllegalArgumentException {
306         this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
307     }
308 
309     /** Create a new instance from orbit and mass.
310      * <p>Attitude law is set to an unspecified default attitude.</p>
311      * @param absPva position-velocity-acceleration
312      * @param mass the mass (kg)
313      */
314     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass) {
315         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
316                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
317              mass, (FieldArrayDictionary<T>) null);
318     }
319 
320     /** Build a spacecraft state from orbit, attitude and mass.
321      * @param absPva position-velocity-acceleration
322      * @param attitude attitude
323      * @param mass the mass (kg)
324      * @exception IllegalArgumentException if orbit and attitude dates
325      * or frames are not equal
326      */
327     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass)
328         throws IllegalArgumentException {
329         this(absPva, attitude, mass, (FieldArrayDictionary<T>) null);
330     }
331 
332     /** Build a spacecraft state from orbit only.
333      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
334      * @param absPva position-velocity-acceleration
335      * @param additional additional states
336      * @since 11.1
337      */
338     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldArrayDictionary<T> additional) {
339         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
340                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
341              absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional);
342     }
343 
344     /** Build a spacecraft state from orbit and attitude.
345      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
346      * @param absPva position-velocity-acceleration
347      * @param attitude attitude
348      * @param additional additional states
349      * @exception IllegalArgumentException if orbit and attitude dates
350      * or frames are not equal
351      * @since 11.1
352      */
353     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
354                                 final FieldArrayDictionary<T> additional)
355         throws IllegalArgumentException {
356         this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional);
357     }
358 
359     /** Create a new instance from orbit and mass.
360      * <p>Attitude law is set to an unspecified default attitude.</p>
361      * @param absPva position-velocity-acceleration
362      * @param mass the mass (kg)
363      * @param additional additional states
364      * @since 11.1
365      */
366     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass, final FieldArrayDictionary<T> additional) {
367         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
368                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
369              mass, additional);
370     }
371 
372     /** Build a spacecraft state from orbit, attitude and mass.
373      * @param absPva position-velocity-acceleration
374      * @param attitude attitude
375      * @param mass the mass (kg)
376      * @param additional additional states (may be null if no additional states are available)
377      * @since 11.1
378      */
379     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
380                            final T mass, final FieldArrayDictionary<T> additional) {
381         this(absPva, attitude, mass, additional, null);
382     }
383 
384     /** Build a spacecraft state from orbit, attitude and mass.
385      * @param absPva position-velocity-acceleration
386      * @param attitude attitude
387      * @param mass the mass (kg)
388      * @param additional additional states (may be null if no additional states are available)
389      * @param additionalDot additional states derivatives(may be null if no additional states derivatives are available)
390      * @exception IllegalArgumentException if orbit and attitude dates
391      * or frames are not equal
392      * @since 11.1
393      */
394     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass,
395                                 final FieldArrayDictionary<T> additional, final FieldArrayDictionary<T> additionalDot)
396         throws IllegalArgumentException {
397         checkConsistency(absPva, attitude);
398         this.orbit      = null;
399         this.absPva     = absPva;
400         this.attitude   = attitude;
401         this.mass       = mass;
402         if (additional == null) {
403             this.additional = new FieldArrayDictionary<>(absPva.getDate().getField());
404         } else {
405             this.additional = new FieldArrayDictionary<>(additional);
406         }
407         if (additionalDot == null) {
408             this.additionalDot = new FieldArrayDictionary<>(absPva.getDate().getField());
409         } else {
410             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
411         }
412     }
413 
414     /** Add an additional state.
415      * <p>
416      * {@link FieldSpacecraftState SpacecraftState} instances are immutable,
417      * so this method does <em>not</em> change the instance, but rather
418      * creates a new instance, which has the same orbit, attitude, mass
419      * and additional states as the original instance, except it also
420      * has the specified state. If the original instance already had an
421      * additional state with the same name, it will be overridden. If it
422      * did not have any additional state with that name, the new instance
423      * will have one more additional state than the original instance.
424      * </p>
425      * @param name name of the additional state
426      * @param value value of the additional state
427      * @return a new instance, with the additional state added
428      * @see #hasAdditionalState(String)
429      * @see #getAdditionalState(String)
430      * @see #getAdditionalStatesValues()
431      */
432     @SafeVarargs
433     public final FieldSpacecraftState<T> addAdditionalState(final String name, final T... value) {
434         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additional);
435         newDict.put(name, value.clone());
436         if (isOrbitDefined()) {
437             return new FieldSpacecraftState<>(orbit, attitude, mass, newDict, additionalDot);
438         } else {
439             return new FieldSpacecraftState<>(absPva, attitude, mass, newDict, additionalDot);
440         }
441     }
442 
443     /** Add an additional state derivative.
444     * {@link FieldSpacecraftState FieldSpacecraftState} instances are immutable,
445      * so this method does <em>not</em> change the instance, but rather
446      * creates a new instance, which has the same components as the original
447      * instance, except it also has the specified state derivative. If the
448      * original instance already had an additional state derivative with the
449      * same name, it will be overridden. If it did not have any additional
450      * state derivative with that name, the new instance will have one more
451      * additional state derivative than the original instance.
452      * @param name name of the additional state derivative
453      * @param value value of the additional state derivative
454      * @return a new instance, with the additional state derivative added
455      * @see #hasAdditionalStateDerivative(String)
456      * @see #getAdditionalStateDerivative(String)
457      * @see #getAdditionalStatesDerivatives()
458      */
459     @SafeVarargs
460     public final FieldSpacecraftState<T> addAdditionalStateDerivative(final String name, final T... value) {
461         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additionalDot);
462         newDict.put(name, value.clone());
463         if (isOrbitDefined()) {
464             return new FieldSpacecraftState<>(orbit, attitude, mass, additional, newDict);
465         } else {
466             return new FieldSpacecraftState<>(absPva, attitude, mass, additional, newDict);
467         }
468     }
469 
470     /** Check orbit and attitude dates are equal.
471      * @param orbitN the orbit
472      * @param attitudeN attitude
473      * @exception IllegalArgumentException if orbit and attitude dates
474      * are not equal
475      */
476     private void checkConsistency(final FieldOrbit<T> orbitN, final FieldAttitude<T> attitudeN)
477         throws IllegalArgumentException {
478         if (orbitN.getDate().durationFrom(attitudeN.getDate()).abs().getReal() >
479             DATE_INCONSISTENCY_THRESHOLD) {
480 
481             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
482                                                      orbitN.getDate(), attitudeN.getDate());
483         }
484 
485         if (orbitN.getFrame() != attitudeN.getReferenceFrame()) {
486             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
487                                                      orbitN.getFrame().getName(),
488                                                      attitudeN.getReferenceFrame().getName());
489         }
490     }
491 
492     /** Check if the state contains an orbit part.
493      * <p>
494      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
495      * position-velocity-acceleration} or an {@link FieldOrbit orbit}.
496      * </p>
497      * @return true if state contains an orbit (in which case {@link #getOrbit()}
498      * will not throw an exception), or false if the state contains an
499      * absolut position-velocity-acceleration (in which case {@link #getAbsPVA()}
500      * will not throw an exception)
501      */
502     public boolean isOrbitDefined() {
503         return orbit != null;
504     }
505 
506     /**
507      * Check FieldAbsolutePVCoordinates and attitude dates are equal.
508      * @param absPva   position-velocity-acceleration
509      * @param attitude attitude
510      * @param <T>      the type of the field elements
511      * @exception IllegalArgumentException if orbit and attitude dates are not equal
512      */
513     private static <T extends CalculusFieldElement<T>> void checkConsistency(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
514         throws IllegalArgumentException {
515         if (FastMath.abs(absPva.getDate().durationFrom(attitude.getDate())).getReal() >
516             DATE_INCONSISTENCY_THRESHOLD) {
517             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
518                                                      absPva.getDate(), attitude.getDate());
519         }
520         if (absPva.getFrame() != attitude.getReferenceFrame()) {
521             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
522                                                      absPva.getFrame().getName(),
523                                                      attitude.getReferenceFrame().getName());
524         }
525     }
526 
527     /** Get a time-shifted state.
528      * <p>
529      * The state can be slightly shifted to close dates. This shift is based on
530      * a simple Keplerian model for orbit, a linear extrapolation for attitude
531      * taking the spin rate into account and neither mass nor additional states
532      * changes. It is <em>not</em> intended as a replacement for proper orbit
533      * and attitude propagation but should be sufficient for small time shifts
534      * or coarse accuracy.
535      * </p>
536      * <p>
537      * As a rough order of magnitude, the following table shows the extrapolation
538      * errors obtained between this simple shift method and an {@link
539      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
540      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
541      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
542      * Beware that these results will be different for other orbits.
543      * </p>
544      * <table border="1">
545      * <caption>Extrapolation Error</caption>
546      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
547      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
548      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
549      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
550      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
551      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
552      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
553      * </table>
554      * @param dt time shift in seconds
555      * @return a new state, shifted with respect to the instance (which is immutable)
556      * except for the mass which stay unchanged
557      */
558     @Override
559     public FieldSpacecraftState<T> shiftedBy(final double dt) {
560         if (isOrbitDefined()) {
561             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
562                                               mass, shiftAdditional(dt), additionalDot);
563         } else {
564             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
565                                               mass, shiftAdditional(dt), additionalDot);
566         }
567     }
568 
569     /** Get a time-shifted state.
570      * <p>
571      * The state can be slightly shifted to close dates. This shift is based on
572      * a simple Keplerian model for orbit, a linear extrapolation for attitude
573      * taking the spin rate into account and neither mass nor additional states
574      * changes. It is <em>not</em> intended as a replacement for proper orbit
575      * and attitude propagation but should be sufficient for small time shifts
576      * or coarse accuracy.
577      * </p>
578      * <p>
579      * As a rough order of magnitude, the following table shows the extrapolation
580      * errors obtained between this simple shift method and an {@link
581      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
582      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
583      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
584      * Beware that these results will be different for other orbits.
585      * </p>
586      * <table border="1">
587      * <caption>Extrapolation Error</caption>
588      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
589      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
590      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
591      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
592      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
593      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
594      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
595      * </table>
596      * @param dt time shift in seconds
597      * @return a new state, shifted with respect to the instance (which is immutable)
598      * except for the mass which stay unchanged
599      */
600     @Override
601     public FieldSpacecraftState<T> shiftedBy(final T dt) {
602         if (isOrbitDefined()) {
603             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
604                                               mass, shiftAdditional(dt), additionalDot);
605         } else {
606             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
607                                               mass, shiftAdditional(dt), additionalDot);
608         }
609     }
610 
611     /** Shift additional states.
612      * @param dt time shift in seconds
613      * @return shifted additional states
614      * @since 11.1.1
615      */
616     private FieldArrayDictionary<T> shiftAdditional(final double dt) {
617 
618         // fast handling when there are no derivatives at all
619         if (additionalDot.size() == 0) {
620             return additional;
621         }
622 
623         // there are derivatives, we need to take them into account in the additional state
624         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<>(additional);
625         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
626             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
627             if (entry != null) {
628                 entry.scaledIncrement(dt, dotEntry);
629             }
630         }
631 
632         return shifted;
633 
634     }
635 
636     /** Shift additional states.
637      * @param dt time shift in seconds
638      * @return shifted additional states
639      * @since 11.1.1
640      */
641     private FieldArrayDictionary<T> shiftAdditional(final T dt) {
642 
643         // fast handling when there are no derivatives at all
644         if (additionalDot.size() == 0) {
645             return additional;
646         }
647 
648         // there are derivatives, we need to take them into account in the additional state
649         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<>(additional);
650         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
651             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
652             if (entry != null) {
653                 entry.scaledIncrement(dt, dotEntry);
654             }
655         }
656 
657         return shifted;
658 
659     }
660 
661     /** Get the absolute position-velocity-acceleration.
662      * <p>
663      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
664      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
665      * one is present can be checked using {@link #isOrbitDefined()}.
666      * </p>
667      * @return absolute position-velocity-acceleration
668      * @exception OrekitIllegalStateException if position-velocity-acceleration is null,
669      * which mean the state rather contains an {@link FieldOrbit}
670      * @see #isOrbitDefined()
671      * @see #getOrbit()
672      */
673     public FieldAbsolutePVCoordinates<T> getAbsPVA() throws OrekitIllegalStateException {
674         if (isOrbitDefined()) {
675             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES);
676         }
677         return absPva;
678     }
679 
680     /** Get the current orbit.
681      * <p>
682      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
683      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
684      * one is present can be checked using {@link #isOrbitDefined()}.
685      * </p>
686      * @return the orbit
687      * @exception OrekitIllegalStateException if orbit is null,
688      * which means the state rather contains an {@link FieldAbsolutePVCoordinates absolute
689      * position-velocity-acceleration}
690      * @see #isOrbitDefined()
691      * @see #getAbsPVA()
692      */
693     public FieldOrbit<T> getOrbit() throws OrekitIllegalStateException {
694         if (orbit == null) {
695             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ORBIT);
696         }
697         return orbit;
698     }
699 
700     /** {@inheritDoc} */
701     @Override
702     public FieldAbsoluteDate<T> getDate() {
703         return isOrbitDefined() ? orbit.getDate() : absPva.getDate();
704     }
705 
706     /** Get the defining frame.
707      * @return the frame in which state is defined
708      */
709     public Frame getFrame() {
710         return isOrbitDefined() ? orbit.getFrame() : absPva.getFrame();
711     }
712 
713 
714     /** Check if an additional state is available.
715      * @param name name of the additional state
716      * @return true if the additional state is available
717      * @see #addAdditionalState(String, CalculusFieldElement...)
718      * @see #getAdditionalState(String)
719      * @see #getAdditionalStatesValues()
720      */
721     public boolean hasAdditionalState(final String name) {
722         return additional.getEntry(name) != null;
723     }
724 
725     /** Check if an additional state derivative is available.
726      * @param name name of the additional state derivative
727      * @return true if the additional state derivative is available
728      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
729      * @see #getAdditionalStateDerivative(String)
730      * @see #getAdditionalStatesDerivatives()
731      */
732     public boolean hasAdditionalStateDerivative(final String name) {
733         return additionalDot.getEntry(name) != null;
734     }
735 
736     /** Check if two instances have the same set of additional states available.
737      * <p>
738      * Only the names and dimensions of the additional states are compared,
739      * not their values.
740      * </p>
741      * @param state state to compare to instance
742      * @exception MathIllegalArgumentException if an additional state does not have
743      * the same dimension in both states
744      */
745     public void ensureCompatibleAdditionalStates(final FieldSpacecraftState<T> state)
746         throws MathIllegalArgumentException {
747 
748         // check instance additional states is a subset of the other one
749         for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
750             final T[] other = state.additional.get(entry.getKey());
751             if (other == null) {
752                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
753                                           entry.getKey());
754             }
755             if (other.length != entry.getValue().length) {
756                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
757                                                     other.length, entry.getValue().length);
758             }
759         }
760 
761         // check instance additional states derivatives is a subset of the other one
762         for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
763             final T[] other = state.additionalDot.get(entry.getKey());
764             if (other == null) {
765                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
766                                           entry.getKey());
767             }
768             if (other.length != entry.getValue().length) {
769                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
770                                                     other.length, entry.getValue().length);
771             }
772         }
773 
774         if (state.additional.size() > additional.size()) {
775             // the other state has more additional states
776             for (final FieldArrayDictionary<T>.Entry entry : state.additional.getData()) {
777                 if (additional.getEntry(entry.getKey()) == null) {
778                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
779                                               entry.getKey());
780                 }
781             }
782         }
783 
784         if (state.additionalDot.size() > additionalDot.size()) {
785             // the other state has more additional states
786             for (final FieldArrayDictionary<T>.Entry entry : state.additionalDot.getData()) {
787                 if (additionalDot.getEntry(entry.getKey()) == null) {
788                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
789                                               entry.getKey());
790                 }
791             }
792         }
793 
794     }
795 
796     /** Get an additional state.
797      * @param name name of the additional state
798      * @return value of the additional state
799           * @see #addAdditionalState(String, CalculusFieldElement...)
800      * @see #hasAdditionalState(String)
801      * @see #getAdditionalStatesValues()
802      */
803     public T[] getAdditionalState(final String name) {
804         final FieldArrayDictionary<T>.Entry entry = additional.getEntry(name);
805         if (entry == null) {
806             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
807         }
808         return entry.getValue();
809     }
810 
811     /** Get an additional state derivative.
812      * @param name name of the additional state derivative
813      * @return value of the additional state derivative
814      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
815      * @see #hasAdditionalStateDerivative(String)
816      * @see #getAdditionalStatesDerivatives()
817      * @since 11.1
818      */
819     public T[] getAdditionalStateDerivative(final String name) {
820         final FieldArrayDictionary<T>.Entry entry = additionalDot.getEntry(name);
821         if (entry == null) {
822             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
823         }
824         return entry.getValue();
825     }
826 
827     /** Get an unmodifiable map of additional states.
828      * @return unmodifiable map of additional states
829      * @see #addAdditionalState(String, CalculusFieldElement...)
830      * @see #hasAdditionalState(String)
831      * @see #getAdditionalState(String)
832      * @since 11.1
833      */
834     public FieldArrayDictionary<T> getAdditionalStatesValues() {
835         return additional.unmodifiableView();
836     }
837 
838     /** Get an unmodifiable map of additional states derivatives.
839      * @return unmodifiable map of additional states derivatives
840      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
841      * @see #hasAdditionalStateDerivative(String)
842      * @see #getAdditionalStateDerivative(String)
843     * @since 11.1
844       */
845     public FieldArrayDictionary<T> getAdditionalStatesDerivatives() {
846         return additionalDot.unmodifiableView();
847     }
848 
849     /** Compute the transform from state defining frame to spacecraft frame.
850      * <p>The spacecraft frame origin is at the point defined by the orbit,
851      * and its orientation is defined by the attitude.</p>
852      * @return transform from specified frame to current spacecraft frame
853      */
854     public FieldTransform<T> toTransform() {
855         final TimeStampedFieldPVCoordinates<T> pv = getPVCoordinates();
856         return new FieldTransform<>(pv.getDate(),
857                                     new FieldTransform<>(pv.getDate(), pv.negate()),
858                                     new FieldTransform<>(pv.getDate(), attitude.getOrientation()));
859     }
860 
861     /** Compute the static transform from state defining frame to spacecraft frame.
862      * @return static transform from specified frame to current spacecraft frame
863      * @see #toTransform()
864      * @since 12.0
865      */
866     public FieldStaticTransform<T> toStaticTransform() {
867         return FieldStaticTransform.of(getDate(), getPosition().negate(), attitude.getRotation());
868     }
869 
870     /** Get the central attraction coefficient.
871      * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the
872      * state contains an absolute position-velocity-acceleration rather than an orbit
873      */
874     public T getMu() {
875         return isOrbitDefined() ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN);
876     }
877 
878     /** Get the Keplerian period.
879      * <p>The Keplerian period is computed directly from semi major axis
880      * and central acceleration constant.</p>
881      * @return Keplerian period in seconds, or {code Double.NaN} if the
882      * state contains an absolute position-velocity-acceleration rather
883      * than an orbit
884      */
885     public T getKeplerianPeriod() {
886         return isOrbitDefined() ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN);
887     }
888 
889     /** Get the Keplerian mean motion.
890      * <p>The Keplerian mean motion is computed directly from semi major axis
891      * and central acceleration constant.</p>
892      * @return Keplerian mean motion in radians per second, or {code Double.NaN} if the
893      * state contains an absolute position-velocity-acceleration rather
894      * than an orbit
895      */
896     public T getKeplerianMeanMotion() {
897         return isOrbitDefined() ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN);
898     }
899 
900     /** Get the semi-major axis.
901      * @return semi-major axis (m), or {code Double.NaN} if the
902      * state contains an absolute position-velocity-acceleration rather
903      * than an orbit
904      */
905     public T getA() {
906         return isOrbitDefined() ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN);
907     }
908 
909     /** Get the first component of the eccentricity vector (as per equinoctial parameters).
910      * @return e cos(ω + Ω), first component of eccentricity vector, or {code Double.NaN} if the
911      * state contains an absolute position-velocity-acceleration rather
912      * than an orbit
913      * @see #getE()
914      */
915     public T getEquinoctialEx() {
916         return isOrbitDefined() ? orbit.getEquinoctialEx() : absPva.getDate().getField().getZero().add(Double.NaN);
917     }
918 
919     /** Get the second component of the eccentricity vector (as per equinoctial parameters).
920      * @return e sin(ω + Ω), second component of the eccentricity vector, or {code Double.NaN} if the
921      * state contains an absolute position-velocity-acceleration rather
922      * than an orbit
923      * @see #getE()
924      */
925     public T getEquinoctialEy() {
926         return isOrbitDefined() ? orbit.getEquinoctialEy() : absPva.getDate().getField().getZero().add(Double.NaN);
927     }
928 
929     /** Get the first component of the inclination vector (as per equinoctial parameters).
930      * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the
931      * state contains an absolute position-velocity-acceleration rather
932      * than an orbit
933      * @see #getI()
934      */
935     public T getHx() {
936         return isOrbitDefined() ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN);
937     }
938 
939     /** Get the second component of the inclination vector (as per equinoctial parameters).
940      * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the
941      * state contains an absolute position-velocity-acceleration rather
942      * than an orbit
943      * @see #getI()
944      */
945     public T getHy() {
946         return isOrbitDefined() ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN);
947     }
948 
949     /** Get the true latitude argument (as per equinoctial parameters).
950      * @return v + ω + Ω true longitude argument (rad), or {code Double.NaN} if the
951      * state contains an absolute position-velocity-acceleration rather
952      * than an orbit
953      * @see #getLE()
954      * @see #getLM()
955      */
956     public T getLv() {
957         return isOrbitDefined() ? orbit.getLv() : absPva.getDate().getField().getZero().add(Double.NaN);
958     }
959 
960     /** Get the eccentric latitude argument (as per equinoctial parameters).
961      * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the
962      * state contains an absolute position-velocity-acceleration rather
963      * than an orbit
964      * @see #getLv()
965      * @see #getLM()
966      */
967     public T getLE() {
968         return isOrbitDefined() ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN);
969     }
970 
971     /** Get the mean longitude argument (as per equinoctial parameters).
972      * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the
973      * state contains an absolute position-velocity-acceleration rather
974      * than an orbit
975      * @see #getLv()
976      * @see #getLE()
977      */
978     public T getLM() {
979         return isOrbitDefined() ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN);
980     }
981 
982     // Additional orbital elements
983 
984     /** Get the eccentricity.
985      * @return eccentricity, or {code Double.NaN} if the
986      * state contains an absolute position-velocity-acceleration rather
987      * than an orbit
988      * @see #getEquinoctialEx()
989      * @see #getEquinoctialEy()
990      */
991     public T getE() {
992         return isOrbitDefined() ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN);
993     }
994 
995     /** Get the inclination.
996      * @return inclination (rad)
997      * @see #getHx()
998      * @see #getHy()
999      */
1000     public T getI() {
1001         return isOrbitDefined() ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN);
1002     }
1003 
1004     /** Get the position in orbit definition frame.
1005      * @return position in orbit definition frame
1006      * @since 12.0
1007      */
1008     public FieldVector3D<T> getPosition() {
1009         return isOrbitDefined() ? orbit.getPosition() : absPva.getPosition();
1010     }
1011 
1012     /** Get the {@link TimeStampedFieldPVCoordinates} in orbit definition frame.
1013      * <p>
1014      * Compute the position and velocity of the satellite. This method caches its
1015      * results, and recompute them only when the method is called with a new value
1016      * for mu. The result is provided as a reference to the internally cached
1017      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
1018      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
1019      * </p>
1020      * @return pvCoordinates in orbit definition frame
1021      */
1022     public TimeStampedFieldPVCoordinates<T> getPVCoordinates() {
1023         return isOrbitDefined() ? orbit.getPVCoordinates() : absPva.getPVCoordinates();
1024     }
1025 
1026     /** Get the position in given output frame.
1027      * @param outputFrame frame in which position should be defined
1028      * @return position in given output frame
1029      * @since 12.0
1030      * @see #getPVCoordinates(Frame)
1031      */
1032     public FieldVector3D<T> getPosition(final Frame outputFrame) {
1033         return isOrbitDefined() ? orbit.getPosition(outputFrame) : absPva.getPosition(outputFrame);
1034     }
1035 
1036     /** Get the {@link TimeStampedFieldPVCoordinates} in given output frame.
1037      * <p>
1038      * Compute the position and velocity of the satellite. This method caches its
1039      * results, and recompute them only when the method is called with a new value
1040      * for mu. The result is provided as a reference to the internally cached
1041      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
1042      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
1043      * </p>
1044      * @param outputFrame frame in which coordinates should be defined
1045      * @return pvCoordinates in orbit definition frame
1046      */
1047     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final Frame outputFrame) {
1048         return isOrbitDefined() ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame);
1049     }
1050 
1051     /** Get the attitude.
1052      * @return the attitude.
1053      */
1054     public FieldAttitude<T> getAttitude() {
1055         return attitude;
1056     }
1057 
1058     /** Gets the current mass.
1059      * @return the mass (kg)
1060      */
1061     public T getMass() {
1062         return mass;
1063     }
1064 
1065     /**To convert a FieldSpacecraftState instance into a SpacecraftState instance.
1066      *
1067      * @return SpacecraftState instance with the same properties
1068      */
1069     public SpacecraftState toSpacecraftState() {
1070         final DoubleArrayDictionary dictionary;
1071         if (additional.size() == 0) {
1072             dictionary = new DoubleArrayDictionary();
1073         } else {
1074             dictionary = new DoubleArrayDictionary(additional.size());
1075             for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
1076                 final double[] array = new double[entry.getValue().length];
1077                 for (int k = 0; k < array.length; ++k) {
1078                     array[k] = entry.getValue()[k].getReal();
1079                 }
1080                 dictionary.put(entry.getKey(), array);
1081             }
1082         }
1083         final DoubleArrayDictionary dictionaryDot;
1084         if (additionalDot.size() == 0) {
1085             dictionaryDot = new DoubleArrayDictionary();
1086         } else {
1087             dictionaryDot = new DoubleArrayDictionary(additionalDot.size());
1088             for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
1089                 final double[] array = new double[entry.getValue().length];
1090                 for (int k = 0; k < array.length; ++k) {
1091                     array[k] = entry.getValue()[k].getReal();
1092                 }
1093                 dictionaryDot.put(entry.getKey(), array);
1094             }
1095         }
1096         if (isOrbitDefined()) {
1097             return new SpacecraftState(orbit.toOrbit(), attitude.toAttitude(),
1098                                        mass.getReal(), dictionary, dictionaryDot);
1099         } else {
1100             return new SpacecraftState(absPva.toAbsolutePVCoordinates(),
1101                                        attitude.toAttitude(), mass.getReal(),
1102                                        dictionary, dictionaryDot);
1103         }
1104     }
1105 
1106     @Override
1107     public String toString() {
1108         return "FieldSpacecraftState{" +
1109                 "orbit=" + orbit +
1110                 ", attitude=" + attitude +
1111                 ", mass=" + mass +
1112                 ", additional=" + additional +
1113                 ", additionalDot=" + additionalDot +
1114                 '}';
1115     }
1116 
1117 }