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.attitudes;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.GradientField;
22  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
24  import org.hipparchus.complex.ComplexField;
25  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.hipparchus.geometry.euclidean.threed.Rotation;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.hipparchus.util.Binary64Field;
30  import org.junit.jupiter.api.Assertions;
31  import org.junit.jupiter.api.BeforeAll;
32  import org.junit.jupiter.api.Test;
33  import org.orekit.Utils;
34  import org.orekit.annotation.DefaultDataContext;
35  import org.orekit.frames.Frame;
36  import org.orekit.frames.FramesFactory;
37  import org.orekit.orbits.EquinoctialOrbit;
38  import org.orekit.orbits.FieldEquinoctialOrbit;
39  import org.orekit.orbits.PositionAngleType;
40  import org.orekit.time.AbsoluteDate;
41  import org.orekit.time.FieldAbsoluteDate;
42  import org.orekit.utils.Constants;
43  import org.orekit.utils.FieldPVCoordinates;
44  import org.orekit.utils.FieldPVCoordinatesProvider;
45  import org.orekit.utils.IERSConventions;
46  import org.orekit.utils.PVCoordinates;
47  import org.orekit.utils.PVCoordinatesProvider;
48  import org.orekit.utils.TimeStampedFieldPVCoordinates;
49  import org.orekit.utils.TimeStampedPVCoordinates;
50  
51  class GroundPointingTest {
52      
53      private static Frame INERTIAL_FRAME;
54      
55      private static Frame OTHER_INERTIAL_FRAME;
56      
57      private static Frame EARTH_FIXED_FRAME;
58  
59      @BeforeAll
60      @DefaultDataContext
61      public static void setUp() {
62  
63          Utils.setDataRoot("regular-data");
64  
65          INERTIAL_FRAME = FramesFactory.getEME2000();
66          OTHER_INERTIAL_FRAME = FramesFactory.getGCRF();
67          EARTH_FIXED_FRAME = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
68  
69      }
70  
71      private static class TestGroundPointing extends GroundPointing {
72          
73          TestGroundPointing(Frame inertialFrame, Frame bodyFrame) {
74              super(inertialFrame, bodyFrame);
75          }
76          
77          @Override
78          public TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) {
79              return new TimeStampedPVCoordinates(date, PVCoordinates.ZERO);
80          }
81  
82          @Override
83          public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T>
84              getTargetPV(FieldPVCoordinatesProvider<T> pvProv, FieldAbsoluteDate<T> date, Frame frame) {
85              return new TimeStampedFieldPVCoordinates<>(date, FieldPVCoordinates.getZero(date.getField()));
86          }
87          
88      }
89  
90      @Test
91      void templateTestGetRotationSameFrame() {
92          templateTestGetRotation(INERTIAL_FRAME);
93      }
94  
95      @Test
96      void templateTestGetRotationDifferentFrame() {
97          templateTestGetRotation(OTHER_INERTIAL_FRAME);
98      }
99  
100     private void templateTestGetRotation(final Frame frame) {
101         // GIVEN
102         final TestGroundPointing groundPointing = new TestGroundPointing(INERTIAL_FRAME, EARTH_FIXED_FRAME);
103         final EquinoctialOrbit orbit = createPVCoordinatesProvider();
104         // WHEN
105         final Rotation actualRotation = groundPointing.getAttitudeRotation(orbit, orbit.getDate(), frame);
106         // THEN
107         final Attitude attitude = groundPointing.getAttitude(orbit, orbit.getDate(), frame);
108         final Rotation expectedRotation = attitude.getRotation();
109         Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation));
110     }
111     
112     @Test
113     void testGetAttitudeRotationFieldSameFrame() {
114         templateTestGetRotationField(ComplexField.getInstance(), INERTIAL_FRAME);
115     }
116 
117     @Test
118     void testGetAttitudeRotationFieldDifferentFrame() {
119         templateTestGetRotationField(new UnivariateDerivative1(0., 0.).getField(), OTHER_INERTIAL_FRAME);
120     }
121     
122     private <T extends CalculusFieldElement<T>> void templateTestGetRotationField(final Field<T> field,
123                                                                                   final Frame frame) {
124         // GIVEN
125         final TestGroundPointing groundPointing = new TestGroundPointing(INERTIAL_FRAME, EARTH_FIXED_FRAME);
126         final EquinoctialOrbit orbit = createPVCoordinatesProvider();
127         final FieldEquinoctialOrbit<T> fieldOrbit = convertToField(field, orbit);
128         // WHEN
129         final FieldRotation<T> actualRotation = groundPointing.getAttitudeRotation(fieldOrbit, fieldOrbit.getDate(), frame);
130         // THEN
131         final FieldAttitude<T> attitude = groundPointing.getAttitude(fieldOrbit, fieldOrbit.getDate(), frame);
132         final FieldRotation<T> expectedRotation = attitude.getRotation();
133         Assertions.assertEquals(0., Rotation.distance(expectedRotation.toRotation(), actualRotation.toRotation()));
134     }
135 
136     @Test
137     void testGetAttitudeFieldGradient() {
138         templateTestGetAttitudeField(GradientField.getField(1));
139     }
140 
141     @Test
142     void testGetAttitudeFieldUnivariateDerivative2() {
143         templateTestGetAttitudeField(new UnivariateDerivative2(0., 0., 0.).getField());
144     }
145 
146     private <T extends CalculusFieldElement<T>> void templateTestGetAttitudeField(final Field<T> field) {
147         // GIVEN
148         final TestGroundPointing groundPointing = new TestGroundPointing(INERTIAL_FRAME, EARTH_FIXED_FRAME);
149         final EquinoctialOrbit orbit = createPVCoordinatesProvider();
150         final FieldEquinoctialOrbit<T> fieldOrbit = convertToField(field, orbit);
151         // WHEN
152         final Attitude actualAttitude = groundPointing.getAttitude(fieldOrbit, fieldOrbit.getDate(), OTHER_INERTIAL_FRAME).toAttitude();
153         // THEN
154         final Attitude expectedAttitude = groundPointing.getAttitude(orbit, orbit.getDate(), OTHER_INERTIAL_FRAME);
155         Assertions.assertEquals(0., Rotation.distance(expectedAttitude.getRotation(), actualAttitude.getRotation()));
156         Assertions.assertEquals(expectedAttitude.getSpin(), actualAttitude.getSpin());
157         Assertions.assertEquals(expectedAttitude.getRotationAcceleration(), actualAttitude.getRotationAcceleration());
158     }
159 
160     @Test
161     void testGetTargetPosition() {
162         // GIVEN
163         final TestGroundPointing groundPointing = new TestGroundPointing(INERTIAL_FRAME, EARTH_FIXED_FRAME);
164         final EquinoctialOrbit orbit = createPVCoordinatesProvider();
165         // WHEN
166         final Vector3D actualPosition = groundPointing.getTargetPosition(orbit, orbit.getDate(), INERTIAL_FRAME);
167         // THEN
168         final Vector3D expectedPosition = groundPointing.getTargetPV(orbit, orbit.getDate(), INERTIAL_FRAME).
169                 getPosition();
170         Assertions.assertEquals(expectedPosition, actualPosition);
171     }
172 
173     @Test
174     void testGetTargetPositionFieldBinary64() {
175         templateTestGetTargetPositionField(Binary64Field.getInstance());
176     }
177 
178     @Test
179     void testGetTargetPositionFieldComplex() {
180         templateTestGetTargetPositionField(ComplexField.getInstance());
181     }
182 
183     private <T extends CalculusFieldElement<T>> void templateTestGetTargetPositionField(final Field<T> field) {
184         // GIVEN
185         final TestGroundPointing groundPointing = new TestGroundPointing(INERTIAL_FRAME, EARTH_FIXED_FRAME);
186         final EquinoctialOrbit orbit = createPVCoordinatesProvider();
187         final FieldEquinoctialOrbit<T> fieldOrbit = convertToField(field, orbit);
188         // WHEN
189         final FieldVector3D<T> actualPosition = groundPointing.getTargetPosition(fieldOrbit, fieldOrbit.getDate(), INERTIAL_FRAME);
190         // THEN
191         final FieldVector3D<T> expectedPosition = groundPointing.getTargetPV(fieldOrbit, fieldOrbit.getDate(), INERTIAL_FRAME).
192                 getPosition();
193         Assertions.assertEquals(expectedPosition, actualPosition);
194     }
195 
196     private EquinoctialOrbit createPVCoordinatesProvider() {
197         final AbsoluteDate epoch = AbsoluteDate.ARBITRARY_EPOCH;
198         final double semiMajorAxis = 45000.e3;
199         final double mu = Constants.EGM96_EARTH_MU;
200         return new EquinoctialOrbit(semiMajorAxis, 0., 0., 0., 0., 0., PositionAngleType.ECCENTRIC, INERTIAL_FRAME, epoch, mu);
201     }
202 
203     private <T extends CalculusFieldElement<T>> FieldEquinoctialOrbit<T> convertToField(final Field<T> field,
204                                                                                         final EquinoctialOrbit orbit) {
205         final T zero = field.getZero();
206         final T fieldSemiMajorAxis = zero.add(orbit.getA());
207         final FieldAbsoluteDate<T> fieldDate = new FieldAbsoluteDate<>(field, orbit.getDate());
208         final PositionAngleType positionAngleType = PositionAngleType.MEAN;
209         final T fieldAngle = zero.add(orbit.getL(positionAngleType));
210         return new FieldEquinoctialOrbit<>(fieldSemiMajorAxis, zero, zero, zero, zero, fieldAngle,
211                 positionAngleType, orbit.getFrame(), fieldDate, zero.add(orbit.getMu()));
212     }
213 
214 }