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.Rotation;
24  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
25  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
26  import org.orekit.annotation.DefaultDataContext;
27  import org.orekit.data.DataContext;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.errors.OrekitInternalError;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  import org.orekit.time.TimeScalarFunction;
33  import org.orekit.time.TimeScales;
34  import org.orekit.time.TimeVectorFunction;
35  import org.orekit.utils.IERSConventions;
36  
37  /** Provider for True of Date (ToD) frame.
38   * <p>This frame handles nutation effects according to selected IERS conventions.</p>
39   * <p>Transform is computed with reference to the {@link MODProvider Mean of Date} frame.</p>
40   * @author Pascal Parraud
41   */
42  class TODProvider implements EOPBasedTransformProvider {
43  
44      /** Serializable UID. */
45      private static final long serialVersionUID = 20131209L;
46  
47      /** Conventions. */
48      private final IERSConventions conventions;
49  
50      /** EOP history. */
51      private final EOPHistory eopHistory;
52  
53      /** Function computing the mean obliquity. */
54      private final transient TimeScalarFunction obliquityFunction;
55  
56      /** Function computing the nutation angles. */
57      private final transient TimeVectorFunction nutationFunction;
58  
59  
60      /**
61       * Simple constructor.
62       *  @param conventions IERS conventions to apply
63       * @param eopHistory  EOP history, or {@code null} if no correction should be
64       *                    applied.
65       * @param timeScales         TAI time scale.
66       */
67      TODProvider(final IERSConventions conventions,
68                  final EOPHistory eopHistory,
69                  final TimeScales timeScales) {
70          this.conventions       = conventions;
71          this.eopHistory        = eopHistory;
72          this.obliquityFunction = conventions.getMeanObliquityFunction(timeScales);
73          this.nutationFunction  =
74                  conventions.getNutationFunction(timeScales);
75      }
76  
77      /**
78       * Private constructor.
79       *
80       * @param conventions       IERS conventions to use.
81       * @param eopHistory        or {@code null} if no correction should be applied.
82       * @param obliquityFunction to use.
83       * @param nutationFunction  to use.
84       */
85      private TODProvider(final IERSConventions conventions,
86                          final EOPHistory eopHistory,
87                          final TimeScalarFunction obliquityFunction,
88                          final TimeVectorFunction nutationFunction) {
89          this.conventions = conventions;
90          this.eopHistory = eopHistory;
91          this.obliquityFunction = obliquityFunction;
92          this.nutationFunction = nutationFunction;
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public EOPHistory getEOPHistory() {
98          return eopHistory;
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     public TODProvider getNonInterpolatingProvider() {
104         return new TODProvider(conventions, eopHistory.getEOPHistoryWithoutCachedTidalCorrection(),
105                 obliquityFunction, nutationFunction);
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public Transform getTransform(final AbsoluteDate date) {
111 
112         // compute nutation angles
113         final double[] angles = nutationFunction.value(date);
114 
115         // compute the mean obliquity of the ecliptic
116         final double moe = obliquityFunction.value(date);
117 
118         double dpsi = angles[0];
119         double deps = angles[1];
120         if (eopHistory != null) {
121             // apply the corrections for the nutation parameters
122             final double[] correction = eopHistory.getEquinoxNutationCorrection(date);
123             dpsi += correction[0];
124             deps += correction[1];
125         }
126 
127         // compute the true obliquity of the ecliptic
128         final double toe = moe + deps;
129 
130         // complete nutation
131         final Rotation nutation = new Rotation(RotationOrder.XZX, RotationConvention.FRAME_TRANSFORM,
132                                                moe, -dpsi, -toe);
133 
134         // set up the transform from parent MOD
135         return new Transform(date, nutation);
136 
137     }
138 
139     /** Replace the instance with a data transfer object for serialization.
140     /** {@inheritDoc} */
141     @Override
142     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
143 
144         // compute nutation angles
145         final T[] angles = nutationFunction.value(date);
146 
147         // compute the mean obliquity of the ecliptic
148         final T moe = obliquityFunction.value(date);
149 
150         T dpsi = angles[0];
151         T deps = angles[1];
152         if (eopHistory != null) {
153             // apply the corrections for the nutation parameters
154             final T[] correction = eopHistory.getEquinoxNutationCorrection(date);
155             dpsi = dpsi.add(correction[0]);
156             deps = deps.add(correction[1]);
157         }
158 
159         // compute the true obliquity of the ecliptic
160         final T toe = moe.add(deps);
161 
162         // complete nutation
163         final FieldRotation<T> nutation = new FieldRotation<>(RotationOrder.XZX, RotationConvention.FRAME_TRANSFORM,
164                                                               moe, dpsi.negate(), toe.negate());
165 
166         // set up the transform from parent MOD
167         return new FieldTransform<>(date, nutation);
168 
169     }
170 
171     /** Replace the instance with a data transfer object for serialization.
172      * <p>
173      * This intermediate class serializes only the frame key.
174      * </p>
175      * @return data transfer object that will be serialized
176      */
177     @DefaultDataContext
178     private Object writeReplace() {
179         return new DataTransferObject(conventions, eopHistory);
180     }
181 
182     /** Internal class used only for serialization. */
183     @DefaultDataContext
184     private static class DataTransferObject implements Serializable {
185 
186         /** Serializable UID. */
187         private static final long serialVersionUID = 20131209L;
188 
189         /** Conventions. */
190         private final IERSConventions conventions;
191 
192         /** EOP history. */
193         private final EOPHistory eopHistory;
194 
195         /** Simple constructor.
196          * @param conventions IERS conventions to apply
197          * @param eopHistory EOP history
198          */
199         DataTransferObject(final IERSConventions conventions, final EOPHistory eopHistory) {
200             this.conventions = conventions;
201             this.eopHistory  = eopHistory;
202         }
203 
204         /** Replace the deserialized data transfer object with a {@link TODProvider}.
205          * @return replacement {@link TODProvider}
206          */
207         private Object readResolve() {
208             try {
209                 // retrieve a managed frame
210                 return new TODProvider(conventions, eopHistory,
211                         DataContext.getDefault().getTimeScales());
212             } catch (OrekitException oe) {
213                 throw new OrekitInternalError(oe);
214             }
215         }
216 
217     }
218 
219 }