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  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.orekit.frames.FieldKinematicTransform;
24  import org.orekit.frames.FieldStaticTransform;
25  import org.orekit.frames.FieldTransform;
26  import org.orekit.frames.Frame;
27  import org.orekit.frames.KinematicTransform;
28  import org.orekit.frames.OriginTransformProvider;
29  import org.orekit.frames.Predefined;
30  import org.orekit.frames.StaticTransform;
31  import org.orekit.frames.Transform;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.FieldAbsoluteDate;
34  import org.orekit.utils.FieldPVCoordinates;
35  import org.orekit.utils.PVCoordinates;
36  import org.orekit.utils.TimeStampedFieldPVCoordinates;
37  import org.orekit.utils.TimeStampedPVCoordinates;
38  
39  /** Implementation of the {@link CelestialBody} interface using JPL or INPOP ephemerides.
40   * @author Luc Maisonobe
41   */
42  class JPLCelestialBody implements CelestialBody {
43  
44      /** Name of the body. */
45      private final String name;
46  
47      /** Regular expression for supported files names. */
48      private final String supportedNames;
49  
50      /** Raw position-velocity provider. */
51      private final JPLEphemeridesLoader.RawPVProvider rawPVProvider;
52  
53      /** Attraction coefficient of the body (m³/s²). */
54      private final double gm;
55  
56      /** Scaling factor for position-velocity. */
57      private final double scale;
58  
59      /** Body's PV coordinates are defined in this frame. */
60      private final Frame definingFrameAlignedWithIcrf;
61  
62      /** Body centered frame aligned with ICRF. */
63      private final Frame icrfAlignedFrame;
64  
65      /** Inertially oriented, body-centered frame. */
66      private final Frame inertialFrame;
67  
68      /** Body oriented, body-centered frame. */
69      private final Frame bodyFrame;
70  
71      /** Build an instance and the underlying frame.
72       * @param name name of the body
73       * @param supportedNames regular expression for supported files names
74       * @param generateType ephemeris type to generate
75       * @param rawPVProvider raw position-velocity provider
76       * @param gm attraction coefficient (in m³/s²)
77       * @param scale scaling factor for position-velocity
78       * @param iauPole IAU pole implementation
79       * @param definingFrameAlignedWithICRF frame in which celestial body coordinates are defined,
80       * this frame <strong>must</strong> be aligned with ICRF
81       * @since 14.0
82       */
83      JPLCelestialBody(final String name, final String supportedNames,
84                       final JPLEphemeridesLoader.EphemerisType generateType,
85                       final JPLEphemeridesLoader.RawPVProvider rawPVProvider,
86                       final double gm, final double scale,
87                       final IAUPole iauPole,
88                       final Frame definingFrameAlignedWithICRF) {
89          this.name           = name;
90          this.gm             = gm;
91          this.scale          = scale;
92          this.supportedNames = supportedNames;
93          this.rawPVProvider  = rawPVProvider;
94          this.definingFrameAlignedWithIcrf = definingFrameAlignedWithICRF;
95          if (rawPVProvider instanceof JPLEphemeridesLoader.ZeroRawPVProvider) {
96              // no translation or rotation needed, use directly
97              // might be better to have a method instead of using "instanceof"
98              // but the classes are tightly coupled and package private
99              this.icrfAlignedFrame = definingFrameAlignedWithICRF;
100         } else {
101             // translation needed
102             final String icrfName;
103             if (JPLEphemeridesLoader.EphemerisType.SOLAR_SYSTEM_BARYCENTER == generateType) {
104                 // in Orekit FramesFactory.getICRF() is implemented by
105                 // CelestialBodyFactor.getSsb().getInertiallyOrientedFrame()
106                 // so have to match Predefined.ICRF
107                 icrfName = Predefined.ICRF.getName();
108             } else {
109                 icrfName = name + "/ICRF";
110             }
111             this.icrfAlignedFrame = new Frame(
112                     definingFrameAlignedWithICRF,
113                     new OriginTransformProvider(this, definingFrameAlignedWithICRF),
114                     icrfName,
115                     true);
116         }
117         if (iauPole == null || iauPole.isGcrfAligned()) {
118             // Body "fixed" and inertial frames are GCRF aligned.
119             this.inertialFrame = icrfAlignedFrame;
120             this.bodyFrame = icrfAlignedFrame;
121         } else {
122             this.inertialFrame  = new InertiallyOriented(new JPLInertialTransformProvider(icrfAlignedFrame, this, iauPole));
123             this.bodyFrame      = new BodyOriented(new JPLRotatingTransformProvider(iauPole));
124         }
125 
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
131 
132         // apply the scale factor to raw position-velocity
133         final PVCoordinates rawPV    = rawPVProvider.getRawPV(date);
134         final TimeStampedPVCoordinates scaledPV = new TimeStampedPVCoordinates(date, scale, rawPV);
135 
136         // the raw PV are relative to the parent of the body centered inertially oriented frame
137         final Transform transform = definingFrameAlignedWithIcrf.getTransformTo(frame, date);
138 
139         // convert to requested frame
140         return transform.transformPVCoordinates(scaledPV);
141 
142     }
143 
144     /** Get the {@link FieldPVCoordinates} of the body in the selected frame.
145      * @param date current date
146      * @param frame the frame where to define the position
147      * @param <T> type of the field elements
148      * @return time-stamped position/velocity of the body (m and m/s)
149      */
150     @Override
151     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date,
152                                                                                                  final Frame frame) {
153 
154         // apply the scale factor to raw position-velocity
155         final FieldPVCoordinates<T>            rawPV    = rawPVProvider.getRawPV(date);
156         final TimeStampedFieldPVCoordinates<T> scaledPV = new TimeStampedFieldPVCoordinates<>(date, scale, rawPV);
157 
158         // the raw PV are relative to the parent of the body centered inertially oriented frame
159         final FieldTransform<T> transform = definingFrameAlignedWithIcrf.getTransformTo(frame, date);
160 
161         // convert to requested frame
162         return transform.transformPVCoordinates(scaledPV);
163 
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public Vector3D getVelocity(final AbsoluteDate date, final Frame frame) {
169 
170         // apply the scale factor to raw position-velocity
171         final PVCoordinates rawPV    = rawPVProvider.getRawPV(date);
172         final TimeStampedPVCoordinates scaledPV = new TimeStampedPVCoordinates(date, scale, rawPV);
173 
174         // the raw PV are relative to the parent of the body centered inertially oriented frame
175         final KinematicTransform transform = definingFrameAlignedWithIcrf.getKinematicTransformTo(frame, date);
176 
177         // convert to requested frame
178         return transform.transformOnlyPV(scaledPV).getVelocity();
179 
180     }
181 
182     /** {@inheritDoc} */
183     @Override
184     public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
185 
186         // apply the scale factor to raw position
187         final Vector3D rawPosition    = rawPVProvider.getRawPosition(date);
188         final Vector3D scaledPosition = rawPosition.scalarMultiply(scale);
189 
190         // the raw position is relative to the parent of the body centered inertially oriented frame
191         final StaticTransform transform = definingFrameAlignedWithIcrf.getStaticTransformTo(frame, date);
192 
193         // convert to requested frame
194         return transform.transformPosition(scaledPosition);
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public <T extends CalculusFieldElement<T>> FieldVector3D<T> getPosition(final FieldAbsoluteDate<T> date, final Frame frame) {
200 
201         // apply the scale factor to raw position
202         final FieldVector3D<T> rawPosition     = rawPVProvider.getRawPosition(date);
203         final FieldVector3D<T> scaledPosition  = rawPosition.scalarMultiply(scale);
204 
205         // the raw position is relative to the parent of the body centered inertially oriented frame
206         final FieldStaticTransform<T> transform = definingFrameAlignedWithIcrf.getStaticTransformTo(frame, date);
207 
208         // convert to requested frame
209         return transform.transformPosition(scaledPosition);
210     }
211 
212     /** {@inheritDoc} */
213     @Override
214     public <T extends CalculusFieldElement<T>> FieldVector3D<T> getVelocity(final FieldAbsoluteDate<T> date, final Frame frame) {
215         // apply the scale factor to raw position-velocity
216         final FieldPVCoordinates<T> rawPV    = rawPVProvider.getRawPV(date);
217         final TimeStampedFieldPVCoordinates<T> scaledPV = new TimeStampedFieldPVCoordinates<>(date, scale, rawPV);
218 
219         // the raw PV are relative to the parent of the body centered inertially oriented frame
220         final FieldKinematicTransform<T> transform = definingFrameAlignedWithIcrf.getKinematicTransformTo(frame, date);
221 
222         // convert to requested frame
223         return transform.transformOnlyPV(scaledPV).getVelocity();
224     }
225 
226     /** {@inheritDoc} */
227     public String getName() {
228         return name;
229     }
230 
231     /** {@inheritDoc} */
232     public double getGM() {
233         return gm;
234     }
235 
236     @Override
237     public Frame getIcrfAlignedFrame() {
238         return icrfAlignedFrame;
239     }
240 
241     /** {@inheritDoc} */
242     public Frame getInertiallyOrientedFrame() {
243         return inertialFrame;
244     }
245 
246     /** {@inheritDoc} */
247     public Frame getBodyOrientedFrame() {
248         return bodyFrame;
249     }
250 
251     /** Inertially oriented body centered frame. */
252     private class InertiallyOriented extends Frame {
253 
254         /** Suffix for inertial frame name. */
255         private static final String INERTIAL_FRAME_SUFFIX = "/inertial";
256 
257         /** Simple constructor.
258          * @param transformProvider transform provider to frame in which celestial body coordinates are defined
259          * @since 14.0
260          */
261         InertiallyOriented(final JPLInertialTransformProvider transformProvider) {
262             super(transformProvider.getDefiningFrame(), transformProvider, name + INERTIAL_FRAME_SUFFIX, true);
263         }
264 
265     }
266 
267     /** Body oriented body centered frame. */
268     private class BodyOriented extends Frame {
269 
270         /**
271          * Suffix for body frame name.
272          */
273         private static final String BODY_FRAME_SUFFIX = "/rotating";
274 
275         /**
276          * Simple constructor.
277          *
278          * @param transformProvider transform provider to body-fixed frame
279          * @since 14.0
280          */
281         BodyOriented(final JPLRotatingTransformProvider transformProvider) {
282             super(inertialFrame, transformProvider, name + BODY_FRAME_SUFFIX, false);
283         }
284     }
285 }