1   /* Copyright 2002-2024 CS GROUP
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.frames;
18  
19  import java.io.Serializable;
20  
21  import org.hipparchus.CalculusFieldElement;
22  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.hipparchus.util.FastMath;
28  import org.orekit.annotation.DefaultDataContext;
29  import org.orekit.data.DataContext;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitInternalError;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.FieldAbsoluteDate;
34  import org.orekit.time.TimeScalarFunction;
35  import org.orekit.time.TimeScales;
36  import org.orekit.time.TimeVectorFunction;
37  import org.orekit.utils.IERSConventions;
38  
39  /** True Equator Mean Equinox Frame.
40   * <p>This frame is used for the SGP4 model in TLE propagation. This frame has <em>no</em>
41   * official definition and there are some ambiguities about whether it should be used
42   * as "of date" or "of epoch". This frame should therefore be used <em>only</em> for
43   * TLE propagation and not for anything else, as recommended by the CCSDS Orbit Data Message
44   * blue book.</p>
45   * @author Luc Maisonobe
46   */
47  class TEMEProvider implements EOPBasedTransformProvider {
48  
49      /** Serializable UID. */
50      private static final long serialVersionUID = 20131209L;
51  
52      /** Conventions. */
53      private final IERSConventions conventions;
54  
55      /** EOP history. */
56      private final EOPHistory eopHistory;
57  
58      /** Function computing the mean obliquity. */
59      private final transient TimeScalarFunction obliquityFunction;
60  
61      /** Function computing the nutation angles. */
62      private final transient TimeVectorFunction nutationFunction;
63  
64      /**
65       * Simple constructor.
66       *  @param conventions IERS conventions to apply
67       * @param eopHistory  EOP history or {@code null} if no corrections should be
68       *                    applied.
69       * @param timeScales  other time scales used in computing the transform.
70       */
71      TEMEProvider(final IERSConventions conventions,
72                   final EOPHistory eopHistory,
73                   final TimeScales timeScales) {
74          this.conventions       = conventions;
75          this.eopHistory        = eopHistory;
76          this.obliquityFunction = conventions.getMeanObliquityFunction(timeScales);
77          this.nutationFunction  = conventions.getNutationFunction(timeScales);
78      }
79  
80      /**
81       * Private constructor.
82       *
83       * @param conventions       IERS conventions to apply
84       * @param eopHistory        EOP history
85       * @param obliquityFunction to use.
86       * @param nutationFunction  to use.
87       */
88      private TEMEProvider(final IERSConventions conventions,
89                           final EOPHistory eopHistory,
90                           final TimeScalarFunction obliquityFunction,
91                           final TimeVectorFunction nutationFunction) {
92          this.conventions = conventions;
93          this.eopHistory = eopHistory;
94          this.obliquityFunction = obliquityFunction;
95          this.nutationFunction = nutationFunction;
96      }
97  
98      /** {@inheritDoc} */
99      @Override
100     public EOPHistory getEOPHistory() {
101         return eopHistory;
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public TEMEProvider getNonInterpolatingProvider() {
107         return new TEMEProvider(conventions, eopHistory.getEOPHistoryWithoutCachedTidalCorrection(),
108                 obliquityFunction, nutationFunction);
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public Transform getTransform(final AbsoluteDate date) {
114         final double eqe = getEquationOfEquinoxes(date);
115         return new Transform(date, new Rotation(Vector3D.PLUS_K, eqe, RotationConvention.FRAME_TRANSFORM));
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
121         final T eqe = getEquationOfEquinoxes(date);
122         return new FieldTransform<>(date, new FieldRotation<>(FieldVector3D.getPlusK(date.getField()),
123                                                               eqe,
124                                                               RotationConvention.FRAME_TRANSFORM));
125     }
126 
127     /** Get the Equation of the Equinoxes at the current date.
128      * @param  date the date
129      * @return equation of the equinoxes
130      */
131     private double getEquationOfEquinoxes(final AbsoluteDate date) {
132 
133         // compute nutation angles
134         final double[] angles = nutationFunction.value(date);
135 
136         // nutation in longitude
137         double dPsi = angles[0];
138 
139         if (eopHistory != null) {
140             // apply the corrections for the nutation parameters
141             final double[] correction = eopHistory.getEquinoxNutationCorrection(date);
142             dPsi += correction[0];
143         }
144 
145         // mean obliquity of ecliptic
146         final double moe = obliquityFunction.value(date);
147 
148         // original definition of equation of equinoxes
149         final double eqe = dPsi * FastMath.cos(moe);
150 
151         // apply correction if needed
152         return eqe + angles[2];
153 
154     }
155 
156     /** Get the Equation of the Equinoxes at the current date.
157      * @param  date the date
158      * @param <T> type of the field elements
159      * @return equation of the equinoxes
160      */
161     private <T extends CalculusFieldElement<T>> T getEquationOfEquinoxes(final FieldAbsoluteDate<T> date) {
162 
163         // compute nutation angles
164         final T[] angles = nutationFunction.value(date);
165 
166         // nutation in longitude
167         T dPsi = angles[0];
168 
169         if (eopHistory != null) {
170             // apply the corrections for the nutation parameters
171             final T[] correction = eopHistory.getEquinoxNutationCorrection(date);
172             dPsi = dPsi.add(correction[0]);
173         }
174 
175         // mean obliquity of ecliptic
176         final T moe = obliquityFunction.value(date);
177 
178         // original definition of equation of equinoxes
179         final T eqe = dPsi.multiply(moe.cos());
180 
181         // apply correction if needed
182         return eqe.add(angles[2]);
183 
184     }
185 
186     /** Replace the instance with a data transfer object for serialization.
187      * <p>
188      * This intermediate class serializes only the frame key.
189      * </p>
190      * @return data transfer object that will be serialized
191      */
192     @DefaultDataContext
193     private Object writeReplace() {
194         return new DataTransferObject(conventions, eopHistory);
195     }
196 
197     /** Internal class used only for serialization. */
198     @DefaultDataContext
199     private static class DataTransferObject implements Serializable {
200 
201         /** Serializable UID. */
202         private static final long serialVersionUID = 20131209L;
203 
204         /** Conventions. */
205         private final IERSConventions conventions;
206 
207         /** EOP history. */
208         private final EOPHistory eopHistory;
209 
210         /** Simple constructor.
211          * @param conventions IERS conventions to apply
212          * @param eopHistory EOP history
213          */
214         DataTransferObject(final IERSConventions conventions, final EOPHistory eopHistory) {
215             this.conventions = conventions;
216             this.eopHistory  = eopHistory;
217         }
218 
219         /** Replace the deserialized data transfer object with a {@link TEMEProvider}.
220          * @return replacement {@link TEMEProvider}
221          */
222         private Object readResolve() {
223             try {
224                 // retrieve a managed frame
225                 return new TEMEProvider(conventions, eopHistory,
226                         DataContext.getDefault().getTimeScales());
227             } catch (OrekitException oe) {
228                 throw new OrekitInternalError(oe);
229             }
230         }
231 
232     }
233 
234 }