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