1   /* Copyright 2002-2020 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.RealFieldElement;
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.Vector3D;
26  import org.hipparchus.util.FastMath;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitInternalError;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.time.TimeVectorFunction;
32  
33  /** Celestial Intermediate Reference Frame.
34   * <p>This provider includes precession effects according to either the IAU 2006 precession
35   * (also known as Nicole Capitaines's P03 precession theory) and IAU 2000A_R06 nutation
36   * for IERS 2010 conventions or the IAU 2000A precession-nutation model for IERS 2003
37   * conventions. These models replaced the older IAU-76 precession (Lieske) and IAU-80
38   * theory of nutation (Wahr) which were used in the classical equinox-based paradigm.
39   * It <strong>must</strong> be used with the Earth Rotation Angle (REA) defined by
40   * Capitaine's model and <strong>not</strong> IAU-82 sidereal
41   * time which is consistent with the older models only.</p>
42   * <p>Its parent frame is the GCRF frame.
43   */
44  class CIRFProvider implements EOPBasedTransformProvider {
45  
46      /** Serializable UID. */
47      private static final long serialVersionUID = 20130806L;
48  
49      /** Function computing CIP/CIO components. */
50      private final transient TimeVectorFunction xysPxy2Function;
51  
52      /** EOP history. */
53      private final EOPHistory eopHistory;
54  
55      /** Simple constructor.
56       * @param eopHistory EOP history
57       * @see Frame
58       */
59      CIRFProvider(final EOPHistory eopHistory) {
60  
61          // load the nutation model
62          xysPxy2Function = eopHistory.getConventions()
63                  .getXYSpXY2Function(eopHistory.getTimeScales());
64  
65          // store correction to the model
66          this.eopHistory = eopHistory;
67  
68      }
69  
70      /** {@inheritDoc} */
71      @Override
72      public EOPHistory getEOPHistory() {
73          return eopHistory;
74      }
75  
76      /** {@inheritDoc} */
77      @Override
78      public CIRFProvider getNonInterpolatingProvider() {
79          return new CIRFProvider(eopHistory.getNonInterpolatingEOPHistory());
80      }
81  
82      /** {@inheritDoc} */
83      @Override
84      public Transform getTransform(final AbsoluteDate date) {
85  
86          final double[] xys  = xysPxy2Function.value(date);
87          final double[] dxdy = eopHistory.getNonRotatinOriginNutationCorrection(date);
88  
89          // position of the Celestial Intermediate Pole (CIP)
90          final double xCurrent = xys[0] + dxdy[0];
91          final double yCurrent = xys[1] + dxdy[1];
92  
93          // position of the Celestial Intermediate Origin (CIO)
94          final double sCurrent = xys[2] - xCurrent * yCurrent / 2;
95  
96          // set up the bias, precession and nutation rotation
97          final double x2Py2  = xCurrent * xCurrent + yCurrent * yCurrent;
98          final double zP1    = 1 + FastMath.sqrt(1 - x2Py2);
99          final double r      = FastMath.sqrt(x2Py2);
100         final double sPe2   = 0.5 * (sCurrent + FastMath.atan2(yCurrent, xCurrent));
101         final double cos    = FastMath.cos(sPe2);
102         final double sin    = FastMath.sin(sPe2);
103         final double xPr    = xCurrent + r;
104         final double xPrCos = xPr * cos;
105         final double xPrSin = xPr * sin;
106         final double yCos   = yCurrent * cos;
107         final double ySin   = yCurrent * sin;
108         final Rotation bpn  = new Rotation(zP1 * (xPrCos + ySin), -r * (yCos + xPrSin),
109                                            r * (xPrCos - ySin), zP1 * (yCos - xPrSin),
110                                            true);
111 
112         return new Transform(date, bpn, Vector3D.ZERO);
113 
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public <T extends RealFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
119 
120         final T[] xys  = xysPxy2Function.value(date);
121         final T[] dxdy = eopHistory.getNonRotatinOriginNutationCorrection(date);
122 
123         // position of the Celestial Intermediate Pole (CIP)
124         final T xCurrent = xys[0].add(dxdy[0]);
125         final T yCurrent = xys[1].add(dxdy[1]);
126 
127         // position of the Celestial Intermediate Origin (CIO)
128         final T sCurrent = xys[2].subtract(xCurrent.multiply(yCurrent).multiply(0.5));
129 
130         // set up the bias, precession and nutation rotation
131         final T x2Py2  = xCurrent.multiply(xCurrent).add(yCurrent.multiply(yCurrent));
132         final T zP1    = x2Py2.subtract(1).negate().sqrt().add(1);
133         final T r      = x2Py2.sqrt();
134         final T sPe2   = sCurrent.add(yCurrent.atan2(xCurrent)).multiply(0.5);
135         final T cos    = sPe2.cos();
136         final T sin    = sPe2.sin();
137         final T xPr    = xCurrent.add(r);
138         final T xPrCos = xPr.multiply(cos);
139         final T xPrSin = xPr.multiply(sin);
140         final T yCos   = yCurrent.multiply(cos);
141         final T ySin   = yCurrent.multiply(sin);
142         final FieldRotation<T> bpn  = new FieldRotation<>(zP1.multiply(xPrCos.add(ySin)),
143                                                           r.multiply(yCos.add(xPrSin)).negate(),
144                                                           r.multiply(xPrCos.subtract(ySin)),
145                                                           zP1.multiply(yCos.subtract(xPrSin)),
146                                                           true);
147 
148         return new FieldTransform<>(date, bpn, FieldVector3D.getZero(date.getField()));
149 
150     }
151 
152     /** Replace the instance with a data transfer object for serialization.
153      * <p>
154      * This intermediate class serializes only the frame key.
155      * </p>
156      * @return data transfer object that will be serialized
157      */
158     private Object writeReplace() {
159         return new DataTransferObject(eopHistory);
160     }
161 
162     /** Internal class used only for serialization. */
163     private static class DataTransferObject implements Serializable {
164 
165         /** Serializable UID. */
166         private static final long serialVersionUID = 20131209L;
167 
168         /** EOP history. */
169         private final EOPHistory eopHistory;
170 
171         /** Simple constructor.
172          * @param eopHistory EOP history
173          */
174         DataTransferObject(final EOPHistory eopHistory) {
175             this.eopHistory = eopHistory;
176         }
177 
178         /** Replace the deserialized data transfer object with a {@link CIRFProvider}.
179          * @return replacement {@link CIRFProvider}
180          */
181         private Object readResolve() {
182             try {
183                 // retrieve a managed frame
184                 return new CIRFProvider(eopHistory);
185             } catch (OrekitException oe) {
186                 throw new OrekitInternalError(oe);
187             }
188         }
189 
190     }
191 
192 }