1   /* Copyright 2002-2024 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, 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     protected FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
220         return new FieldCartesianOrbit<>(ecefFrame.getTransformTo(inertialFrame, date).transformPVCoordinates(propagateInEcef(date)), inertialFrame, date,
221                                          date.getField().getZero().add(Constants.WGS84_EARTH_MU));
222     }
223 
224     /**
225      * Computes the satellite's east longitude.
226      *
227      * @param tDays delta time in days
228      * @param scW   sin/cos of the W angle
229      * @param sc2W  sin/cos of the 2xW angle
230      * @return the satellite's east longitude in degrees
231      */
232     private FieldUnivariateDerivative2<T> computeSatelliteEastLongitudeDegrees(final FieldUnivariateDerivative2<T> tDays, final FieldSinCos<FieldUnivariateDerivative2<T>> scW,
233                                                                                final FieldSinCos<FieldUnivariateDerivative2<T>> sc2W) {
234         final FieldUnivariateDerivative2<T> longitude = tDays.multiply(tDays).multiply(elements.getLm2()) //
235                                                              .add(tDays.multiply(elements.getLm1())) //
236                                                              .add(elements.getLm0());
237         final FieldUnivariateDerivative2<T> cosineLongitudeTerm = scW.cos().multiply(tDays.multiply(elements.getLonC1()).add(elements.getLonC()));
238         final FieldUnivariateDerivative2<T> sineLongitudeTerm = scW.sin().multiply(tDays.multiply(elements.getLonS1()).add(elements.getLonS()));
239         final FieldUnivariateDerivative2<T> latitudeTerm = sc2W.sin()
240                                                                .multiply(elements.getLatC()
241                                                                                  .multiply(elements.getLatC())
242                                                                                  .subtract(elements.getLatS().multiply(elements.getLatS()))
243                                                                                  .multiply(0.5)) //
244                                                                .subtract(sc2W.cos().multiply(elements.getLatC().multiply(elements.getLatS()))) //
245                                                                .multiply(IntelsatElevenElements.K);
246         return longitude.add(cosineLongitudeTerm).add(sineLongitudeTerm).add(latitudeTerm);
247     }
248 
249     /**
250      * Computes the satellite's geocentric latitude.
251      *
252      * @param tDays delta time in days
253      * @param scW   sin/cos of the W angle
254      * @return he satellite geocentric latitude in degrees
255      */
256     private FieldUnivariateDerivative2<T> computeSatelliteGeocentricLatitudeDegrees(final FieldUnivariateDerivative2<T> tDays,
257                                                                                     final FieldSinCos<FieldUnivariateDerivative2<T>> scW) {
258         final FieldUnivariateDerivative2<T> cosineTerm = scW.cos().multiply(tDays.multiply(elements.getLatC1()).add(elements.getLatC()));
259         final FieldUnivariateDerivative2<T> sineTerm = scW.sin().multiply(tDays.multiply(elements.getLatS1()).add(elements.getLatS()));
260         return cosineTerm.add(sineTerm);
261     }
262 
263     /**
264      * Computes the satellite's orbit radius.
265      *
266      * @param wDegreesPerDay W angle in degrees/day
267      * @param scW            sin/cos of the W angle
268      * @return the satellite's orbit radius in kilometers
269      */
270     private FieldUnivariateDerivative2<T> computeSatelliteRadiusKilometers(final T wDegreesPerDay, final FieldSinCos<FieldUnivariateDerivative2<T>> scW) {
271         final T coefficient = elements.getLm1()
272                                       .multiply(2.0)
273                                       .divide(wDegreesPerDay.subtract(elements.getLm1()).multiply(3.0))
274                                       .negate()
275                                       .add(1.0)
276                                       .multiply(IntelsatElevenElements.SYNCHRONOUS_RADIUS_KM);
277         return scW.sin()
278                   .multiply(elements.getLonC().multiply(IntelsatElevenElements.K))
279                   .add(1.0)
280                   .subtract(scW.cos().multiply(elements.getLonS().multiply(IntelsatElevenElements.K)))
281                   .multiply(coefficient);
282     }
283 
284     /**
285      * Get the computed satellite's east longitude.
286      *
287      * @return the satellite's east longitude in degrees
288      */
289     public FieldUnivariateDerivative2<T> getEastLongitudeDegrees() {
290         return eastLongitudeDegrees;
291     }
292 
293     /**
294      * Get the computed satellite's geocentric latitude.
295      *
296      * @return the satellite's geocentric latitude in degrees
297      */
298     public FieldUnivariateDerivative2<T> getGeocentricLatitudeDegrees() {
299         return geocentricLatitudeDegrees;
300     }
301 
302     /**
303      * Get the computed satellite's orbit.
304      *
305      * @return satellite's orbit radius in meters
306      */
307     public FieldUnivariateDerivative2<T> getOrbitRadius() {
308         return orbitRadius;
309     }
310 
311     /**
312      * {@inheritDoc}.
313      */
314     @Override
315     public Frame getFrame() {
316         return inertialFrame;
317     }
318 
319     /**
320      * {@inheritDoc}.
321      */
322     @Override
323     public List<ParameterDriver> getParametersDrivers() {
324         return Collections.emptyList();
325     }
326 
327     /**
328      * Get the Intelsat's 11 elements used by the propagator.
329      *
330      * @return the Intelsat's 11 elements used by the propagator
331      */
332     public FieldIntelsatElevenElements<T> getIntelsatElevenElements() {
333         return elements;
334     }
335 }