1   /* Copyright 2002-2025 Airbus Defence and Space
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    * Airbus Defence and Space 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.analytical.intelsat;
18  
19  import java.util.Collections;
20  import java.util.List;
21  import org.hipparchus.CalculusFieldElement;
22  import org.hipparchus.Field;
23  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.util.FastMath;
26  import org.hipparchus.util.FieldSinCos;
27  import org.orekit.annotation.DefaultDataContext;
28  import org.orekit.attitudes.AttitudeProvider;
29  import org.orekit.attitudes.FieldAttitude;
30  import org.orekit.attitudes.FrameAlignedProvider;
31  import org.orekit.data.DataContext;
32  import org.orekit.errors.OrekitException;
33  import org.orekit.errors.OrekitMessages;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.orbits.FieldCartesianOrbit;
37  import org.orekit.orbits.FieldOrbit;
38  import org.orekit.propagation.FieldSpacecraftState;
39  import org.orekit.propagation.Propagator;
40  import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator;
41  import org.orekit.time.FieldAbsoluteDate;
42  import org.orekit.utils.Constants;
43  import org.orekit.utils.FieldPVCoordinates;
44  import org.orekit.utils.IERSConventions;
45  import org.orekit.utils.ParameterDriver;
46  import org.orekit.utils.units.Unit;
47  
48  /**
49   * This class provides elements to propagate Intelsat's 11 elements.
50   * <p>
51   * Intelsat's 11 elements propagation is defined in ITU-R S.1525 standard.
52   * </p>
53   *
54   * @author Bryan Cazabonne
55   * @since 12.1
56   */
57  public class FieldIntelsatElevenElementsPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractAnalyticalPropagator<T> {
58  
59      /**
60       * Intelsat's 11 elements.
61       */
62      private final FieldIntelsatElevenElements<T> elements;
63  
64      /**
65       * Inertial frame for the output orbit.
66       */
67      private final Frame inertialFrame;
68  
69      /**
70       * ECEF frame related to the Intelsat's 11 elements.
71       */
72      private final Frame ecefFrame;
73  
74      /**
75       * Spacecraft mass in kilograms.
76       */
77      private final T mass;
78  
79      /**
80       * Compute spacecraft's east longitude.
81       */
82      private FieldUnivariateDerivative2<T> eastLongitudeDegrees;
83  
84      /**
85       * Compute spacecraft's geocentric latitude.
86       */
87      private FieldUnivariateDerivative2<T> geocentricLatitudeDegrees;
88  
89      /**
90       * Compute spacecraft's orbit radius.
91       */
92      private FieldUnivariateDerivative2<T> orbitRadius;
93  
94      /**
95       * Default constructor.
96       * <p>
97       * This constructor uses the {@link DataContext#getDefault() default data context}.
98       * </p>
99       * <p> The attitude provider is set by default to be aligned with the inertial frame.<br>
100      * The mass is set by default to the
101      * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.<br>
102      * The inertial frame is set by default to the
103      * {@link org.orekit.frames.Predefined#TOD_CONVENTIONS_2010_SIMPLE_EOP TOD frame} in the default data
104      * context.<br>
105      * The ECEF frame is set by default to the
106      * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP
107      * CIO/2010-based ITRF simple EOP} in the default data context.
108      * </p>
109      *
110      * @param elements Intelsat's 11 elements
111      */
112     @DefaultDataContext
113     public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements<T> elements) {
114         this(elements, FramesFactory.getTOD(IERSConventions.IERS_2010, true), FramesFactory.getITRF(IERSConventions.IERS_2010, true));
115     }
116 
117     /**
118      * Constructor.
119      *
120      * <p> The attitude provider is set by default to be aligned with the inertial frame.<br>
121      * The mass is set by default to the
122      * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.<br>
123      * </p>
124      *
125      * @param elements      Intelsat's 11 elements
126      * @param inertialFrame inertial frame for the output orbit
127      * @param ecefFrame     ECEF frame related to the Intelsat's 11 elements
128      */
129     public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements<T> elements, final Frame inertialFrame, final Frame ecefFrame) {
130         this(elements, inertialFrame, ecefFrame, FrameAlignedProvider.of(inertialFrame), elements.getEpoch().getField().getZero().add(Propagator.DEFAULT_MASS));
131     }
132 
133     /**
134      * Constructor.
135      *
136      * @param elements         Intelsat's 11 elements
137      * @param inertialFrame    inertial frame for the output orbit
138      * @param ecefFrame        ECEF frame related to the Intelsat's 11 elements
139      * @param attitudeProvider attitude provider
140      * @param mass             spacecraft mass
141      */
142     public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements<T> elements, final Frame inertialFrame, final Frame ecefFrame,
143                                                  final AttitudeProvider attitudeProvider, final T mass) {
144         super(elements.getEpoch().getField(), attitudeProvider);
145         this.elements = elements;
146         this.inertialFrame = inertialFrame;
147         this.ecefFrame = ecefFrame;
148         this.mass = mass;
149         setStartDate(elements.getEpoch());
150         final FieldOrbit<T> orbitAtElementsDate = propagateOrbit(elements.getEpoch(), getParameters(elements.getEpoch().getField()));
151         final FieldAttitude<T> attitude = attitudeProvider.getAttitude(orbitAtElementsDate, elements.getEpoch(), inertialFrame);
152         super.resetInitialState(new FieldSpacecraftState<>(orbitAtElementsDate, attitude).withMass(mass));
153     }
154 
155     /**
156      * Converts the Intelsat's 11 elements into Position/Velocity coordinates in ECEF.
157      *
158      * @param date computation epoch
159      * @return Position/Velocity coordinates in ECEF
160      */
161     public FieldPVCoordinates<T> propagateInEcef(final FieldAbsoluteDate<T> date) {
162         final Field<T> field = date.getField();
163         final FieldUnivariateDerivative2<T> tDays = new FieldUnivariateDerivative2<>(date.durationFrom(elements.getEpoch()), field.getOne(), field.getZero()).divide(
164                 Constants.JULIAN_DAY);
165         final T wDegreesPerDay = elements.getLm1().add(IntelsatElevenElements.DRIFT_RATE_SHIFT_DEG_PER_DAY);
166         final FieldUnivariateDerivative2<T> wt = FastMath.toRadians(tDays.multiply(wDegreesPerDay));
167         final FieldSinCos<FieldUnivariateDerivative2<T>> scWt = FastMath.sinCos(wt);
168         final FieldSinCos<FieldUnivariateDerivative2<T>> sc2Wt = FastMath.sinCos(wt.multiply(2.0));
169         final FieldUnivariateDerivative2<T> satelliteEastLongitudeDegrees = computeSatelliteEastLongitudeDegrees(tDays, scWt, sc2Wt);
170         final FieldUnivariateDerivative2<T> satelliteGeocentricLatitudeDegrees = computeSatelliteGeocentricLatitudeDegrees(tDays, scWt);
171         final FieldUnivariateDerivative2<T> satelliteRadius = computeSatelliteRadiusKilometers(wDegreesPerDay, scWt).multiply(Unit.KILOMETRE.getScale());
172         this.eastLongitudeDegrees = satelliteEastLongitudeDegrees;
173         this.geocentricLatitudeDegrees = satelliteGeocentricLatitudeDegrees;
174         this.orbitRadius = satelliteRadius;
175         final FieldSinCos<FieldUnivariateDerivative2<T>> scLongitude = FastMath.sinCos(FastMath.toRadians(satelliteEastLongitudeDegrees));
176         final FieldSinCos<FieldUnivariateDerivative2<T>> scLatitude = FastMath.sinCos(FastMath.toRadians(satelliteGeocentricLatitudeDegrees));
177         final FieldVector3D<FieldUnivariateDerivative2<T>> positionWithDerivatives = new FieldVector3D<>(satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.cos()),
178                                                                                                          satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.sin()),
179                                                                                                          satelliteRadius.multiply(scLatitude.sin()));
180         return new FieldPVCoordinates<>(new FieldVector3D<>(positionWithDerivatives.getX().getValue(), //
181                                                             positionWithDerivatives.getY().getValue(), //
182                                                             positionWithDerivatives.getZ().getValue()), //
183                                         new FieldVector3D<>(positionWithDerivatives.getX().getFirstDerivative(), //
184                                                             positionWithDerivatives.getY().getFirstDerivative(), //
185                                                             positionWithDerivatives.getZ().getFirstDerivative()), //
186                                         new FieldVector3D<>(positionWithDerivatives.getX().getSecondDerivative(), //
187                                                             positionWithDerivatives.getY().getSecondDerivative(), //
188                                                             positionWithDerivatives.getZ().getSecondDerivative()));
189     }
190 
191     /**
192      * {@inheritDoc}.
193      */
194     @Override
195     public void resetInitialState(final FieldSpacecraftState<T> state) {
196         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
197     }
198 
199     /**
200      * {@inheritDoc}.
201      */
202     @Override
203     protected T getMass(final FieldAbsoluteDate<T> date) {
204         return mass;
205     }
206 
207     /**
208      * {@inheritDoc}.
209      */
210     @Override
211     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
212         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
213     }
214 
215     /**
216      * {@inheritDoc}.
217      */
218     @Override
219     public FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date,
220                                         final T[] parameters) {
221         return new FieldCartesianOrbit<>(ecefFrame.getTransformTo(inertialFrame, date).transformPVCoordinates(propagateInEcef(date)), inertialFrame, date,
222                                          date.getField().getZero().add(Constants.WGS84_EARTH_MU));
223     }
224 
225     /**
226      * Computes the satellite's east longitude.
227      *
228      * @param tDays delta time in days
229      * @param scW   sin/cos of the W angle
230      * @param sc2W  sin/cos of the 2xW angle
231      * @return the satellite's east longitude in degrees
232      */
233     private FieldUnivariateDerivative2<T> computeSatelliteEastLongitudeDegrees(final FieldUnivariateDerivative2<T> tDays, final FieldSinCos<FieldUnivariateDerivative2<T>> scW,
234                                                                                final FieldSinCos<FieldUnivariateDerivative2<T>> sc2W) {
235         final FieldUnivariateDerivative2<T> longitude = tDays.multiply(tDays).multiply(elements.getLm2()) //
236                                                              .add(tDays.multiply(elements.getLm1())) //
237                                                              .add(elements.getLm0());
238         final FieldUnivariateDerivative2<T> cosineLongitudeTerm = scW.cos().multiply(tDays.multiply(elements.getLonC1()).add(elements.getLonC()));
239         final FieldUnivariateDerivative2<T> sineLongitudeTerm = scW.sin().multiply(tDays.multiply(elements.getLonS1()).add(elements.getLonS()));
240         final FieldUnivariateDerivative2<T> latitudeTerm = sc2W.sin()
241                                                                .multiply(elements.getLatC()
242                                                                                  .multiply(elements.getLatC())
243                                                                                  .subtract(elements.getLatS().multiply(elements.getLatS()))
244                                                                                  .multiply(0.5)) //
245                                                                .subtract(sc2W.cos().multiply(elements.getLatC().multiply(elements.getLatS()))) //
246                                                                .multiply(IntelsatElevenElements.K);
247         return longitude.add(cosineLongitudeTerm).add(sineLongitudeTerm).add(latitudeTerm);
248     }
249 
250     /**
251      * Computes the satellite's geocentric latitude.
252      *
253      * @param tDays delta time in days
254      * @param scW   sin/cos of the W angle
255      * @return he satellite geocentric latitude in degrees
256      */
257     private FieldUnivariateDerivative2<T> computeSatelliteGeocentricLatitudeDegrees(final FieldUnivariateDerivative2<T> tDays,
258                                                                                     final FieldSinCos<FieldUnivariateDerivative2<T>> scW) {
259         final FieldUnivariateDerivative2<T> cosineTerm = scW.cos().multiply(tDays.multiply(elements.getLatC1()).add(elements.getLatC()));
260         final FieldUnivariateDerivative2<T> sineTerm = scW.sin().multiply(tDays.multiply(elements.getLatS1()).add(elements.getLatS()));
261         return cosineTerm.add(sineTerm);
262     }
263 
264     /**
265      * Computes the satellite's orbit radius.
266      *
267      * @param wDegreesPerDay W angle in degrees/day
268      * @param scW            sin/cos of the W angle
269      * @return the satellite's orbit radius in kilometers
270      */
271     private FieldUnivariateDerivative2<T> computeSatelliteRadiusKilometers(final T wDegreesPerDay, final FieldSinCos<FieldUnivariateDerivative2<T>> scW) {
272         final T coefficient = elements.getLm1()
273                                       .multiply(2.0)
274                                       .divide(wDegreesPerDay.subtract(elements.getLm1()).multiply(3.0))
275                                       .negate()
276                                       .add(1.0)
277                                       .multiply(IntelsatElevenElements.SYNCHRONOUS_RADIUS_KM);
278         return scW.sin()
279                   .multiply(elements.getLonC().multiply(IntelsatElevenElements.K))
280                   .add(1.0)
281                   .subtract(scW.cos().multiply(elements.getLonS().multiply(IntelsatElevenElements.K)))
282                   .multiply(coefficient);
283     }
284 
285     /**
286      * Get the computed satellite's east longitude.
287      *
288      * @return the satellite's east longitude in degrees
289      */
290     public FieldUnivariateDerivative2<T> getEastLongitudeDegrees() {
291         return eastLongitudeDegrees;
292     }
293 
294     /**
295      * Get the computed satellite's geocentric latitude.
296      *
297      * @return the satellite's geocentric latitude in degrees
298      */
299     public FieldUnivariateDerivative2<T> getGeocentricLatitudeDegrees() {
300         return geocentricLatitudeDegrees;
301     }
302 
303     /**
304      * Get the computed satellite's orbit.
305      *
306      * @return satellite's orbit radius in meters
307      */
308     public FieldUnivariateDerivative2<T> getOrbitRadius() {
309         return orbitRadius;
310     }
311 
312     /**
313      * {@inheritDoc}.
314      */
315     @Override
316     public Frame getFrame() {
317         return inertialFrame;
318     }
319 
320     /**
321      * {@inheritDoc}.
322      */
323     @Override
324     public List<ParameterDriver> getParametersDrivers() {
325         return Collections.emptyList();
326     }
327 
328     /**
329      * Get the Intelsat's 11 elements used by the propagator.
330      *
331      * @return the Intelsat's 11 elements used by the propagator
332      */
333     public FieldIntelsatElevenElements<T> getIntelsatElevenElements() {
334         return elements;
335     }
336 }