1   /* Copyright 2002-2025 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  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.polynomials.PolynomialFunction;
22  import org.hipparchus.complex.Complex;
23  import org.hipparchus.complex.ComplexField;
24  import org.hipparchus.exception.LocalizedCoreFormats;
25  import org.hipparchus.exception.MathIllegalStateException;
26  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
27  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28  import org.hipparchus.geometry.euclidean.threed.Rotation;
29  import org.hipparchus.geometry.euclidean.threed.Vector3D;
30  import org.hipparchus.ode.FieldODEIntegrator;
31  import org.hipparchus.ode.events.Action;
32  import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
33  import org.hipparchus.util.Binary64;
34  import org.hipparchus.util.Binary64Field;
35  import org.hipparchus.util.FastMath;
36  import org.hipparchus.util.MathArrays;
37  import org.junit.jupiter.api.Assertions;
38  import org.junit.jupiter.api.BeforeEach;
39  import org.junit.jupiter.api.Test;
40  import org.orekit.TestUtils;
41  import org.orekit.Utils;
42  import org.orekit.attitudes.BodyCenterPointing;
43  import org.orekit.attitudes.FieldAttitude;
44  import org.orekit.attitudes.FrameAlignedProvider;
45  import org.orekit.bodies.OneAxisEllipsoid;
46  import org.orekit.errors.OrekitException;
47  import org.orekit.errors.OrekitIllegalArgumentException;
48  import org.orekit.errors.OrekitMessages;
49  import org.orekit.frames.FieldStaticTransform;
50  import org.orekit.frames.FieldTransform;
51  import org.orekit.frames.FramesFactory;
52  import org.orekit.frames.Transform;
53  import org.orekit.orbits.FieldCartesianOrbit;
54  import org.orekit.orbits.FieldKeplerianOrbit;
55  import org.orekit.orbits.FieldOrbit;
56  import org.orekit.orbits.KeplerianOrbit;
57  import org.orekit.orbits.Orbit;
58  import org.orekit.orbits.PositionAngleType;
59  import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator;
60  import org.orekit.propagation.analytical.FieldKeplerianPropagator;
61  import org.orekit.propagation.events.FieldDateDetector;
62  import org.orekit.propagation.events.FieldEventDetector;
63  import org.orekit.propagation.events.handlers.FieldEventHandler;
64  import org.orekit.propagation.numerical.FieldNumericalPropagator;
65  import org.orekit.time.AbsoluteDate;
66  import org.orekit.time.DateComponents;
67  import org.orekit.time.FieldAbsoluteDate;
68  import org.orekit.time.TimeComponents;
69  import org.orekit.time.TimeScalesFactory;
70  import org.orekit.utils.AbsolutePVCoordinates;
71  import org.orekit.utils.Constants;
72  import org.orekit.utils.FieldAbsolutePVCoordinates;
73  import org.orekit.utils.FieldAngularCoordinates;
74  import org.orekit.utils.FieldArrayDictionary;
75  import org.orekit.utils.FieldDataDictionary;
76  import org.orekit.utils.FieldPVCoordinates;
77  import org.orekit.utils.IERSConventions;
78  import org.orekit.utils.PVCoordinates;
79  import org.orekit.utils.TimeStampedAngularCoordinates;
80  import org.orekit.utils.TimeStampedFieldAngularCoordinates;
81  
82  
83  class FieldSpacecraftStateTest {
84  
85      @Test
86      public void testToFieldSpacecraftStateWithStringAdditionalData() {
87          final Binary64Field field = Binary64Field.getInstance();
88          final SpacecraftState state = new SpacecraftState(TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH)).addAdditionalData("test-string", "hello");
89          final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
90          Assertions.assertTrue(fieldState.hasAdditionalData("test-string"));
91          Assertions.assertEquals("hello", fieldState.getAdditionalData("test-string"));
92      }
93  
94      @Test
95      void testWithAttitudeAndOrbit() {
96          // GIVEN
97          final SpacecraftState state = new SpacecraftState(TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH));
98          final Binary64Field field = Binary64Field.getInstance();
99          final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
100         final FieldAttitude<Binary64> attitude = new FieldAttitude<>(fieldState.getDate(), state.getFrame(),
101                 new FieldAngularCoordinates<>(FieldRotation.getIdentity(field), FieldVector3D.getMinusI(field)));
102         // WHEN
103         final FieldSpacecraftState<Binary64> fieldStateWithAttitude = fieldState.withAttitude(attitude);
104         // THEN
105         Assertions.assertEquals(attitude, fieldStateWithAttitude.getAttitude());
106         Assertions.assertEquals(fieldState.getMass(), fieldStateWithAttitude.getMass());
107         Assertions.assertEquals(fieldState.getOrbit(), fieldStateWithAttitude.getOrbit());
108     }
109 
110     @Test
111     void testWithMassAndOrbit() {
112         // GIVEN
113         final SpacecraftState state = new SpacecraftState(TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH));
114         final Binary64Field field = Binary64Field.getInstance();
115         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
116         final Binary64 expectedMass = new Binary64(123);
117         // WHEN
118         final FieldSpacecraftState<Binary64> fieldStateWithMass = fieldState.withMass(expectedMass);
119         // THEN
120         Assertions.assertEquals(expectedMass, fieldStateWithMass.getMass());
121         Assertions.assertEquals(fieldState.getAttitude(), fieldStateWithMass.getAttitude());
122         Assertions.assertEquals(fieldState.getOrbit(), fieldStateWithMass.getOrbit());
123     }
124 
125     @Test
126     void testWithAttitudeAndAbsolutePV() {
127         // GIVEN
128         final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(FramesFactory.getEME2000(),
129                 AbsoluteDate.ARBITRARY_EPOCH, new PVCoordinates());
130         final SpacecraftState state = new SpacecraftState(absolutePVCoordinates);
131         final Binary64Field field = Binary64Field.getInstance();
132         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
133         final FieldAttitude<Binary64> attitude = new FieldAttitude<>(fieldState.getDate(), state.getFrame(),
134                 new FieldAngularCoordinates<>(FieldRotation.getIdentity(field), FieldVector3D.getMinusI(field)));
135         // WHEN
136         final FieldSpacecraftState<Binary64> fieldStateWithAttitude = fieldState.withAttitude(attitude);
137         // THEN
138         Assertions.assertEquals(attitude, fieldStateWithAttitude.getAttitude());
139         Assertions.assertEquals(fieldState.getMass(), fieldStateWithAttitude.getMass());
140         Assertions.assertEquals(fieldState.getAbsPVA(), fieldStateWithAttitude.getAbsPVA());
141     }
142 
143     @Test
144     void testWithMassAndAbsolutePV() {
145         // GIVEN
146         final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(FramesFactory.getEME2000(),
147                 AbsoluteDate.ARBITRARY_EPOCH, new PVCoordinates());
148         final SpacecraftState state = new SpacecraftState(absolutePVCoordinates);
149         final Binary64Field field = Binary64Field.getInstance();
150         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
151         final Binary64 expectedMass = new Binary64(123);
152         // WHEN
153         final FieldSpacecraftState<Binary64> fieldStateWithMass = fieldState.withMass(expectedMass);
154         // THEN
155         Assertions.assertEquals(expectedMass, fieldStateWithMass.getMass());
156         Assertions.assertEquals(fieldState.getAttitude(), fieldStateWithMass.getAttitude());
157         Assertions.assertEquals(fieldState.getAbsPVA(), fieldStateWithMass.getAbsPVA());
158     }
159 
160     @Test
161     void testFieldVSReal() {
162         doTestFieldVsReal(Binary64Field.getInstance());
163     }
164 
165     @Test
166     void testShiftVsEcksteinHechlerError() {
167         doTestShiftVsEcksteinHechlerError(Binary64Field.getInstance());
168     }
169 
170     @Test
171     void testDatesConsistency() {
172         Assertions.assertThrows(IllegalArgumentException.class, () -> doTestDatesConsistency(Binary64Field.getInstance()));
173     }
174 
175     @Test
176     void testDateConsistencyClose() {
177         doTestDateConsistencyClose(Binary64Field.getInstance());
178     }
179 
180     @Test
181     void testFramesConsistency() {
182         Assertions.assertThrows(IllegalArgumentException.class, () -> doTestFramesConsistency(Binary64Field.getInstance()));
183     }
184 
185     @Test
186     void testGetAdditionalStateBadType() {
187         doTestGetAdditionalStateBadType(Binary64Field.getInstance());
188     }
189 
190     @Test
191     void testTransform() {
192         doTestTransform(Binary64Field.getInstance());
193     }
194 
195     @Test
196     void testAdditionalStates() {
197         doTestAdditionalStates(Binary64Field.getInstance());
198     }
199 
200     @Test
201     void testAdditionalStatesDerivatives() {
202         doTestAdditionalStatesDerivatives(Binary64Field.getInstance());
203     }
204 
205     @Test
206     void testFieldVSRealAbsPV() {
207         doTestFieldVsRealAbsPV(Binary64Field.getInstance());
208     }
209 
210     @Test
211     void testDateConsistencyCloseAbsPV() {
212         doTestDateConsistencyCloseAbsPV(Binary64Field.getInstance());
213     }
214 
215     @Test
216     void testFramesConsistencyAbsPV() {
217         Assertions.assertThrows(IllegalArgumentException.class,
218                                 () -> doTestFramesConsistencyAbsPV(Binary64Field.getInstance()));
219     }
220 
221     @Test
222     void testAdditionalStatesAbsPV() {
223         doTestAdditionalStatesAbsPV(Binary64Field.getInstance());
224     }
225 
226     @Test
227     void testAdditionalStatesDerivativesAbsPV() {
228         doTestAdditionalStatesDerivativesAbsPV(Binary64Field.getInstance());
229     }
230 
231     @Test
232     void testResetOnEventAnalytical() {
233         doTestAdditionalTestResetOnEventAnalytical(Binary64Field.getInstance());
234     }
235 
236     @Test
237     void testResetOnEventNumerical() {
238         doTestAdditionalTestResetOnEventNumerical(Binary64Field.getInstance());
239     }
240 
241     @Test
242     void testShiftAdditionalDerivativesDouble() {
243         doTestShiftAdditionalDerivativesDouble(Binary64Field.getInstance());
244     }
245 
246     @Test
247     void testShiftAdditionalDerivativesField() {
248         doTestShiftAdditionalDerivativesField(Binary64Field.getInstance());
249     }
250 
251     @Test
252     void testToTransform() {
253         // GIVEN
254         final ComplexField field = ComplexField.getInstance();
255         final FieldOrbit<Complex> orbit = new FieldCartesianOrbit<>(field, rOrbit);
256         final TimeStampedAngularCoordinates angularCoordinates = new TimeStampedAngularCoordinates(
257                 orbit.getDate().toAbsoluteDate(), Rotation.IDENTITY, Vector3D.ZERO, Vector3D.ZERO);
258         final FieldAttitude<Complex> attitude = new FieldAttitude<>(orbit.getFrame(),
259                 new TimeStampedFieldAngularCoordinates<>(field, angularCoordinates));
260         final FieldSpacecraftState<Complex> state = new FieldSpacecraftState<>(orbit, attitude);
261         // WHEN
262         final FieldTransform<Complex> fieldTransform = state.toTransform();
263         // THEN
264         final Transform expectedTransform = state.toSpacecraftState().toTransform();
265         Assertions.assertEquals(expectedTransform.getDate(), fieldTransform.getDate());
266         final double tolerance = 1e-10;
267         Assertions.assertEquals(expectedTransform.getTranslation().getX(),
268                 fieldTransform.getTranslation().getX().getReal(), tolerance);
269         Assertions.assertEquals(expectedTransform.getTranslation().getY(),
270                 fieldTransform.getTranslation().getY().getReal(), tolerance);
271         Assertions.assertEquals(expectedTransform.getTranslation().getZ(),
272                 fieldTransform.getTranslation().getZ().getReal(), tolerance);
273         Assertions.assertEquals(0., Rotation.distance(expectedTransform.getRotation(),
274                 fieldTransform.getRotation().toRotation()));
275     }
276     
277     @Test
278     void testToStaticTransform() {
279         // GIVEN
280         final ComplexField field = ComplexField.getInstance();
281         final FieldOrbit<Complex> orbit = new FieldCartesianOrbit<>(field, rOrbit);
282         final TimeStampedAngularCoordinates angularCoordinates = new TimeStampedAngularCoordinates(
283                 orbit.getDate().toAbsoluteDate(), Rotation.IDENTITY, Vector3D.ZERO, Vector3D.ZERO);
284         final FieldAttitude<Complex> attitude = new FieldAttitude<>(orbit.getFrame(),
285                 new TimeStampedFieldAngularCoordinates<>(field, angularCoordinates));
286         final FieldSpacecraftState<Complex> state = new FieldSpacecraftState<>(orbit, attitude);
287         // WHEN
288         final FieldStaticTransform<Complex> actualStaticTransform = state.toStaticTransform();
289         // THEN
290         final FieldStaticTransform<Complex> expectedStaticTransform = state.toTransform();
291         Assertions.assertEquals(expectedStaticTransform.getDate(), actualStaticTransform.getDate());
292         final double tolerance = 1e-10;
293         Assertions.assertEquals(expectedStaticTransform.getTranslation().getX().getReal(),
294                 actualStaticTransform.getTranslation().getX().getReal(), tolerance);
295         Assertions.assertEquals(expectedStaticTransform.getTranslation().getY().getReal(),
296                 actualStaticTransform.getTranslation().getY().getReal(), tolerance);
297         Assertions.assertEquals(expectedStaticTransform.getTranslation().getZ().getReal(),
298                 actualStaticTransform.getTranslation().getZ().getReal(), tolerance);
299         Assertions.assertEquals(0., Rotation.distance(expectedStaticTransform.getRotation().toRotation(),
300                 actualStaticTransform.getRotation().toRotation()));
301     }
302 
303     @Test
304     public void testIssue1557() {
305         // GIVEN
306         // Define orbit state
307         final FieldOrbit<Binary64>           mockOrbit  = TestUtils.getFakeFieldOrbit();
308         final FieldSpacecraftState<Binary64> orbitState = new FieldSpacecraftState<>(mockOrbit);
309 
310         // Define PVA state
311         final FieldAbsolutePVCoordinates<Binary64> pva      = TestUtils.getFakeFieldAbsolutePVACoordinates();
312         final FieldSpacecraftState<Binary64>       pvaState = new FieldSpacecraftState<>(pva);
313 
314         // WHEN
315         final FieldVector3D<Binary64> pvaVelocity   = pvaState.getVelocity();
316         final FieldVector3D<Binary64> orbitVelocity = orbitState.getVelocity();
317 
318         // THEN
319         Assertions.assertEquals(pvaState.getVelocity(), pvaVelocity);
320         Assertions.assertEquals(orbitState.getVelocity(), orbitVelocity);
321     }
322 
323     private <T extends CalculusFieldElement<T>> void doTestFieldVsReal(final Field<T> field) {
324         T zero = field.getZero();
325 
326         double mu = 3.9860047e14;
327 
328         T a_f     = zero.add(150000);
329         T e_f     = zero.add(     0);
330         T i_f     = zero.add(     0);
331         T pa_f    = zero.add(     0);
332         T raan_f  = zero.add(     0);
333         T m_f     = zero.add(     0);
334 
335         FieldAbsoluteDate<T> t_f = new FieldAbsoluteDate<>(field);
336 
337         double a_r = a_f.getReal();
338         double e_r = e_f.getReal();
339         double i_r = i_f.getReal();
340         double pa_r = pa_f.getReal();
341         double raan_r = raan_f.getReal();
342         double m_r = m_f.getReal();
343 
344         AbsoluteDate t_r = t_f.toAbsoluteDate();
345 
346 
347         KeplerianOrbit      kep_r = new KeplerianOrbit(a_r, e_r, i_r, pa_r, raan_r, m_r, PositionAngleType.ECCENTRIC, FramesFactory.getEME2000(), t_r, mu);
348         FieldKeplerianOrbit<T> kep_f = new FieldKeplerianOrbit<>(a_f, e_f, i_f, pa_f, raan_f, m_f, PositionAngleType.ECCENTRIC, FramesFactory.getEME2000(), t_f, zero.add(mu));
349 
350         SpacecraftState ScS_r = new SpacecraftState(kep_r);
351         FieldSpacecraftState<T> ScS_f = new FieldSpacecraftState<>(kep_f);
352 
353         for (double dt = 0; dt < 500; dt+=100){
354             SpacecraftState control_r = ScS_r.shiftedBy(dt);
355             FieldSpacecraftState<T> control_f = ScS_f.shiftedBy(zero.add(dt));
356 
357 
358             Assertions.assertEquals(control_r.getOrbit().getA(), control_f.getOrbit().getA().getReal(), 1e-10);
359             Assertions.assertEquals(control_r.getOrbit().getE(), control_f.getOrbit().getE().getReal(), 1e-10);
360             Assertions.assertEquals(control_r.getOrbit().getEquinoctialEx(), control_f.getOrbit().getEquinoctialEx().getReal(), 1e-10);
361             Assertions.assertEquals(control_r.getOrbit().getEquinoctialEy(), control_f.getOrbit().getEquinoctialEy().getReal(), 1e-10);
362             Assertions.assertEquals(control_r.getPosition().getX(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getX(), 1e-10);
363             Assertions.assertEquals(control_r.getPosition().getY(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getY(), 1e-10);
364             Assertions.assertEquals(control_r.getPosition().getZ(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getZ(), 1e-10);
365             Assertions.assertEquals(control_r.getVelocity().getX(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getX(), 1e-10);
366             Assertions.assertEquals(control_r.getVelocity().getY(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getY(), 1e-10);
367             Assertions.assertEquals(control_r.getVelocity().getZ(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getZ(), 1e-10);
368             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getX(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getX(), 1e-10);
369             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getY(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getY(), 1e-10);
370             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getZ(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getZ(), 1e-10);
371             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ0(), control_f.getAttitude().getOrientation().getRotation().getQ0().getReal(), 1e-10);
372             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ1(), control_f.getAttitude().getOrientation().getRotation().getQ1().getReal(), 1e-10);
373             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ2(), control_f.getAttitude().getOrientation().getRotation().getQ2().getReal(), 1e-10);
374             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ3(), control_f.getAttitude().getOrientation().getRotation().getQ3().getReal(), 1e-10);
375 
376             Assertions.assertEquals(control_r.getAttitude().getSpin().getAlpha(), control_f.getAttitude().getSpin().getAlpha().getReal(), 1e-10);
377             Assertions.assertEquals(control_r.getAttitude().getSpin().getDelta(), control_f.getAttitude().getSpin().getDelta().getReal(), 1e-10);
378             Assertions.assertEquals(control_r.getAttitude().getSpin().getNorm(), control_f.getAttitude().getSpin().getNorm().getReal(), 1e-10);
379 
380             Assertions.assertEquals(control_r.getAttitude().getReferenceFrame().isPseudoInertial(), control_f.getAttitude().getReferenceFrame().isPseudoInertial());
381             Assertions.assertEquals(control_r.getAttitude().getDate().durationFrom(AbsoluteDate.J2000_EPOCH), control_f.getAttitude().getDate().durationFrom(AbsoluteDate.J2000_EPOCH).getReal(), 1e-10);
382 
383 
384         }
385 
386     }
387 
388     private <T extends CalculusFieldElement<T>>  void doTestShiftVsEcksteinHechlerError(final Field<T> field)
389         {
390 
391         T zero = field.getZero();
392         T mass = zero.add(2500.);
393         T a = zero.add(rOrbit.getA());
394         T e = zero.add(rOrbit.getE());
395         T i = zero.add(rOrbit.getI());
396         T pa = zero.add(1.9674147913622104);
397         T raan = zero.add(FastMath.toRadians(261));
398         T lv = zero.add(0);
399         final double ae  = 6.378137e6;
400         final double c20 = -1.08263e-3;
401         final double c30 =  2.54e-6;
402         final double c40 =  1.62e-6;
403         final double c50 =  2.3e-7;
404         final double c60 =  -5.5e-7;
405 
406 
407         // polynomial models for interpolation error in position, velocity, acceleration and attitude
408         // these models grow as follows
409         //   interpolation time (s)    position error (m)   velocity error (m/s)   acceleration error (m/s²)  attitude error (°)
410         //           60                        2                    0.07                  0.002               0.00002
411         //          120                       12                    0.3                   0.005               0.00009
412         //          300                      170                    1.6                   0.012               0.0009
413         //          600                     1200                    5.7                   0.024               0.006
414         //          900                     3600                   10.6                   0.034               0.02
415         // the expected maximum residuals with respect to these models are about 0.4m, 0.5mm/s, 8μm/s² and 3e-6°
416         PolynomialFunction pModel = new PolynomialFunction(new double[] {
417             1.5664070631933846e-01,  7.5504722733047560e-03, -8.2460562451009510e-05,
418             6.9546332080305580e-06, -1.7045365367533077e-09, -4.2187860791066264e-13
419         });
420         PolynomialFunction vModel = new PolynomialFunction(new double[] {
421            -3.5472364019908720e-04,  1.6568103861124980e-05,  1.9637913327830596e-05,
422            -3.4248792843039766e-09, -5.6565135131014254e-12,  1.4730170946808630e-15
423         });
424         PolynomialFunction aModel = new PolynomialFunction(new double[] {
425             3.0731707577766896e-06,  3.9770746399850350e-05,  1.9779039254538660e-09,
426             8.0263328220724900e-12, -1.5600835252366078e-14,  1.1785257001549687e-18
427         });
428         PolynomialFunction rModel = new PolynomialFunction(new double[] {
429            -2.7689062063188115e-06,  1.7406542538258334e-07,  2.5109795349592287e-09,
430             2.0399322661074575e-11,  9.9126348912426750e-15, -3.5015638905729510e-18
431         });
432 
433         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
434                                                             TimeComponents.H00,
435                                                             TimeScalesFactory.getUTC());
436 
437         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
438                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
439 
440         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
441 
442         FieldPropagator<T> propagator =
443             new FieldEcksteinHechlerPropagator<>(orbit, attitudeLaw, mass,
444                                                  ae, zero.add(mu), c20, c30, c40, c50, c60);
445 
446         FieldAbsoluteDate<T> centerDate = orbit.getDate().shiftedBy(100.0);
447 
448         FieldSpacecraftState<T> centerState = propagator.propagate(centerDate);
449 
450         double maxResidualP = 0;
451         double maxResidualV = 0;
452         double maxResidualA = 0;
453         double maxResidualR = 0;
454         for (T dt = field.getZero(); dt.getReal() < 900.0; dt = dt.add(5)) {
455 
456             FieldSpacecraftState<T> shifted = centerState.shiftedBy(dt);
457             FieldSpacecraftState<T> propagated = propagator.propagate(centerDate.shiftedBy(dt));
458             FieldPVCoordinates<T> dpv = new FieldPVCoordinates<>(propagated.getPVCoordinates(), shifted.getPVCoordinates());
459 
460 
461             double residualP = pModel.value(dt.getReal()) - dpv.getPosition().getNorm().getReal();
462             double residualV = vModel.value(dt.getReal()) - dpv.getVelocity().getNorm().getReal();
463             double residualA = aModel.value(dt.getReal()) - dpv.getAcceleration().getNorm().getReal();
464             double residualR = rModel.value(dt.getReal()) -
465                                FastMath.toDegrees(FieldRotation.distance(shifted.getAttitude().getRotation(),
466                                                                          propagated.getAttitude().getRotation()).getReal());
467             maxResidualP = FastMath.max(maxResidualP, FastMath.abs(residualP));
468             maxResidualV = FastMath.max(maxResidualV, FastMath.abs(residualV));
469             maxResidualA = FastMath.max(maxResidualA, FastMath.abs(residualA));
470             maxResidualR = FastMath.max(maxResidualR, FastMath.abs(residualR));
471 
472         }
473 
474         Assertions.assertEquals(0.40,   maxResidualP, 0.01);
475         Assertions.assertEquals(4.9e-4, maxResidualV, 1.0e-5);
476         Assertions.assertEquals(2.8e-6, maxResidualR, 1.0e-1);
477 
478     }
479 
480     private <T extends CalculusFieldElement<T>> void doTestDatesConsistency(final Field<T> field) {
481 
482         T zero = field.getZero();
483         T a = zero.add(rOrbit.getA());
484         T e = zero.add(rOrbit.getE());
485         T i = zero.add(rOrbit.getI());
486         T pa = zero.add(1.9674147913622104);
487         T raan = zero.add(FastMath.toRadians(261));
488         T lv = zero.add(0);
489 
490 
491         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
492                                                             TimeComponents.H00,
493                                                             TimeScalesFactory.getUTC());
494 
495         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
496                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
497         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
498 
499         new FieldSpacecraftState<>(orbit, attitudeLaw.getAttitude(orbit.shiftedBy(zero.add(10.0)),
500                                                                   orbit.getDate().shiftedBy(10.0),
501                                                                   orbit.getFrame()));
502     }
503 
504     /**
505      * Check orbit and attitude dates can be off by a few ulps. I see this when using
506      * FixedRate attitude provider.
507      */
508     private <T extends CalculusFieldElement<T>> void doTestDateConsistencyClose(final Field<T> field) {
509 
510 
511         //setup
512         T zero = field.getZero();
513         T one  = field.getOne();
514         T a = zero.add(rOrbit.getA());
515         T e = zero.add(rOrbit.getE());
516         T i = zero.add(rOrbit.getI());
517         T pa = zero.add(1.9674147913622104);
518         T raan = zero.add(FastMath.toRadians(261));
519         T lv = zero.add(0);
520 
521         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
522                                                             TimeComponents.H00,
523                                                             TimeScalesFactory.getUTC());
524         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
525                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
526 
527         FieldKeplerianOrbit<T> orbit10Shifts = orbit;
528         for (int ii = 0; ii < 10; ii++) {
529             orbit10Shifts = orbit10Shifts.shiftedBy(zero.add(0.1));
530         }
531         final FieldOrbit<T> orbit1Shift = orbit.shiftedBy(one);
532 
533         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
534 
535         FieldAttitude<T> shiftedAttitude = attitudeLaw
536                 .getAttitude(orbit1Shift, orbit1Shift.getDate(), orbit.getFrame());
537 
538         // since Orekit 13, dates are equal
539         Assertions.assertEquals(shiftedAttitude.getDate(), orbit10Shifts.getDate());
540 
541         //action + verify no exception is thrown
542         new FieldSpacecraftState<>(orbit10Shifts, shiftedAttitude);
543     }
544 
545     // 
546     private <T extends CalculusFieldElement<T>> void doTestFramesConsistency(final Field<T> field) {
547 
548         T zero = field.getZero();
549         T a = zero.add(rOrbit.getA());
550         T e = zero.add(rOrbit.getE());
551         T i = zero.add(rOrbit.getI());
552         T pa = zero.add(1.9674147913622104);
553         T raan = zero.add(FastMath.toRadians(261));
554         T lv = zero.add(0);
555 
556         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
557                                                             TimeComponents.H00,
558                                                             TimeScalesFactory.getUTC());
559         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
560                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
561 
562         new FieldSpacecraftState<>(orbit,
563                             new FieldAttitude<>(orbit.getDate(),
564                                                 FramesFactory.getGCRF(),
565                                                 Rotation.IDENTITY,
566                                                 Vector3D.ZERO,
567                                                 Vector3D.ZERO,
568                                                 field));
569     }
570 
571     private <T extends CalculusFieldElement<T>> void doTestTransform(final Field<T> field) {
572 
573         T zero = field.getZero();
574         T a = zero.add(rOrbit.getA());
575         T e = zero.add(rOrbit.getE());
576         T i = zero.add(rOrbit.getI());
577         T pa = zero.add(1.9674147913622104);
578         T raan = zero.add(FastMath.toRadians(261));
579         T lv = zero.add(0);
580         T mass = zero.add(2500);
581 
582         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
583                         TimeComponents.H00,
584                         TimeScalesFactory.getUTC());
585 
586         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
587                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
588 
589         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
590 
591         FieldKeplerianPropagator<T> propagator =
592                         new FieldKeplerianPropagator<>(orbit, attitudeLaw, zero.add(mu), mass);
593 
594         double maxDP = 0;
595         double maxDV = 0;
596         double maxDA = 0;
597         for (double t = 0; t < orbit.getKeplerianPeriod().getReal(); t += 60) {
598             final FieldSpacecraftState<T> state = propagator.propagate(orbit.getDate().shiftedBy(zero.add(t)));
599             final Transform transform = state.toSpacecraftState().toTransform().getInverse();
600             PVCoordinates pv = transform.transformPVCoordinates(PVCoordinates.ZERO);
601             PVCoordinates dPV = new PVCoordinates(pv, state.getPVCoordinates().toPVCoordinates());
602             Vector3D mZDirection = transform.transformVector(Vector3D.MINUS_K);
603             double alpha = Vector3D.angle(mZDirection, state.getPosition().toVector3D());
604             maxDP = FastMath.max(maxDP, dPV.getPosition().getNorm());
605             maxDV = FastMath.max(maxDV, dPV.getVelocity().getNorm());
606             maxDA = FastMath.max(maxDA, FastMath.toDegrees(alpha));
607         }
608         Assertions.assertEquals(0.0, maxDP, 1.0e-6);
609         Assertions.assertEquals(0.0, maxDV, 1.0e-9);
610         Assertions.assertEquals(0.0, maxDA, 8.1e-10);
611 
612     }
613 
614     private <T extends CalculusFieldElement<T>> void doTestAdditionalStates(final Field<T> field) {
615 
616         T zero = field.getZero();
617         T a = zero.add(rOrbit.getA());
618         T e = zero.add(rOrbit.getE());
619         T i = zero.add(rOrbit.getI());
620         T pa = zero.add(1.9674147913622104);
621         T raan = zero.add(FastMath.toRadians(261));
622         T lv = zero.add(0);
623         T mass = zero.add(2500);
624 
625         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
626                                                             TimeComponents.H00,
627                                                             TimeScalesFactory.getUTC());
628 
629         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
630                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
631 
632         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
633 
634         FieldKeplerianPropagator<T> propagator =
635                         new FieldKeplerianPropagator<>(orbit, attitudeLaw, zero.add(mu), mass);
636 
637 
638 
639 
640         final FieldSpacecraftState<T> state = propagator.propagate(orbit.getDate().shiftedBy(60));
641         T[] add = MathArrays.buildArray(field, 2);
642         add[0] = zero.add(1.);
643         add[1] = zero.add(2.);
644         final FieldSpacecraftState<T> extended =
645                 state.
646                         addAdditionalData("test-1", add).
647                         addAdditionalData("test-2", zero.add(42.0)).
648                         addAdditionalData("test-string", "hello");
649         Assertions.assertEquals(0, state.getAdditionalDataValues().size());
650         Assertions.assertFalse(state.hasAdditionalData("test-1"));
651         try {
652             state.getAdditionalData("test-1");
653             Assertions.fail("an exception should have been thrown");
654         } catch (OrekitException oe) {
655             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
656             Assertions.assertEquals(oe.getParts()[0], "test-1");
657         }
658         try {
659             state.ensureCompatibleAdditionalStates(extended);
660             Assertions.fail("an exception should have been thrown");
661         } catch (OrekitException oe) {
662             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
663             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
664         }
665         try {
666             extended.ensureCompatibleAdditionalStates(state);
667             Assertions.fail("an exception should have been thrown");
668         } catch (OrekitException oe) {
669             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
670             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
671         }
672         try {
673             T[] kk = MathArrays.buildArray(field, 7);
674             extended.ensureCompatibleAdditionalStates(extended.addAdditionalData("test-2", kk));
675             Assertions.fail("an exception should have been thrown");
676         } catch (MathIllegalStateException mise) {
677             Assertions.assertEquals(LocalizedCoreFormats.DIMENSIONS_MISMATCH, mise.getSpecifier());
678             Assertions.assertEquals(7, ((Integer) mise.getParts()[0]).intValue());
679         }
680         extended.ensureCompatibleAdditionalStates(extended.addAdditionalData("test-string", "hello"));
681         Assertions.assertEquals(3, extended.getAdditionalDataValues().size());
682         Assertions.assertTrue(extended.hasAdditionalData("test-1"));
683         Assertions.assertTrue(extended.hasAdditionalData("test-2"));
684         Assertions.assertEquals( 1.0, extended.getAdditionalState("test-1")[0].getReal(), 1.0e-15);
685         Assertions.assertEquals( 2.0, extended.getAdditionalState("test-1")[1].getReal(), 1.0e-15);
686         Assertions.assertEquals(42.0, extended.getAdditionalState("test-2")[0].getReal(), 1.0e-15);
687         Assertions.assertEquals("hello", extended.getAdditionalData("test-string"));
688 
689         // test various constructors
690         T[] dd = MathArrays.buildArray(field, 1);
691         dd[0] = zero.add(-6.0);
692         FieldDataDictionary<T> dictionary = new FieldDataDictionary<>(field);
693         dictionary.put("test-3", dd);
694         FieldSpacecraftState<T> sO = state.withAdditionalData(dictionary);
695         FieldSpacecraftState<T> sFromDouble = new FieldSpacecraftState<>(field, sO.toSpacecraftState());
696         Assertions.assertEquals(-6.0, sFromDouble.getAdditionalState("test-3")[0].getReal(), 1.0e-15);
697 
698     }
699 
700     private <T extends CalculusFieldElement<T>> void doTestAdditionalStatesDerivatives(final Field<T> field) {
701 
702         T zero = field.getZero();
703         T a = zero.add(rOrbit.getA());
704         T e = zero.add(rOrbit.getE());
705         T i = zero.add(rOrbit.getI());
706         T pa = zero.add(1.9674147913622104);
707         T raan = zero.add(FastMath.toRadians(261));
708         T lv = zero.add(0);
709         T mass = zero.add(2500);
710 
711         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
712                                                             TimeComponents.H00,
713                                                             TimeScalesFactory.getUTC());
714 
715         FieldKeplerianOrbit<T> orbit = new FieldKeplerianOrbit<>(a, e, i, pa, raan, lv, PositionAngleType.TRUE,
716                                                                  FramesFactory.getEME2000(), date, zero.add(mu));
717 
718         BodyCenterPointing attitudeLaw = new BodyCenterPointing(orbit.getFrame(), earth);
719 
720         FieldKeplerianPropagator<T> propagator =
721                         new FieldKeplerianPropagator<>(orbit, attitudeLaw, zero.add(mu), mass);
722 
723 
724 
725 
726         final FieldSpacecraftState<T> state = propagator.propagate(orbit.getDate().shiftedBy(60));
727         T[] add = MathArrays.buildArray(field, 2);
728         add[0] = zero.add(1.);
729         add[1] = zero.add(2.);
730         final FieldSpacecraftState<T> extended =
731                 state.
732                  addAdditionalStateDerivative("test-1", add).
733                   addAdditionalStateDerivative("test-2", zero.add(42.0));
734         Assertions.assertEquals(0, state.getAdditionalStatesDerivatives().size());
735         Assertions.assertFalse(state.hasAdditionalStateDerivative("test-1"));
736         try {
737             state.getAdditionalStateDerivative("test-1");
738             Assertions.fail("an exception should have been thrown");
739         } catch (OrekitException oe) {
740             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
741             Assertions.assertEquals(oe.getParts()[0], "test-1");
742         }
743         try {
744             state.ensureCompatibleAdditionalStates(extended);
745             Assertions.fail("an exception should have been thrown");
746         } catch (OrekitException oe) {
747             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
748             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
749         }
750         try {
751             extended.ensureCompatibleAdditionalStates(state);
752             Assertions.fail("an exception should have been thrown");
753         } catch (OrekitException oe) {
754             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
755             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
756         }
757         try {
758             T[] kk = MathArrays.buildArray(field, 7);
759             extended.ensureCompatibleAdditionalStates(extended.addAdditionalStateDerivative("test-2", kk));
760             Assertions.fail("an exception should have been thrown");
761         } catch (MathIllegalStateException mise) {
762             Assertions.assertEquals(LocalizedCoreFormats.DIMENSIONS_MISMATCH, mise.getSpecifier());
763             Assertions.assertEquals(7, ((Integer) mise.getParts()[0]).intValue());
764         }
765         Assertions.assertEquals(2, extended.getAdditionalStatesDerivatives().size());
766         Assertions.assertTrue(extended.hasAdditionalStateDerivative("test-1"));
767         Assertions.assertTrue(extended.hasAdditionalStateDerivative("test-2"));
768         Assertions.assertEquals( 1.0, extended.getAdditionalStateDerivative("test-1")[0].getReal(), 1.0e-15);
769         Assertions.assertEquals( 2.0, extended.getAdditionalStateDerivative("test-1")[1].getReal(), 1.0e-15);
770         Assertions.assertEquals(42.0, extended.getAdditionalStateDerivative("test-2")[0].getReal(), 1.0e-15);
771 
772         // test most complete constructor
773         T[] dd = MathArrays.buildArray(field, 1);
774         dd[0] = zero.add(-6.0);
775         FieldArrayDictionary<T> dict = new FieldArrayDictionary<>(field);
776         dict.put("test-3", dd);
777         FieldSpacecraftState<T> s = new FieldSpacecraftState<>(state.getOrbit(), state.getAttitude(), state.getMass(), null, dict);
778         Assertions.assertFalse(s.hasAdditionalData("test-3"));
779         Assertions.assertEquals(-6.0, s.getAdditionalStateDerivative("test-3")[0].getReal(), 1.0e-15);
780 
781         // test conversion
782         FieldSpacecraftState<T> rebuilt = new FieldSpacecraftState<>(field, s.toSpacecraftState());
783         rebuilt.ensureCompatibleAdditionalStates(s);
784 
785     }
786 
787     @Test
788     void testWithAdditionalEmpty() {
789         // GIVEN
790         final SpacecraftState state = new SpacecraftState(TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH));
791         final Binary64Field field = Binary64Field.getInstance();
792         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
793         // WHEN
794         final FieldSpacecraftState<Binary64> newState = fieldState.withAdditionalData(null);
795         // THEN
796         Assertions.assertEquals(0, newState.getAdditionalDataValues().size());
797     }
798 
799     @Test
800     void testWithAdditionalStatesDerivatives() {
801         // GIVEN
802         final SpacecraftState state = new SpacecraftState(TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH));
803         final Binary64Field field = Binary64Field.getInstance();
804         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(field, state);
805         // WHEN
806         final FieldSpacecraftState<Binary64> newState = fieldState.withAdditionalStatesDerivatives(null);
807         // THEN
808         Assertions.assertEquals(0, newState.getAdditionalStatesDerivatives().size());
809     }
810 
811     private <T extends CalculusFieldElement<T>> void doTestFieldVsRealAbsPV(final Field<T> field) {
812         T zero = field.getZero();
813 
814         T x_f     = zero.add(0.8);
815         T y_f     = zero.add(0.2);
816         T z_f     = zero;
817         T vx_f    = zero;
818         T vy_f    = zero;
819         T vz_f    = zero.add(0.1);
820 
821         FieldAbsoluteDate<T> t_f = new FieldAbsoluteDate<>(field);
822 
823         FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x_f,y_f,z_f), new FieldVector3D<>(vx_f,vy_f,vz_f));
824 
825         FieldAbsolutePVCoordinates<T> absPV_f = new FieldAbsolutePVCoordinates<>(FramesFactory.getEME2000(), t_f, pva_f);
826 
827         FieldSpacecraftState<T> ScS_f = new FieldSpacecraftState<>(absPV_f);
828         FieldSpacecraftState<T> withM = new FieldSpacecraftState<>(absPV_f).withMass(zero.newInstance(1234.5));
829         Assertions.assertEquals(1234.5, withM.getMass().getReal(), 1.0e-10);
830         SpacecraftState ScS_r = ScS_f.toSpacecraftState();
831 
832         for (double dt = 0; dt < 500; dt+=100){
833             SpacecraftState control_r = ScS_r.shiftedBy(dt);
834             FieldSpacecraftState<T> control_f = ScS_f.shiftedBy(zero.add(dt));
835 
836             Assertions.assertEquals(control_r.getPosition().getX(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getX(), 1e-10);
837             Assertions.assertEquals(control_r.getPosition().getY(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getY(), 1e-10);
838             Assertions.assertEquals(control_r.getPosition().getZ(), control_f.getPVCoordinates().toPVCoordinates().getPosition().getZ(), 1e-10);
839             Assertions.assertEquals(control_r.getVelocity().getX(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getX(), 1e-10);
840             Assertions.assertEquals(control_r.getVelocity().getY(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getY(), 1e-10);
841             Assertions.assertEquals(control_r.getVelocity().getZ(), control_f.getPVCoordinates().toPVCoordinates().getVelocity().getZ(), 1e-10);
842             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getX(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getX(), 1e-10);
843             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getY(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getY(), 1e-10);
844             Assertions.assertEquals(control_r.getPVCoordinates().getAcceleration().getZ(), control_f.getPVCoordinates().toPVCoordinates().getAcceleration().getZ(), 1e-10);
845             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ0(), control_f.getAttitude().getOrientation().getRotation().getQ0().getReal(), 1e-10);
846             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ1(), control_f.getAttitude().getOrientation().getRotation().getQ1().getReal(), 1e-10);
847             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ2(), control_f.getAttitude().getOrientation().getRotation().getQ2().getReal(), 1e-10);
848             Assertions.assertEquals(control_r.getAttitude().getOrientation().getRotation().getQ3(), control_f.getAttitude().getOrientation().getRotation().getQ3().getReal(), 1e-10);
849 
850             Assertions.assertEquals(control_r.getAttitude().getSpin().getAlpha(), control_f.getAttitude().getSpin().getAlpha().getReal(), 1e-10);
851             Assertions.assertEquals(control_r.getAttitude().getSpin().getDelta(), control_f.getAttitude().getSpin().getDelta().getReal(), 1e-10);
852             Assertions.assertEquals(control_r.getAttitude().getSpin().getNorm(), control_f.getAttitude().getSpin().getNorm().getReal(), 1e-10);
853 
854             Assertions.assertEquals(control_r.getAttitude().getReferenceFrame().isPseudoInertial(), control_f.getAttitude().getReferenceFrame().isPseudoInertial());
855             Assertions.assertEquals(control_r.getAttitude().getDate().durationFrom(AbsoluteDate.J2000_EPOCH), control_f.getAttitude().getDate().durationFrom(AbsoluteDate.J2000_EPOCH).getReal(), 1e-10);
856 
857 
858         }
859     }
860 
861 
862     /**
863      * Check orbit and attitude dates can be off by a few ulps. I see this when using
864      * FixedRate attitude provider.
865      */
866     private <T extends CalculusFieldElement<T>> void doTestDateConsistencyCloseAbsPV(final Field<T> field) {
867 
868 
869         //setup
870         T zero = field.getZero();
871         T one  = field.getOne();
872         T x_f     = zero.add(0.8);
873         T y_f     = zero.add(0.2);
874         T z_f     = zero;
875         T vx_f    = zero;
876         T vy_f    = zero;
877         T vz_f    = zero.add(0.1);
878         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
879                                                             TimeComponents.H00,
880                                                             TimeScalesFactory.getUTC());
881         FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x_f,y_f,z_f), new FieldVector3D<>(vx_f,vy_f,vz_f));
882 
883         FieldAbsolutePVCoordinates<T> absPV_f = new FieldAbsolutePVCoordinates<>(FramesFactory.getEME2000(), date, pva_f);
884 
885         FieldAbsolutePVCoordinates<T> AbsolutePVCoordinates10Shifts = absPV_f;
886         for (int ii = 0; ii < 10; ii++) {
887             AbsolutePVCoordinates10Shifts = AbsolutePVCoordinates10Shifts.shiftedBy(zero.add(0.1));
888         }
889         final FieldAbsolutePVCoordinates<T> AbsolutePVCoordinates1Shift = absPV_f.shiftedBy(one);
890 
891         BodyCenterPointing attitudeLaw = new BodyCenterPointing(absPV_f.getFrame(), earth);
892 
893         FieldAttitude<T> shiftedAttitude = attitudeLaw.getAttitude(AbsolutePVCoordinates1Shift,
894                                                                    AbsolutePVCoordinates1Shift.getDate(),
895                                                                    absPV_f.getFrame());
896         // since Orekit 13, dates are equal
897         Assertions.assertEquals(shiftedAttitude.getDate(), AbsolutePVCoordinates10Shifts.getDate());
898 
899         //action + verify no exception is thrown
900         FieldSpacecraftState<T> s1 = new FieldSpacecraftState<>(AbsolutePVCoordinates10Shifts, shiftedAttitude);
901         FieldSpacecraftState<T> s2 = s1.shiftedBy(0.001);
902 
903         try {
904             // but here, the time difference is too great
905             new FieldSpacecraftState<>(AbsolutePVCoordinates10Shifts, s2.getAttitude());
906             Assertions.fail("an exception should have been thrown");
907         } catch (OrekitIllegalArgumentException oiae) {
908             Assertions.assertEquals(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
909                                 oiae.getSpecifier());
910         }
911     }
912 
913     private <T extends CalculusFieldElement<T>> void doTestGetAdditionalStateBadType(final Field<T> field) {
914         final SpacecraftState state = new SpacecraftState(rOrbit);
915         final FieldSpacecraftState<T> fieldState = new FieldSpacecraftState<>(field, state).addAdditionalData("string", "hello there");
916         OrekitException exception = Assertions.assertThrows(OrekitException.class, () -> fieldState.getAdditionalState("string"));
917         Assertions.assertEquals(OrekitMessages.ADDITIONAL_STATE_BAD_TYPE, exception.getSpecifier());
918         Assertions.assertEquals("string", exception.getParts()[0]);
919     }
920 
921     private <T extends CalculusFieldElement<T>> void doTestFramesConsistencyAbsPV(final Field<T> field) {
922 
923         T zero = field.getZero();
924 
925         T x_f     = zero.add(0.8);
926         T y_f     = zero.add(0.2);
927         T z_f     = zero;
928         T vx_f    = zero;
929         T vy_f    = zero;
930         T vz_f    = zero.add(0.1);
931 
932 
933         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
934                         TimeComponents.H00,
935                         TimeScalesFactory.getUTC());
936 
937         FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x_f,y_f,z_f), new FieldVector3D<>(vx_f,vy_f,vz_f));
938 
939         FieldAbsolutePVCoordinates<T> absPV_f = new FieldAbsolutePVCoordinates<>(FramesFactory.getEME2000(), date, pva_f);
940 
941         new FieldSpacecraftState<>(absPV_f,
942                         new FieldAttitude<>(absPV_f.getDate(),
943                                         FramesFactory.getGCRF(),
944                                         FieldRotation.getIdentity(field),
945                                         FieldVector3D.getZero(field),
946                                         FieldVector3D.getZero(field)));
947     }
948 
949     private <T extends CalculusFieldElement<T>> void doTestAdditionalStatesAbsPV(final Field<T> field) {
950 
951         T zero = field.getZero();
952         T x_f     = zero.add(0.8);
953         T y_f     = zero.add(0.2);
954         T z_f     = zero;
955         T vx_f    = zero;
956         T vy_f    = zero;
957         T vz_f    = zero.add(0.1);
958 
959         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
960                                                             TimeComponents.H00,
961                                                             TimeScalesFactory.getUTC());
962 
963         FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x_f,y_f,z_f), new FieldVector3D<>(vx_f,vy_f,vz_f));
964 
965         FieldAbsolutePVCoordinates<T> absPV_f = new FieldAbsolutePVCoordinates<>(FramesFactory.getEME2000(), date, pva_f);
966 
967         FieldNumericalPropagator<T> prop = new FieldNumericalPropagator<>(field,
968                         new DormandPrince853FieldIntegrator<>(field, 0.1, 500, 0.001, 0.001));
969         prop.setOrbitType(null);
970 
971         final FieldSpacecraftState<T> initialState = new FieldSpacecraftState<>(absPV_f);
972 
973         prop.resetInitialState(initialState);
974 
975         final FieldSpacecraftState<T> state = prop.propagate(absPV_f.getDate().shiftedBy(60));
976         T[] add = MathArrays.buildArray(field, 2);
977         add[0] = zero.add(1.);
978         add[1] = zero.add(2.);
979         final FieldSpacecraftState<T> extended =
980                 state.
981                         addAdditionalData("test-1", add).
982                         addAdditionalData("test-2", zero.add(42.0));
983         Assertions.assertEquals(0, state.getAdditionalDataValues().size());
984         Assertions.assertFalse(state.hasAdditionalData("test-1"));
985         try {
986             state.getAdditionalData("test-1");
987             Assertions.fail("an exception should have been thrown");
988         } catch (OrekitException oe) {
989             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
990             Assertions.assertEquals(oe.getParts()[0], "test-1");
991         }
992         try {
993             state.ensureCompatibleAdditionalStates(extended);
994             Assertions.fail("an exception should have been thrown");
995         } catch (OrekitException oe) {
996             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
997             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
998         }
999         try {
1000             extended.ensureCompatibleAdditionalStates(state);
1001             Assertions.fail("an exception should have been thrown");
1002         } catch (OrekitException oe) {
1003             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
1004             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
1005         }
1006         try {
1007             T[] kk = MathArrays.buildArray(field, 7);
1008             extended.ensureCompatibleAdditionalStates(extended.addAdditionalData("test-2", kk));
1009             Assertions.fail("an exception should have been thrown");
1010         } catch (MathIllegalStateException mise) {
1011             Assertions.assertEquals(LocalizedCoreFormats.DIMENSIONS_MISMATCH, mise.getSpecifier());
1012             Assertions.assertEquals(7, ((Integer) mise.getParts()[0]).intValue());
1013         }
1014         Assertions.assertEquals(2, extended.getAdditionalDataValues().size());
1015         Assertions.assertTrue(extended.hasAdditionalData("test-1"));
1016         Assertions.assertTrue(extended.hasAdditionalData("test-2"));
1017         Assertions.assertEquals( 1.0, extended.getAdditionalState("test-1")[0].getReal(), 1.0e-15);
1018         Assertions.assertEquals( 2.0, extended.getAdditionalState("test-1")[1].getReal(), 1.0e-15);
1019         Assertions.assertEquals(42.0, extended.getAdditionalState("test-2")[0].getReal(), 1.0e-15);
1020 
1021         // test various constructors
1022         T[] dd = MathArrays.buildArray(field, 1);
1023         dd[0] = zero.add(-6.0);
1024         FieldDataDictionary<T> map = new FieldDataDictionary<>(field);
1025         map.put("test-3", dd);
1026         FieldSpacecraftState<T> sO = new FieldSpacecraftState<>(state.getAbsPVA()).withAdditionalData(map);
1027         Assertions.assertEquals(-6.0, sO.getAdditionalState("test-3")[0].getReal(), 1.0e-15);
1028         FieldSpacecraftState<T> sFromDouble = new FieldSpacecraftState<>(field, sO.toSpacecraftState());
1029         Assertions.assertEquals(-6.0, sFromDouble.getAdditionalState("test-3")[0].getReal(), 1.0e-15);
1030 
1031     }
1032 
1033     private <T extends CalculusFieldElement<T>> void doTestAdditionalStatesDerivativesAbsPV(final Field<T> field) {
1034 
1035         T zero = field.getZero();
1036         T x_f     = zero.add(0.8);
1037         T y_f     = zero.add(0.2);
1038         T z_f     = zero;
1039         T vx_f    = zero;
1040         T vy_f    = zero;
1041         T vz_f    = zero.add(0.1);
1042 
1043         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2004, 01, 01),
1044                                                             TimeComponents.H00,
1045                                                             TimeScalesFactory.getUTC());
1046 
1047         FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x_f,y_f,z_f), new FieldVector3D<>(vx_f,vy_f,vz_f));
1048 
1049         FieldAbsolutePVCoordinates<T> absPV_f = new FieldAbsolutePVCoordinates<>(FramesFactory.getEME2000(), date, pva_f);
1050 
1051         FieldNumericalPropagator<T> prop = new FieldNumericalPropagator<>(field,
1052                         new DormandPrince853FieldIntegrator<>(field, 0.1, 500, 0.001, 0.001));
1053         prop.setOrbitType(null);
1054 
1055         final FieldSpacecraftState<T> initialState = new FieldSpacecraftState<>(absPV_f);
1056 
1057         prop.resetInitialState(initialState);
1058 
1059         final FieldSpacecraftState<T> state = prop.propagate(absPV_f.getDate().shiftedBy(60));
1060         T[] add = MathArrays.buildArray(field, 2);
1061         add[0] = zero.add(1.);
1062         add[1] = zero.add(2.);
1063         final FieldSpacecraftState<T> extended =
1064                 state.
1065                  addAdditionalStateDerivative("test-1", add).
1066                   addAdditionalStateDerivative("test-2", zero.add(42.0));
1067         Assertions.assertEquals(0, state.getAdditionalStatesDerivatives().size());
1068         Assertions.assertFalse(state.hasAdditionalStateDerivative("test-1"));
1069         try {
1070             state.getAdditionalStateDerivative("test-1");
1071             Assertions.fail("an exception should have been thrown");
1072         } catch (OrekitException oe) {
1073             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
1074             Assertions.assertEquals(oe.getParts()[0], "test-1");
1075         }
1076         try {
1077             state.ensureCompatibleAdditionalStates(extended);
1078             Assertions.fail("an exception should have been thrown");
1079         } catch (OrekitException oe) {
1080             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
1081             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
1082         }
1083         try {
1084             extended.ensureCompatibleAdditionalStates(state);
1085             Assertions.fail("an exception should have been thrown");
1086         } catch (OrekitException oe) {
1087             Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.UNKNOWN_ADDITIONAL_DATA);
1088             Assertions.assertTrue(oe.getParts()[0].toString().startsWith("test-"));
1089         }
1090         try {
1091             T[] kk = MathArrays.buildArray(field, 7);
1092             extended.ensureCompatibleAdditionalStates(extended.addAdditionalStateDerivative("test-2", kk));
1093             Assertions.fail("an exception should have been thrown");
1094         } catch (MathIllegalStateException mise) {
1095             Assertions.assertEquals(LocalizedCoreFormats.DIMENSIONS_MISMATCH, mise.getSpecifier());
1096             Assertions.assertEquals(7, ((Integer) mise.getParts()[0]).intValue());
1097         }
1098         Assertions.assertEquals(2, extended.getAdditionalStatesDerivatives().size());
1099         Assertions.assertTrue(extended.hasAdditionalStateDerivative("test-1"));
1100         Assertions.assertTrue(extended.hasAdditionalStateDerivative("test-2"));
1101         Assertions.assertEquals( 1.0, extended.getAdditionalStateDerivative("test-1")[0].getReal(), 1.0e-15);
1102         Assertions.assertEquals( 2.0, extended.getAdditionalStateDerivative("test-1")[1].getReal(), 1.0e-15);
1103         Assertions.assertEquals(42.0, extended.getAdditionalStateDerivative("test-2")[0].getReal(), 1.0e-15);
1104 
1105         // test most complete constructor
1106         T[] dd = MathArrays.buildArray(field, 1);
1107         dd[0] = zero.add(-6.0);
1108         FieldArrayDictionary<T> dictionary = new FieldArrayDictionary<T>(field);
1109         dictionary.put("test-3", dd);
1110         FieldSpacecraftState<T> s = new FieldSpacecraftState<>(state.getAbsPVA(), state.getAttitude(), state.getMass(), null, dictionary);
1111         Assertions.assertFalse(s.hasAdditionalData("test-3"));
1112         Assertions.assertEquals(-6.0, s.getAdditionalStateDerivative("test-3")[0].getReal(), 1.0e-15);
1113 
1114     }
1115 
1116     private <T extends CalculusFieldElement<T>> void doTestAdditionalTestResetOnEventAnalytical(final Field<T> field) {
1117 
1118         T zero = field.getZero();
1119 
1120         // Build orbit
1121         FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, 2000, 1, 1, TimeScalesFactory.getUTC());
1122         FieldOrbit<T> orbit = new FieldKeplerianOrbit<>(zero.add(7.1E6), zero, zero, zero, zero, zero,
1123                                          PositionAngleType.TRUE, FramesFactory.getGCRF(), date0,
1124                                          zero.add(Constants.WGS84_EARTH_MU));
1125 
1126         // Build propagator
1127         FieldKeplerianPropagator<T> propagator = new FieldKeplerianPropagator<>(orbit);
1128 
1129         // Create initial state with one additional state and add it to the propagator
1130         final String name = "A";
1131         FieldSpacecraftState<T> initialState = new FieldSpacecraftState<>(orbit).
1132                 addAdditionalData(name, zero.add(-1));
1133 
1134         propagator.resetInitialState(initialState);
1135 
1136         // Create date detector and handler
1137         FieldAbsoluteDate<T> changeDate = date0.shiftedBy(3);
1138         FieldDateDetector<T> dateDetector = new FieldDateDetector<>(field, changeDate).
1139                                     withHandler(new FieldEventHandler<T>() {
1140 
1141             @Override
1142             public Action eventOccurred(FieldSpacecraftState<T> s, FieldEventDetector<T> detector, boolean increasing) {
1143               return Action.RESET_STATE;
1144             }
1145 
1146             @Override
1147             public FieldSpacecraftState<T> resetState(FieldEventDetector<T> detector, FieldSpacecraftState<T> oldState) {
1148                 return oldState.addAdditionalData(name, zero.add(+1));
1149             }
1150 
1151         });
1152 
1153         propagator.addEventDetector(dateDetector);
1154         propagator.setStepHandler(zero.add(0.125), s -> {
1155             if (s.getDate().durationFrom(changeDate).getReal() < -0.001) {
1156                 Assertions.assertEquals(-1, s.getAdditionalState(name)[0].getReal(), 1.0e-15);
1157             } else if (s.getDate().durationFrom(changeDate).getReal() > +0.001) {
1158                 Assertions.assertEquals(+1, s.getAdditionalState(name)[0].getReal(), 1.0e-15);
1159             }
1160         });
1161         FieldSpacecraftState<T> finalState = propagator.propagate(date0, date0.shiftedBy(5));
1162         Assertions.assertEquals(+1, finalState.getAdditionalState(name)[0].getReal(), 1.0e-15);
1163 
1164     }
1165 
1166     private <T extends CalculusFieldElement<T>> void doTestAdditionalTestResetOnEventNumerical(final Field<T> field) {
1167 
1168         T zero = field.getZero();
1169 
1170         // Build orbit
1171         FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, 2000, 1, 1, TimeScalesFactory.getUTC());
1172         FieldOrbit<T> orbit = new FieldKeplerianOrbit<>(zero.add(7.1E6), zero, zero, zero, zero, zero,
1173                                          PositionAngleType.TRUE, FramesFactory.getGCRF(), date0,
1174                                          zero.add(Constants.WGS84_EARTH_MU));
1175 
1176         // Build propagator
1177         FieldODEIntegrator<T> odeIntegrator = new DormandPrince853FieldIntegrator<>(field, 1E-3, 1E3, 1E-6, 1E-6);
1178         FieldNumericalPropagator<T> propagator = new FieldNumericalPropagator<>(field, odeIntegrator);
1179 
1180         // Create initial state with one additional state and add it to the propagator
1181         final String name = "A";
1182         FieldSpacecraftState<T> initialState = new FieldSpacecraftState<>(orbit).
1183                 addAdditionalData(name, zero.add(-1));
1184 
1185         propagator.resetInitialState(initialState);
1186 
1187         // Create date detector and handler
1188         FieldAbsoluteDate<T> changeDate = date0.shiftedBy(3);
1189         FieldDateDetector<T> dateDetector = new FieldDateDetector<>(field, changeDate).
1190                                     withHandler(new FieldEventHandler<T>() {
1191 
1192             @Override
1193             public Action eventOccurred(FieldSpacecraftState<T> s, FieldEventDetector<T> detector, boolean increasing) {
1194               return Action.RESET_STATE;
1195             }
1196 
1197             @Override
1198             public FieldSpacecraftState<T> resetState(FieldEventDetector<T> detector, FieldSpacecraftState<T> oldState) {
1199                 return oldState.addAdditionalData(name, zero.add(+1));
1200             }
1201 
1202         });
1203 
1204         propagator.addEventDetector(dateDetector);
1205         propagator.setStepHandler(zero.add(0.125), s -> {
1206             if (s.getDate().durationFrom(changeDate).getReal() < -0.001) {
1207                 Assertions.assertEquals(-1, s.getAdditionalState(name)[0].getReal(), 1.0e-15);
1208             } else if (s.getDate().durationFrom(changeDate).getReal() > +0.001) {
1209                 Assertions.assertEquals(+1, s.getAdditionalState(name)[0].getReal(), 1.0e-15);
1210             }
1211         });
1212         FieldSpacecraftState<T> finalState = propagator.propagate(date0, date0.shiftedBy(5));
1213         Assertions.assertEquals(+1, finalState.getAdditionalState(name)[0].getReal(), 1.0e-15);
1214 
1215     }
1216 
1217     private <T extends CalculusFieldElement<T>> void doTestShiftAdditionalDerivativesDouble(final Field<T> field) {
1218 
1219         T zero = field.getZero();
1220 
1221         // Build orbit
1222         FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, 2000, 1, 1, TimeScalesFactory.getUTC());
1223         FieldOrbit<T> orbit = new FieldKeplerianOrbit<>(zero.add(7.1E6), zero, zero, zero, zero, zero,
1224                                          PositionAngleType.TRUE, FramesFactory.getGCRF(), date0,
1225                                          zero.add(Constants.WGS84_EARTH_MU));
1226 
1227         // Build propagator
1228         FieldKeplerianPropagator<T> propagator = new FieldKeplerianPropagator<>(orbit);
1229 
1230         final String valueAndDerivative = "value-and-derivative";
1231         final String valueOnly          = "value-only";
1232         final String derivativeOnly     = "derivative-only";
1233         final FieldSpacecraftState<T> s0 = propagator.getInitialState().
1234                 addAdditionalData(valueAndDerivative,           convert(field, new double[] { 1.0,  2.0 })).
1235                                            addAdditionalStateDerivative(valueAndDerivative, convert(field, new double[] { 3.0,  2.0 })).
1236                 addAdditionalData(valueOnly,                    convert(field, new double[] { 5.0,  4.0 })).
1237                                            addAdditionalStateDerivative(derivativeOnly,     convert(field, new double[] { 1.0, -1.0 }));
1238         Assertions.assertEquals( 1.0, s0.getAdditionalState(valueAndDerivative)[0].getReal(),           1.0e-15);
1239         Assertions.assertEquals( 2.0, s0.getAdditionalState(valueAndDerivative)[1].getReal(),           1.0e-15);
1240         Assertions.assertEquals( 3.0, s0.getAdditionalStateDerivative(valueAndDerivative)[0].getReal(), 1.0e-15);
1241         Assertions.assertEquals( 2.0, s0.getAdditionalStateDerivative(valueAndDerivative)[1].getReal(), 1.0e-15);
1242         Assertions.assertEquals( 5.0, s0.getAdditionalState(valueOnly)[0].getReal(),                    1.0e-15);
1243         Assertions.assertEquals( 4.0, s0.getAdditionalState(valueOnly)[1].getReal(),                    1.0e-15);
1244         Assertions.assertEquals( 1.0, s0.getAdditionalStateDerivative(derivativeOnly)[0].getReal(),     1.0e-15);
1245         Assertions.assertEquals(-1.0, s0.getAdditionalStateDerivative(derivativeOnly)[1].getReal(),     1.0e-15);
1246         final FieldSpacecraftState<T> s1 = s0.shiftedBy(-2.0);
1247         Assertions.assertEquals(-5.0, s1.getAdditionalState(valueAndDerivative)[0].getReal(),           1.0e-15);
1248         Assertions.assertEquals(-2.0, s1.getAdditionalState(valueAndDerivative)[1].getReal(),           1.0e-15);
1249         Assertions.assertEquals( 3.0, s1.getAdditionalStateDerivative(valueAndDerivative)[0].getReal(), 1.0e-15);
1250         Assertions.assertEquals( 2.0, s1.getAdditionalStateDerivative(valueAndDerivative)[1].getReal(), 1.0e-15);
1251         Assertions.assertEquals( 5.0, s1.getAdditionalState(valueOnly)[0].getReal(),                    1.0e-15);
1252         Assertions.assertEquals( 4.0, s1.getAdditionalState(valueOnly)[1].getReal(),                    1.0e-15);
1253         Assertions.assertEquals( 1.0, s1.getAdditionalStateDerivative(derivativeOnly)[0].getReal(),     1.0e-15);
1254         Assertions.assertEquals(-1.0, s1.getAdditionalStateDerivative(derivativeOnly)[1].getReal(),     1.0e-15);
1255 
1256     }
1257 
1258     private <T extends CalculusFieldElement<T>> void doTestShiftAdditionalDerivativesField(final Field<T> field) {
1259 
1260         T zero = field.getZero();
1261 
1262         // Build orbit
1263         FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, 2000, 1, 1, TimeScalesFactory.getUTC());
1264         FieldOrbit<T> orbit = new FieldKeplerianOrbit<>(zero.add(7.1E6), zero, zero, zero, zero, zero,
1265                                          PositionAngleType.TRUE, FramesFactory.getGCRF(), date0,
1266                                          zero.add(Constants.WGS84_EARTH_MU));
1267 
1268         // Build propagator
1269         FieldKeplerianPropagator<T> propagator = new FieldKeplerianPropagator<>(orbit);
1270 
1271         final String valueAndDerivative = "value-and-derivative";
1272         final String valueOnly          = "value-only";
1273         final String derivativeOnly     = "derivative-only";
1274         final FieldSpacecraftState<T> s0 = propagator.getInitialState().
1275                 addAdditionalData(valueAndDerivative,           convert(field, new double[] { 1.0,  2.0 })).
1276                                            addAdditionalStateDerivative(valueAndDerivative, convert(field, new double[] { 3.0,  2.0 })).
1277                 addAdditionalData(valueOnly,                    convert(field, new double[] { 5.0,  4.0 })).
1278                                            addAdditionalStateDerivative(derivativeOnly,     convert(field, new double[] { 1.0, -1.0 }));
1279         Assertions.assertEquals( 1.0, s0.getAdditionalState(valueAndDerivative)[0].getReal(),           1.0e-15);
1280         Assertions.assertEquals( 2.0, s0.getAdditionalState(valueAndDerivative)[1].getReal(),           1.0e-15);
1281         Assertions.assertEquals( 3.0, s0.getAdditionalStateDerivative(valueAndDerivative)[0].getReal(), 1.0e-15);
1282         Assertions.assertEquals( 2.0, s0.getAdditionalStateDerivative(valueAndDerivative)[1].getReal(), 1.0e-15);
1283         Assertions.assertEquals( 5.0, s0.getAdditionalState(valueOnly)[0].getReal(),                    1.0e-15);
1284         Assertions.assertEquals( 4.0, s0.getAdditionalState(valueOnly)[1].getReal(),                    1.0e-15);
1285         Assertions.assertEquals( 1.0, s0.getAdditionalStateDerivative(derivativeOnly)[0].getReal(),     1.0e-15);
1286         Assertions.assertEquals(-1.0, s0.getAdditionalStateDerivative(derivativeOnly)[1].getReal(),     1.0e-15);
1287         final FieldSpacecraftState<T> s1 = s0.shiftedBy(field.getZero().newInstance(-2.0));
1288         Assertions.assertEquals(-5.0, s1.getAdditionalState(valueAndDerivative)[0].getReal(),           1.0e-15);
1289         Assertions.assertEquals(-2.0, s1.getAdditionalState(valueAndDerivative)[1].getReal(),           1.0e-15);
1290         Assertions.assertEquals( 3.0, s1.getAdditionalStateDerivative(valueAndDerivative)[0].getReal(), 1.0e-15);
1291         Assertions.assertEquals( 2.0, s1.getAdditionalStateDerivative(valueAndDerivative)[1].getReal(), 1.0e-15);
1292         Assertions.assertEquals( 5.0, s1.getAdditionalState(valueOnly)[0].getReal(),                    1.0e-15);
1293         Assertions.assertEquals( 4.0, s1.getAdditionalState(valueOnly)[1].getReal(),                    1.0e-15);
1294         Assertions.assertEquals( 1.0, s1.getAdditionalStateDerivative(derivativeOnly)[0].getReal(),     1.0e-15);
1295         Assertions.assertEquals(-1.0, s1.getAdditionalStateDerivative(derivativeOnly)[1].getReal(),     1.0e-15);
1296 
1297     }
1298 
1299     @Deprecated
1300     @Test
1301     void testDeprecated() {
1302         final Binary64 mass = Binary64.ONE;
1303         final Orbit orbit = TestUtils.getDefaultOrbit(AbsoluteDate.ARBITRARY_EPOCH);
1304         final Binary64Field field = Binary64Field.getInstance();
1305         final FieldOrbit<Binary64> fieldOrbit = new FieldCartesianOrbit<>(field, orbit);
1306         final FieldAbsolutePVCoordinates<Binary64> fieldPV = new FieldAbsolutePVCoordinates<>(orbit.getFrame(), fieldOrbit.getDate(),
1307                 fieldOrbit.getPVCoordinates());
1308         final FieldAttitude<Binary64> fieldAttitude = new FrameAlignedProvider(orbit.getFrame()).getAttitude(fieldOrbit,
1309                 fieldOrbit.getDate(), fieldPV.getFrame());
1310         Assertions.assertEquals(mass, new FieldSpacecraftState<>(fieldOrbit, mass).getMass());
1311         Assertions.assertEquals(mass, new FieldSpacecraftState<>(fieldPV, mass).getMass());
1312         Assertions.assertEquals(mass, new FieldSpacecraftState<>(fieldPV, fieldAttitude, mass).getMass());
1313         Assertions.assertEquals(mass, new FieldSpacecraftState<>(fieldOrbit, fieldAttitude, mass).getMass());
1314     }
1315 
1316     @BeforeEach
1317     public void setUp(){
1318         try {
1319 
1320             Utils.setDataRoot("regular-data");
1321             mu  = 3.9860047e14;
1322 
1323             double a     = 7187990.1979844316;
1324             double e     = 0.5e-4;
1325             double i     = 1.7105407051081795;
1326             double omega = 1.9674147913622104;
1327             double OMEGA = FastMath.toRadians(261);
1328             double lv    =    0;
1329 
1330             rDate = new AbsoluteDate(new DateComponents(2004, 1, 1),
1331                                      TimeComponents.H00,
1332                                      TimeScalesFactory.getUTC());
1333             rOrbit = new KeplerianOrbit(a, e, i, omega, OMEGA, lv, PositionAngleType.TRUE,
1334                                         FramesFactory.getEME2000(), rDate, mu);
1335             earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
1336                                          Constants.WGS84_EARTH_FLATTENING,
1337                                          FramesFactory.getITRF(IERSConventions.IERS_2010, true));
1338         } catch (OrekitException oe) {
1339 
1340             Assertions.fail(oe.getLocalizedMessage());
1341         }
1342     }
1343 
1344     private <T extends CalculusFieldElement<T>> T[] convert(final Field<T> field, final double[] a) {
1345         final T[] converted = MathArrays.buildArray(field, a.length);
1346         for (int i = 0; i < a.length; ++i) {
1347             converted[i] = field.getZero().newInstance(a[i]);
1348         }
1349         return converted;
1350     }
1351 
1352     private AbsoluteDate rDate;
1353     private double mu;
1354     private Orbit rOrbit;
1355     private OneAxisEllipsoid earth;
1356 
1357 }
1358