1   /* Copyright 2002-2021 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.orekit.annotation.DefaultDataContext;
28  import org.orekit.data.DataContext;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitInternalError;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.time.FieldAbsoluteDate;
33  import org.orekit.time.TimeScalarFunction;
34  import org.orekit.time.TimeScale;
35  import org.orekit.time.TimeScales;
36  import org.orekit.utils.Constants;
37  import org.orekit.utils.IERSConventions;
38  
39  /** Greenwich True Of Date Frame, also known as True of Date Rotating frame (TDR)
40   * or Greenwich Rotating Coordinate frame (GCR).
41   * <p> This frame handles the sidereal time according to IAU-82 model.</p>
42   * <p> Its parent frame is the {@link TODProvider}.</p>
43   * <p> The pole motion is not applied here.</p>
44   * @author Pascal Parraud
45   * @author Thierry Ceolin
46   */
47  public class GTODProvider implements EOPBasedTransformProvider {
48  
49      /** Serializable UID. */
50      private static final long serialVersionUID = 20141228L;
51  
52      /** Angular velocity of the Earth, in rad/s. */
53      private static final double AVE = 7.292115146706979e-5;
54  
55      /** Conventions. */
56      private final IERSConventions conventions;
57  
58      /** EOP history. */
59      private final EOPHistory eopHistory;
60  
61      /** GAST function. */
62      private final transient TimeScalarFunction gastFunction;
63  
64      /** Simple constructor.
65       * @param conventions IERS conventions to use
66       * @param eopHistory EOP history (may be null)
67       * @param timeScales  set of time scales to use.
68       * @since 10.1
69       */
70      protected GTODProvider(final IERSConventions conventions,
71                             final EOPHistory eopHistory,
72                             final TimeScales timeScales) {
73          final TimeScale ut1 = eopHistory == null ?
74                  timeScales.getUTC() : // UT1 wihthout EOP is UTC
75                  timeScales.getUT1(eopHistory.getConventions(), eopHistory.isSimpleEop());
76          this.conventions   = conventions;
77          this.eopHistory    = eopHistory;
78          this.gastFunction  = conventions.getGASTFunction(ut1, eopHistory, timeScales);
79      }
80  
81      /**
82       * Private constructor.
83       *
84       * @param conventions  IERS conventions to use
85       * @param eopHistory   EOP history (may be null)
86       * @param gastFunction GAST function
87       */
88      private GTODProvider(final IERSConventions conventions,
89                           final EOPHistory eopHistory,
90                           final TimeScalarFunction gastFunction) {
91          this.conventions = conventions;
92          this.eopHistory = eopHistory;
93          this.gastFunction = gastFunction;
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      public EOPHistory getEOPHistory() {
99          return eopHistory;
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public GTODProvider getNonInterpolatingProvider() {
105         return new GTODProvider(conventions, eopHistory.getNonInterpolatingEOPHistory(),
106                 gastFunction);
107     }
108 
109     /** {@inheritDoc} */
110     @Override
111     public Transform getTransform(final AbsoluteDate date) {
112 
113         // compute Greenwich apparent sidereal time, in radians
114         final double gast = gastFunction.value(date);
115 
116         // compute true angular rotation of Earth, in rad/s
117         final double lod = (eopHistory == null) ? 0.0 : eopHistory.getLOD(date);
118         final double omp = AVE * (1 - lod / Constants.JULIAN_DAY);
119         final Vector3D rotationRate = new Vector3D(omp, Vector3D.PLUS_K);
120 
121         // set up the transform from parent TOD
122         return new Transform(date, new Rotation(Vector3D.PLUS_K, gast, RotationConvention.FRAME_TRANSFORM), rotationRate);
123 
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
129 
130         // compute Greenwich apparent sidereal time, in radians
131         final T gast = gastFunction.value(date);
132 
133         // compute true angular rotation of Earth, in rad/s
134         final T lod = (eopHistory == null) ? date.getField().getZero() : eopHistory.getLOD(date);
135         final T omp = lod.multiply(-1.0 / Constants.JULIAN_DAY).add(1).multiply(AVE);
136         final FieldVector3D<T> rotationRate = new FieldVector3D<>(date.getField().getZero(),
137                                                                   date.getField().getZero(),
138                                                                   date.getField().getZero().add(omp));
139 
140         // set up the transform from parent TOD
141         return new FieldTransform<>(date,
142                                     new FieldRotation<>(FieldVector3D.getPlusK(date.getField()),
143                                                         gast, RotationConvention.FRAME_TRANSFORM),
144                                     rotationRate);
145 
146     }
147 
148     /** Replace the instance with a data transfer object for serialization.
149      * <p>
150      * This intermediate class serializes only the frame key.
151      * </p>
152      * @return data transfer object that will be serialized
153      */
154     @DefaultDataContext
155     private Object writeReplace() {
156         return new DataTransferObject(conventions, eopHistory);
157     }
158 
159     /** Internal class used only for serialization. */
160     @DefaultDataContext
161     private static class DataTransferObject implements Serializable {
162 
163         /** Serializable UID. */
164         private static final long serialVersionUID = 20131209L;
165 
166         /** Conventions. */
167         private final IERSConventions conventions;
168 
169         /** EOP history. */
170         private final EOPHistory eopHistory;
171 
172         /** Simple constructor.
173          * @param conventions IERS conventions to apply
174          * @param eopHistory EOP history
175          */
176         DataTransferObject(final IERSConventions conventions, final EOPHistory eopHistory) {
177             this.conventions = conventions;
178             this.eopHistory  = eopHistory;
179         }
180 
181         /** Replace the deserialized data transfer object with a {@link GTODProvider}.
182          * @return replacement {@link GTODProvider}
183          */
184         private Object readResolve() {
185             try {
186                 // retrieve a managed frame
187                 return new GTODProvider(conventions, eopHistory,
188                         DataContext.getDefault().getTimeScales());
189             } catch (OrekitException oe) {
190                 throw new OrekitInternalError(oe);
191             }
192         }
193 
194     }
195 
196 }