1   /* Copyright 2022-2025 Romain Serra
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.orbits;
18  
19  import org.hipparchus.analysis.differentiation.Gradient;
20  import org.hipparchus.analysis.differentiation.GradientField;
21  import org.hipparchus.complex.Complex;
22  import org.hipparchus.complex.ComplexField;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.hipparchus.util.FastMath;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.Test;
28  import org.junit.jupiter.params.ParameterizedTest;
29  import org.junit.jupiter.params.provider.ValueSource;
30  import org.orekit.frames.FramesFactory;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.utils.DerivativeStateUtils;
33  import org.orekit.utils.FieldPVCoordinates;
34  import org.orekit.utils.PVCoordinates;
35  
36  class KeplerianMotionCartesianUtilityTest {
37  
38      private static final double TOLERANCE_DISTANCE = 1e-13;
39      private static final double TOLERANCE_SPEED = 1e-14;
40  
41      @Test
42      void testPredictPositionVelocityElliptic() {
43          // GIVEN
44          final double mu = 1.;
45          final Vector3D position = new Vector3D(10., 1., 0.);
46          final Vector3D velocity = new Vector3D(0., 0.1, 0.2);
47          // WHEN & THEN
48          for (double dt = -10.; dt <= 10.; dt += 0.1) {
49              final PVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position,
50                      velocity, mu);
51              final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(position, velocity), FramesFactory.getGCRF(),
52                      AbsoluteDate.ARBITRARY_EPOCH, mu);
53              final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(orbit);
54              final PVCoordinates expectedPV = equinoctialOrbit.shiftedBy(dt).getPVCoordinates();
55              comparePV(expectedPV, actualPV);
56          }
57      }
58  
59      @Test
60      void testPredictPositionVelocityHyperbolic() {
61          // GIVEN
62          final double mu = 1.;
63          final Vector3D position = new Vector3D(10., 1., 0.);
64          final Vector3D velocity = new Vector3D(0., 0.1, 1.);
65          // WHEN & THEN
66          for (double dt = -10.; dt <= 10.; dt += 0.1) {
67              final PVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position,
68                      velocity, mu);
69              final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(position, velocity), FramesFactory.getGCRF(),
70                      AbsoluteDate.ARBITRARY_EPOCH, mu);
71              final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(orbit);
72              final PVCoordinates expectedPV = keplerianOrbit.shiftedBy(dt).getPVCoordinates();
73              comparePV(expectedPV, actualPV);
74          }
75      }
76  
77      private void comparePV(final PVCoordinates pvCoordinates, final PVCoordinates otherPVCoordinates) {
78          comparePV(pvCoordinates, otherPVCoordinates, TOLERANCE_DISTANCE, TOLERANCE_SPEED);
79      }
80  
81      private void comparePV(final PVCoordinates pvCoordinates, final PVCoordinates otherPVCoordinates,
82                             final double toleranceDistance, final double toleranceSpeed) {
83          final double expectedValue = 0.;
84          final PVCoordinates relativePV = new PVCoordinates(pvCoordinates, otherPVCoordinates);
85          Assertions.assertEquals(expectedValue, relativePV.getPosition().getNorm(), toleranceDistance);
86          Assertions.assertEquals(expectedValue, relativePV.getVelocity().getNorm(), toleranceSpeed);
87      }
88  
89      @ParameterizedTest
90      @ValueSource(doubles = {0., 1e-20, 1e-15, 1e-10, 1e-8, 1e-5, 1e-2, 1e-1})
91      void testPredictPositionVelocityCircularField(final double speedShift) {
92          // GIVEN
93          final double mu = 1.5;
94          final Vector3D position = new Vector3D(4., 0., 0.);
95          final Vector3D velocity = new Vector3D(0., FastMath.sqrt(mu / position.getNorm()) + speedShift, 0.);
96          final GradientField field = GradientField.getField(6);
97          final Gradient fieldMu = new Gradient(mu, new double[6]);
98          final FieldOrbit<Gradient> fieldOrbit = DerivativeStateUtils.buildOrbitGradient(field,
99                  new CartesianOrbit(new PVCoordinates(position, velocity), FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, mu));
100         final FieldVector3D<Gradient> fieldPosition = fieldOrbit.getPosition();
101         final FieldVector3D<Gradient> fieldVelocity = fieldOrbit.getVelocity();
102         // WHEN & THEN
103         for (double dt = -10.; dt <= 10.; dt += 0.1) {
104             final Gradient fieldDt = new Gradient(dt, new double[6]);
105             final FieldPVCoordinates<Gradient> actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(fieldDt,
106                     fieldPosition, fieldVelocity, fieldMu);
107             final PVCoordinates expectedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position,
108                     velocity, mu);
109             comparePV(expectedPV, actualPV.toPVCoordinates());
110             final FieldOrbit<Gradient> fieldEquinoctialOrbit = OrbitType.EQUINOCTIAL.convertType(fieldOrbit);
111             final FieldOrbit<Gradient> shiftedOrbit = fieldEquinoctialOrbit.shiftedBy(fieldDt);
112             compareDerivatives(shiftedOrbit.getPVCoordinates(), actualPV);
113         }
114     }
115 
116     private static void compareDerivatives(final FieldPVCoordinates<Gradient> expectedPV,
117                                            final FieldPVCoordinates<Gradient> actualPV) {
118 
119         final double toleranceGradientPosition = 1e-6;
120         final double toleranceGradientVelocity = 1e-7;
121         for (int i = 0; i < 6; i++) {
122             Assertions.assertEquals(expectedPV.getPosition().getX().getPartialDerivative(i),
123                     actualPV.getPosition().getX().getPartialDerivative(i), toleranceGradientPosition);
124             Assertions.assertEquals(expectedPV.getPosition().getY().getPartialDerivative(i),
125                     actualPV.getPosition().getY().getPartialDerivative(i), toleranceGradientPosition);
126             Assertions.assertEquals(expectedPV.getPosition().getZ().getPartialDerivative(i),
127                     actualPV.getPosition().getZ().getPartialDerivative(i), toleranceGradientPosition);
128             Assertions.assertEquals(expectedPV.getVelocity().getX().getPartialDerivative(i),
129                     actualPV.getVelocity().getX().getPartialDerivative(i), toleranceGradientVelocity);
130             Assertions.assertEquals(expectedPV.getVelocity().getY().getPartialDerivative(i),
131                     actualPV.getVelocity().getY().getPartialDerivative(i), toleranceGradientVelocity);
132             Assertions.assertEquals(expectedPV.getVelocity().getZ().getPartialDerivative(i),
133                     actualPV.getVelocity().getZ().getPartialDerivative(i), toleranceGradientVelocity);
134         }
135     }
136 
137     @Test
138     void testPredictPositionVelocityEllipticField() {
139         // GIVEN
140         final ComplexField field = ComplexField.getInstance();
141         final double mu = 1.;
142         final Vector3D position = new Vector3D(6., 0., 1.);
143         final Vector3D velocity = new Vector3D(0.01, 0.1, 0.);
144         final Complex fieldMu = new Complex(mu);
145         final FieldVector3D<Complex> fieldPosition = new FieldVector3D<>(field, position);
146         final FieldVector3D<Complex> fieldVelocity = new FieldVector3D<>(field, velocity);
147         // WHEN & THEN
148         for (double dt = -10.; dt <= 10.; dt += 0.1) {
149             final FieldPVCoordinates<Complex> actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(new Complex(dt),
150                     fieldPosition, fieldVelocity, fieldMu);
151             final PVCoordinates expectedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position,
152                     velocity, mu);
153             comparePV(expectedPV, actualPV.toPVCoordinates());
154         }
155     }
156 
157     @Test
158     void testPredictPositionVelocityHyperbolicField() {
159         // GIVEN
160         final ComplexField field = ComplexField.getInstance();
161         final double mu = 1.;
162         final Vector3D position = new Vector3D(6., 0., 2.);
163         final Vector3D velocity = new Vector3D(0.01, 1, 0.);
164         final Complex fieldMu = new Complex(mu);
165         final FieldVector3D<Complex> fieldPosition = new FieldVector3D<>(field, position);
166         final FieldVector3D<Complex> fieldVelocity = new FieldVector3D<>(field, velocity);
167         // WHEN & THEN
168         for (double dt = -10.; dt <= 10.; dt += 0.1) {
169             final FieldPVCoordinates<Complex> actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(new Complex(dt),
170                     fieldPosition, fieldVelocity, fieldMu);
171             final PVCoordinates expectedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position,
172                     velocity, mu);
173             comparePV(expectedPV, actualPV.toPVCoordinates());
174         }
175     }
176 
177 }