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