1   /* Copyright 2002-2026 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.bodies;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
21  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
22  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field;
24  import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
25  import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
26  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
27  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28  import org.hipparchus.geometry.euclidean.threed.Rotation;
29  import org.hipparchus.geometry.euclidean.threed.Vector3D;
30  import org.hipparchus.util.Precision;
31  import org.orekit.frames.FieldKinematicTransform;
32  import org.orekit.frames.FieldStaticTransform;
33  import org.orekit.frames.FieldTransform;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.KinematicTransform;
36  import org.orekit.frames.StaticTransform;
37  import org.orekit.frames.Transform;
38  import org.orekit.frames.TransformProvider;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.FieldAbsoluteDate;
41  import org.orekit.utils.AngularCoordinates;
42  import org.orekit.utils.ExtendedPositionProvider;
43  import org.orekit.utils.FieldAngularCoordinates;
44  import org.orekit.utils.FieldPVCoordinates;
45  import org.orekit.utils.PVCoordinates;
46  
47  /** Class for JPL inertial frame transform providers.
48   * @author Luc Maisonobe
49   * @author Davide Degavi
50   * @author Romain Serra
51   * @since 13.1.8
52   */
53  class JPLInertialTransformProvider implements TransformProvider {
54  
55      /** Parent frame. */
56      private final Frame definingFrame;
57      /** Translation provider. */
58      private final ExtendedPositionProvider pvProvider;
59      /** IAU pole. */
60      private final IAUPole iauPole;
61  
62      /**
63       * Constructor.
64       * @param definingFrame parent frame
65       * @param pvProvider position provider
66       * @param iauPole IAU pole
67       */
68      JPLInertialTransformProvider(final Frame definingFrame, final ExtendedPositionProvider pvProvider,
69                                   final IAUPole iauPole) {
70          this.definingFrame = definingFrame;
71          this.pvProvider    = pvProvider;
72          this.iauPole       = iauPole;
73      }
74  
75      /**
76       * Package private getter for the defining frame.
77       * @return frame
78       */
79      Frame getDefiningFrame() {
80          return definingFrame;
81      }
82  
83      /** {@inheritDoc} */
84      @Override
85      public Transform getTransform(final AbsoluteDate date) {
86          // translation part
87          final PVCoordinates pv = pvProvider.getPVCoordinates(date, definingFrame).negate();
88  
89          // use automatic differentiation to compute the rotation derivatives
90          final AngularCoordinates angularCoordinates = getAngularCoordinates(date, true);
91  
92          // set up the transform from parent frame
93          return new Transform(date, pv, angularCoordinates);
94  
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public KinematicTransform getKinematicTransform(final AbsoluteDate date) {
100         // translation part
101         final PVCoordinates pv = pvProvider.getPVCoordinates(date, definingFrame).negate();
102 
103         // use automatic differentiation to compute the rotation rate
104         final AngularCoordinates angularCoordinates = getAngularCoordinates(date, false);
105 
106         // set up the kinematic transform from parent frame
107         return KinematicTransform.of(date, pv, angularCoordinates.getRotation(), angularCoordinates.getRotationRate());
108 
109     }
110 
111     /**
112      * Compute the rotation with derivatives via automatic differentiation.
113      * @param date date
114      * @param order2 flag to use order 2 derivatives
115      * @return angular coordinates
116      */
117     private AngularCoordinates getAngularCoordinates(final AbsoluteDate date, final boolean order2) {
118         if (order2) {
119             final UnivariateDerivative2Field field = UnivariateDerivative2Field.getInstance();
120             final UnivariateDerivative2 dt = new UnivariateDerivative2(0, 1, 0);
121             final FieldAbsoluteDate<UnivariateDerivative2> ud2Date =
122                     new FieldAbsoluteDate<>(field, date).shiftedBy(dt);
123             return new AngularCoordinates(getRotation(ud2Date));
124         } else {
125             final UnivariateDerivative1Field field = UnivariateDerivative1Field.getInstance();
126             final UnivariateDerivative1 dt = new UnivariateDerivative1(0, 1);
127             final FieldAbsoluteDate<UnivariateDerivative1> ud1Date =
128                     new FieldAbsoluteDate<>(field, date).shiftedBy(dt);
129             return new AngularCoordinates(getRotation(ud1Date));
130         }
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public StaticTransform getStaticTransform(final AbsoluteDate date) {
136         // translation part
137         final Vector3D position = pvProvider.getPVCoordinates(date, definingFrame).getPosition().negate();
138         // compute rotation from ICRF frame to self,
139         // as per the "Report of the IAU/IAG Working Group on Cartographic
140         // Coordinates and Rotational Elements of the Planets and Satellites"
141         // These definitions are common for all recent versions of this report
142         // published every three years, the precise values of pole direction
143         // and W angle coefficients may vary from publication year as models are
144         // adjusted. These coefficients are not in this class, they are in the
145         // specialized classes that do implement the getPole and getPrimeMeridianAngle
146         // methods
147         final Vector3D pole  = iauPole.getPole(date);
148         Vector3D qNode = iauPole.getNode(date);
149         if (qNode.getNorm2Sq() < Precision.SAFE_MIN) {
150             qNode = Vector3D.crossProduct(Vector3D.PLUS_K, pole);
151         }
152         final Rotation rotation = new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I);
153         return StaticTransform.of(date, position, rotation);
154     }
155 
156     /** {@inheritDoc} */
157     @Override
158     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
159         // translation part
160         final FieldPVCoordinates<T> pv = pvProvider.getPVCoordinates(date, definingFrame).negate();
161 
162         // compute the rotation while preserving the derivatives already present in the field date
163         final FieldRotation<T> rotation = getRotation(date);
164 
165         // use automatic differentiation to compute the rotation derivatives
166         final FieldAbsoluteDate<FieldUnivariateDerivative2<T>> fud2Date = date.toFUD2Field();
167         final FieldAngularCoordinates<T> derivatives = new FieldAngularCoordinates<>(getRotation(fud2Date));
168 
169         // set up the transform from parent frame
170         return new FieldTransform<>(date, new FieldTransform<>(date, pv),
171                 new FieldTransform<>(date, new FieldAngularCoordinates<>(rotation, derivatives.getRotationRate(),
172                         derivatives.getRotationAcceleration())));
173 
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> getKinematicTransform(final FieldAbsoluteDate<T> date) {
179         // translation part
180         final FieldPVCoordinates<T> pv = pvProvider.getPVCoordinates(date, definingFrame).negate();
181 
182         // compute the rotation while preserving the derivatives already present in the field date
183         final FieldRotation<T> rotation = getRotation(date);
184 
185         // use automatic differentiation to compute the rotation rate
186         final FieldAbsoluteDate<FieldUnivariateDerivative1<T>> fud1Date = date.toFUD1Field();
187         final FieldAngularCoordinates<T> derivatives = new FieldAngularCoordinates<>(getRotation(fud1Date));
188 
189         // set up the kinematic transform from parent frame
190         return FieldKinematicTransform.of(date, pv, rotation, derivatives.getRotationRate());
191 
192     }
193 
194     /** {@inheritDoc} */
195     @Override
196     public <T extends CalculusFieldElement<T>> FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
197         final FieldVector3D<T> position = pvProvider.getPVCoordinates(date, definingFrame).getPosition().negate();
198         return FieldStaticTransform.of(date, position, getRotation(date));
199     }
200 
201     /** Compute the complete (Field) rotation from parent.
202      * @param date current date
203      * @param <T> type of the field elements
204      * @return rotation
205      */
206     private <T extends CalculusFieldElement<T>> FieldRotation<T> getRotation(final FieldAbsoluteDate<T> date) {
207         // compute rotation from ICRF frame to self,
208         // as per the "Report of the IAU/IAG Working Group on Cartographic
209         // Coordinates and Rotational Elements of the Planets and Satellites"
210         // These definitions are common for all recent versions of this report
211         // published every three years, the precise values of pole direction
212         // and W angle coefficients may vary from publication year as models are
213         // adjusted. These coefficients are not in this class, they are in the
214         // specialized classes that do implement the getPole and getPrimeMeridianAngle
215         // methods
216         final FieldVector3D<T> pole  = iauPole.getPole(date);
217         FieldVector3D<T> qNode = iauPole.getNode(date);
218         if (qNode.getNorm2Sq().getReal() < Precision.SAFE_MIN) {
219             qNode = FieldVector3D.crossProduct(Vector3D.PLUS_K, pole);
220         }
221         return new FieldRotation<>(pole, qNode, FieldVector3D.getPlusK(date.getField()),
222                 FieldVector3D.getPlusI(date.getField()));
223     }
224 
225 }