1   /* Contributed in the public domain.
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.forces.gravity;
18  
19  import org.hipparchus.Field;
20  import org.hipparchus.analysis.differentiation.DSFactory;
21  import org.hipparchus.analysis.differentiation.DerivativeStructure;
22  import org.hipparchus.analysis.differentiation.Gradient;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.hipparchus.ode.AbstractIntegrator;
26  import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator;
27  import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
28  import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
29  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
30  import org.hipparchus.util.FastMath;
31  import org.junit.jupiter.api.Assertions;
32  import org.junit.jupiter.api.BeforeAll;
33  import org.junit.jupiter.api.Test;
34  import org.orekit.Utils;
35  import org.orekit.attitudes.LofOffset;
36  import org.orekit.forces.AbstractLegacyForceModelTest;
37  import org.orekit.forces.ForceModel;
38  import org.orekit.frames.Frame;
39  import org.orekit.frames.FramesFactory;
40  import org.orekit.frames.LOFType;
41  import org.orekit.orbits.CartesianOrbit;
42  import org.orekit.orbits.CircularOrbit;
43  import org.orekit.orbits.FieldKeplerianOrbit;
44  import org.orekit.orbits.KeplerianOrbit;
45  import org.orekit.orbits.Orbit;
46  import org.orekit.orbits.OrbitType;
47  import org.orekit.orbits.PositionAngleType;
48  import org.orekit.propagation.FieldSpacecraftState;
49  import org.orekit.propagation.SpacecraftState;
50  import org.orekit.propagation.numerical.FieldNumericalPropagator;
51  import org.orekit.propagation.numerical.NumericalPropagator;
52  import org.orekit.time.AbsoluteDate;
53  import org.orekit.time.FieldAbsoluteDate;
54  import org.orekit.utils.Constants;
55  import org.orekit.utils.FieldPVCoordinates;
56  import org.orekit.utils.PVCoordinates;
57  
58  /** Unit tests for {@link Relativity}. */
59  public class RelativityTest extends AbstractLegacyForceModelTest {
60  
61      /** speed of light */
62      private static final double c = Constants.SPEED_OF_LIGHT;
63      /** arbitrary date */
64      private static final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
65      /** inertial frame */
66      private static final Frame frame = FramesFactory.getGCRF();
67  
68      @Override
69      protected FieldVector3D<DerivativeStructure> accelerationDerivatives(final ForceModel forceModel,
70                                                                           final FieldSpacecraftState<DerivativeStructure> state)
71          {
72          try {
73              final FieldVector3D<DerivativeStructure> position = state.getPVCoordinates().getPosition();
74              final FieldVector3D<DerivativeStructure> velocity = state.getPVCoordinates().getVelocity();
75              double gm = forceModel.
76                          getParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT).
77                          getValue(state.getDate().toAbsoluteDate());
78              //radius
79              final DerivativeStructure r2 = position.getNormSq();
80              final DerivativeStructure r = r2.sqrt();
81              //speed squared
82              final DerivativeStructure s2 = velocity.getNormSq();
83              final double c2 = Constants.SPEED_OF_LIGHT * Constants.SPEED_OF_LIGHT;
84              //eq. 3.146
85              return new FieldVector3D<>(r.reciprocal().multiply(4 * gm).subtract(s2),
86                                         position,
87                                         position.dotProduct(velocity).multiply(4),
88                                         velocity).scalarMultiply(r2.multiply(r).multiply(c2).reciprocal().multiply(gm));
89  
90          } catch (IllegalArgumentException | SecurityException e) {
91              return null;
92          }
93      }
94  
95      @Override
96      protected FieldVector3D<Gradient> accelerationDerivativesGradient(final ForceModel forceModel,
97                                                                        final FieldSpacecraftState<Gradient> state)
98          {
99          try {
100             final FieldVector3D<Gradient> position = state.getPVCoordinates().getPosition();
101             final FieldVector3D<Gradient> velocity = state.getPVCoordinates().getVelocity();
102             double gm = forceModel.
103                         getParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT).
104                         getValue(state.getDate().toAbsoluteDate());
105             //radius
106             final Gradient r2 = position.getNormSq();
107             final Gradient r = r2.sqrt();
108             //speed squared
109             final Gradient s2 = velocity.getNormSq();
110             final double c2 = Constants.SPEED_OF_LIGHT * Constants.SPEED_OF_LIGHT;
111             //eq. 3.146
112             return new FieldVector3D<>(r.reciprocal().multiply(4 * gm).subtract(s2),
113                                        position,
114                                        position.dotProduct(velocity).multiply(4),
115                                        velocity).scalarMultiply(r2.multiply(r).multiply(c2).reciprocal().multiply(gm));
116 
117         } catch (IllegalArgumentException | SecurityException e) {
118             return null;
119         }
120     }
121 
122     /** set orekit data */
123     @BeforeAll
124     public static void setUpBefore() {
125         Utils.setDataRoot("regular-data");
126     }
127 
128     /**
129      * check the acceleration from relativity
130      */
131     @Test
132     public void testAcceleration() {
133         double gm = Constants.EIGEN5C_EARTH_MU;
134         Relativity relativity = new Relativity(gm);
135         Assertions.assertFalse(relativity.dependsOnPositionOnly());
136         final Vector3D p = new Vector3D(3777828.75000531, -5543949.549783845, 2563117.448578311);
137         final Vector3D v = new Vector3D(489.0060271721, -2849.9328929417, -6866.4671013153);
138         SpacecraftState s = new SpacecraftState(new CartesianOrbit(
139                 new PVCoordinates(p, v),
140                 frame,
141                 date,
142                 gm
143         ));
144 
145         //action
146         Vector3D acceleration = relativity.acceleration(s, relativity.getParameters(s.getDate()));
147 
148         //verify
149         //force is ~1e-8 so this give ~3 sig figs.
150         double tol = 2e-11;
151         Vector3D circularApproximation = p.normalize().scalarMultiply(
152                 gm / p.getNormSq() * 3 * v.getNormSq() / (c * c));
153         Assertions.assertEquals(
154                 0,
155                 acceleration.subtract(circularApproximation).getNorm(),
156                 tol);
157         //check derivatives
158         FieldSpacecraftState<DerivativeStructure> sDS = toDS(s, new LofOffset(s.getFrame(), LOFType.LVLH_CCSDS));
159         final Vector3D actualDerivatives = relativity
160                 .acceleration(sDS, relativity.getParameters(sDS.getDate().getField(), sDS.getDate()))
161                 .toVector3D();
162         Assertions.assertEquals(
163                 0,
164                 actualDerivatives.subtract(circularApproximation).getNorm(),
165                 tol);
166     }
167 
168     @Test
169     public void testJacobianVs80Implementation() {
170         double gm = Constants.EIGEN5C_EARTH_MU;
171         Relativity relativity = new Relativity(gm);
172         final Vector3D p = new Vector3D(3777828.75000531, -5543949.549783845, 2563117.448578311);
173         final Vector3D v = new Vector3D(489.0060271721, -2849.9328929417, -6866.4671013153);
174         SpacecraftState s = new SpacecraftState(new CartesianOrbit(
175                 new PVCoordinates(p, v),
176                 frame,
177                 date,
178                 gm
179         ));
180 
181         checkStateJacobianVs80Implementation(s, relativity,
182                                              new LofOffset(s.getFrame(), LOFType.LVLH_CCSDS),
183                                              1.0e-50, false);
184     }
185 
186     @Test
187     public void testJacobianVs80ImplementationGradient() {
188         double gm = Constants.EIGEN5C_EARTH_MU;
189         Relativity relativity = new Relativity(gm);
190         final Vector3D p = new Vector3D(3777828.75000531, -5543949.549783845, 2563117.448578311);
191         final Vector3D v = new Vector3D(489.0060271721, -2849.9328929417, -6866.4671013153);
192         SpacecraftState s = new SpacecraftState(new CartesianOrbit(
193                 new PVCoordinates(p, v),
194                 frame,
195                 date,
196                 gm
197         ));
198 
199         checkStateJacobianVs80ImplementationGradient(s, relativity,
200                                              new LofOffset(s.getFrame(), LOFType.LVLH_CCSDS),
201                                              1.0e-50, false);
202     }
203 
204     /**
205      * Check a nearly circular orbit.
206      */
207     @Test
208     public void testAccelerationCircular() {
209         double gm = Constants.EIGEN5C_EARTH_MU;
210         double re = Constants.WGS84_EARTH_EQUATORIAL_RADIUS;
211         Relativity relativity = new Relativity(gm);
212         final CircularOrbit orbit = new CircularOrbit(
213                 re + 500e3, 0, 0, FastMath.toRadians(41.2), -1, 3, PositionAngleType.TRUE,
214                 frame,
215                 date,
216                 gm
217         );
218         SpacecraftState state = new SpacecraftState(orbit);
219 
220         //action
221         Vector3D acceleration = relativity.acceleration(state, relativity.getParameters(state.getDate()));
222 
223         //verify
224         //force is ~1e-8 so this give ~7 sig figs.
225         double tol = 2e-10;
226         PVCoordinates pv = state.getPVCoordinates();
227         Vector3D p = pv.getPosition();
228         Vector3D v = pv.getVelocity();
229         Vector3D circularApproximation = p.normalize().scalarMultiply(
230                 gm / p.getNormSq() * 3 * v.getNormSq() / (c * c));
231         Assertions.assertEquals(
232                 0,
233                 acceleration.subtract(circularApproximation).getNorm(),
234                 tol);
235         //check derivatives
236         FieldSpacecraftState<DerivativeStructure> sDS = toDS(state, new LofOffset(state.getFrame(), LOFType.LVLH_CCSDS));
237         FieldVector3D<DerivativeStructure> gradient =
238                 relativity.acceleration(sDS, relativity.getParameters(sDS.getDate().getField(), sDS.getDate()));
239         Assertions.assertEquals(
240                 0,
241                 gradient.toVector3D().subtract(circularApproximation).getNorm(),
242                 tol);
243         double r = p.getNorm();
244         double s = v.getNorm();
245         final double[] actualdx = gradient.getX().getAllDerivatives();
246         final double x = p.getX();
247         final double vx = v.getX();
248         double expectedDxDx = gm / (c * c * r * r * r * r * r) *
249                 (-13 * x * x * s * s + 3 * r * r * s * s + 4 * r * r * vx * vx);
250         Assertions.assertEquals(expectedDxDx, actualdx[1], 2);
251     }
252 
253     /**Testing if the propagation between the FieldPropagation and the propagation
254      * is equivalent.
255      * Also testing if propagating X+dX with the propagation is equivalent to
256      * propagation X with the FieldPropagation and then applying the taylor
257      * expansion of dX to the result.*/
258     @Test
259     public void RealFieldTest() {
260         DSFactory factory = new DSFactory(6, 5);
261         DerivativeStructure a_0 = factory.variable(0, 7e7);
262         DerivativeStructure e_0 = factory.variable(1, 0.4);
263         DerivativeStructure i_0 = factory.variable(2, 85 * FastMath.PI / 180);
264         DerivativeStructure R_0 = factory.variable(3, 0.7);
265         DerivativeStructure O_0 = factory.variable(4, 0.5);
266         DerivativeStructure n_0 = factory.variable(5, 0.1);
267         DerivativeStructure mu  = factory.constant(Constants.EIGEN5C_EARTH_MU);
268 
269         Field<DerivativeStructure> field = a_0.getField();
270 
271         FieldAbsoluteDate<DerivativeStructure> J2000 = new FieldAbsoluteDate<>(field);
272 
273         Frame EME = FramesFactory.getEME2000();
274 
275         FieldKeplerianOrbit<DerivativeStructure> FKO = new FieldKeplerianOrbit<>(a_0, e_0, i_0, R_0, O_0, n_0,
276                                                                                  PositionAngleType.MEAN,
277                                                                                  EME,
278                                                                                  J2000,
279                                                                                  mu);
280 
281         FieldSpacecraftState<DerivativeStructure> initialState = new FieldSpacecraftState<>(FKO);
282 
283         SpacecraftState iSR = initialState.toSpacecraftState();
284         OrbitType type = OrbitType.KEPLERIAN;
285         double[][] tolerance = NumericalPropagator.tolerances(0.001, FKO.toOrbit(), type);
286 
287 
288         AdaptiveStepsizeFieldIntegrator<DerivativeStructure> integrator =
289                         new DormandPrince853FieldIntegrator<>(field, 0.001, 200, tolerance[0], tolerance[1]);
290         integrator.setInitialStepSize(60);
291         AdaptiveStepsizeIntegrator RIntegrator =
292                         new DormandPrince853Integrator(0.001, 200, tolerance[0], tolerance[1]);
293         RIntegrator.setInitialStepSize(60);
294 
295         FieldNumericalPropagator<DerivativeStructure> FNP = new FieldNumericalPropagator<>(field, integrator);
296         FNP.setOrbitType(type);
297         FNP.setInitialState(initialState);
298 
299         NumericalPropagator NP = new NumericalPropagator(RIntegrator);
300         NP.setOrbitType(type);
301         NP.setInitialState(iSR);
302 
303         final Relativity forceModel = new Relativity(Constants.EIGEN5C_EARTH_MU);
304 
305         FNP.addForceModel(forceModel);
306         NP.addForceModel(forceModel);
307 
308         // Do the test
309         checkRealFieldPropagation(FKO, PositionAngleType.MEAN, 1005., NP, FNP,
310                                   1.0e-15, 5.0e-10, 3.0e-11, 3.0e-10,
311                                   1, false);
312     }
313 
314     /**Testing if the propagation between the FieldPropagation and the propagation
315      * is equivalent.
316      * Also testing if propagating X+dX with the propagation is equivalent to
317      * propagation X with the FieldPropagation and then applying the taylor
318      * expansion of dX to the result.*/
319     @Test
320     public void RealFieldGradientTest() {
321 
322         final int freeParameters = 6;
323         Gradient a_0 = Gradient.variable(freeParameters, 0, 7e7);
324         Gradient e_0 = Gradient.variable(freeParameters, 1, 0.4);
325         Gradient i_0 = Gradient.variable(freeParameters, 2, 85 * FastMath.PI / 180);
326         Gradient R_0 = Gradient.variable(freeParameters, 3, 0.7);
327         Gradient O_0 = Gradient.variable(freeParameters, 4, 0.5);
328         Gradient n_0 = Gradient.variable(freeParameters, 5, 0.1);
329         Gradient mu  = Gradient.constant(freeParameters, Constants.EIGEN5C_EARTH_MU);
330 
331         Field<Gradient> field = a_0.getField();
332 
333         FieldAbsoluteDate<Gradient> J2000 = new FieldAbsoluteDate<>(field);
334 
335         Frame EME = FramesFactory.getEME2000();
336 
337         FieldKeplerianOrbit<Gradient> FKO = new FieldKeplerianOrbit<>(a_0, e_0, i_0, R_0, O_0, n_0,
338                                                                       PositionAngleType.MEAN,
339                                                                       EME,
340                                                                       J2000,
341                                                                       mu);
342 
343         FieldSpacecraftState<Gradient> initialState = new FieldSpacecraftState<>(FKO);
344 
345         SpacecraftState iSR = initialState.toSpacecraftState();
346         OrbitType type = OrbitType.KEPLERIAN;
347         double[][] tolerance = NumericalPropagator.tolerances(0.001, FKO.toOrbit(), type);
348 
349 
350         AdaptiveStepsizeFieldIntegrator<Gradient> integrator =
351                         new DormandPrince853FieldIntegrator<>(field, 0.001, 200, tolerance[0], tolerance[1]);
352         integrator.setInitialStepSize(60);
353         AdaptiveStepsizeIntegrator RIntegrator =
354                         new DormandPrince853Integrator(0.001, 200, tolerance[0], tolerance[1]);
355         RIntegrator.setInitialStepSize(60);
356 
357         FieldNumericalPropagator<Gradient> FNP = new FieldNumericalPropagator<>(field, integrator);
358         FNP.setOrbitType(type);
359         FNP.setInitialState(initialState);
360 
361         NumericalPropagator NP = new NumericalPropagator(RIntegrator);
362         NP.setOrbitType(type);
363         NP.setInitialState(iSR);
364 
365         final Relativity forceModel = new Relativity(Constants.EIGEN5C_EARTH_MU);
366 
367         FNP.addForceModel(forceModel);
368         NP.addForceModel(forceModel);
369 
370         // Do the test
371         checkRealFieldPropagationGradient(FKO, PositionAngleType.MEAN, 1005., NP, FNP,
372                                   1.0e-15, 1.3e-2, 2.9e-4, 4.4e-3,
373                                   1, false);
374     }
375 
376     /**Same test as the previous one but not adding the ForceModel to the NumericalPropagator
377         it is a test to validate the previous test.
378         (to test if the ForceModel it's actually
379         doing something in the Propagator and the FieldPropagator)*/
380     @Test
381     public void RealFieldExpectErrorTest() {
382         DSFactory factory = new DSFactory(6, 0);
383         DerivativeStructure a_0 = factory.variable(0, 7e7);
384         DerivativeStructure e_0 = factory.variable(1, 0.4);
385         DerivativeStructure i_0 = factory.variable(2, 85 * FastMath.PI / 180);
386         DerivativeStructure R_0 = factory.variable(3, 0.7);
387         DerivativeStructure O_0 = factory.variable(4, 0.5);
388         DerivativeStructure n_0 = factory.variable(5, 0.1);
389 
390         Field<DerivativeStructure> field = a_0.getField();
391         DerivativeStructure zero = field.getZero();
392 
393         FieldAbsoluteDate<DerivativeStructure> J2000 = new FieldAbsoluteDate<>(field);
394 
395         Frame EME = FramesFactory.getEME2000();
396 
397         FieldKeplerianOrbit<DerivativeStructure> FKO = new FieldKeplerianOrbit<>(a_0, e_0, i_0, R_0, O_0, n_0,
398                                                                                  PositionAngleType.MEAN,
399                                                                                  EME,
400                                                                                  J2000,
401                                                                                  zero.add(Constants.EIGEN5C_EARTH_MU));
402 
403         FieldSpacecraftState<DerivativeStructure> initialState = new FieldSpacecraftState<>(FKO);
404 
405         SpacecraftState iSR = initialState.toSpacecraftState();
406 
407         OrbitType type = OrbitType.KEPLERIAN;
408         double[][] tolerance = NumericalPropagator.tolerances(0.001, FKO.toOrbit(), type);
409 
410 
411         AdaptiveStepsizeFieldIntegrator<DerivativeStructure> integrator =
412                         new DormandPrince853FieldIntegrator<>(field, 0.001, 200, tolerance[0], tolerance[1]);
413         integrator.setInitialStepSize(60);
414         AdaptiveStepsizeIntegrator RIntegrator =
415                         new DormandPrince853Integrator(0.001, 200, tolerance[0], tolerance[1]);
416         RIntegrator.setInitialStepSize(60);
417 
418         FieldNumericalPropagator<DerivativeStructure> FNP = new FieldNumericalPropagator<>(field, integrator);
419         FNP.setOrbitType(type);
420         FNP.setInitialState(initialState);
421 
422         NumericalPropagator NP = new NumericalPropagator(RIntegrator);
423         NP.setOrbitType(type);
424         NP.setInitialState(iSR);
425 
426         final Relativity forceModel = new Relativity(Constants.EIGEN5C_EARTH_MU);
427 
428         FNP.addForceModel(forceModel);
429      //NOT ADDING THE FORCE MODEL TO THE NUMERICAL PROPAGATOR   NP.addForceModel(forceModel);
430 
431         FieldAbsoluteDate<DerivativeStructure> target = J2000.shiftedBy(1000.);
432         FieldSpacecraftState<DerivativeStructure> finalState_DS = FNP.propagate(target);
433         SpacecraftState finalState_R = NP.propagate(target.toAbsoluteDate());
434         FieldPVCoordinates<DerivativeStructure> finPVC_DS = finalState_DS.getPVCoordinates();
435         PVCoordinates finPVC_R = finalState_R.getPVCoordinates();
436 
437         Assertions.assertEquals(0,
438                             Vector3D.distance(finPVC_DS.toPVCoordinates().getPosition(), finPVC_R.getPosition()),
439                             8.0e-13 * finPVC_R.getPosition().getNorm());
440     }
441     /**
442      * check against example in Tapley, Schutz, and Born, p 65-66. They predict a
443      * progression of perigee of 11 arcsec/year. To get the same results we must set the
444      * propagation tolerances to 1e-5.
445      */
446     @Test
447     public void testSmallEffectOnOrbit() {
448         //setup
449         final double gm = Constants.EIGEN5C_EARTH_MU;
450         Orbit orbit =
451                 new KeplerianOrbit(
452                         7500e3, 0.025, FastMath.toRadians(41.2), 0, 0, 0, PositionAngleType.TRUE,
453                         frame,
454                         date,
455                         gm
456                 );
457         double[][] tol = NumericalPropagator.tolerances(0.00001, orbit, OrbitType.CARTESIAN);
458         AbstractIntegrator integrator = new DormandPrince853Integrator(1, 3600, tol[0], tol[1]);
459         NumericalPropagator propagator = new NumericalPropagator(integrator);
460         propagator.setOrbitType(OrbitType.CARTESIAN);
461         propagator.addForceModel(new Relativity(gm));
462         propagator.setInitialState(new SpacecraftState(orbit));
463 
464         //action: propagate a period
465         AbsoluteDate end = orbit.getDate().shiftedBy(30 * Constants.JULIAN_DAY);
466         PVCoordinates actual = propagator.getPVCoordinates(end, frame);
467 
468         //verify
469         KeplerianOrbit endOrbit = new KeplerianOrbit(actual, frame, end, gm);
470         KeplerianOrbit startOrbit = new KeplerianOrbit(orbit);
471         double dp = endOrbit.getPerigeeArgument() - startOrbit.getPerigeeArgument();
472         double dtYears = end.durationFrom(orbit.getDate()) / Constants.JULIAN_YEAR;
473         double dpDeg = FastMath.toDegrees(dp);
474         //change in argument of perigee in arcseconds per year
475         double arcsecPerYear = dpDeg * 3600 / dtYears;
476         Assertions.assertEquals(11, arcsecPerYear, 0.5);
477     }
478 
479     /**
480      * check {@link Relativity#setParameter(String, double)}, and {@link
481      * Relativity#getParameter(String)}
482      */
483     @Test
484     public void testGetSetGM() {
485         //setup
486         Relativity relativity = new Relativity(Constants.EIGEN5C_EARTH_MU);
487 
488         //actions + verify
489         Assertions.assertEquals(
490                 Constants.EIGEN5C_EARTH_MU,
491                 relativity.getParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT).getValue(),
492                 0);
493         relativity.getParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT).setValue(1);
494         Assertions.assertEquals(
495                 1,
496                 relativity.getParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT).getValue(),
497                 0);
498     }
499 
500 }