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.files.ccsds.definitions;
18  
19  import org.hipparchus.geometry.euclidean.threed.Rotation;
20  import org.orekit.data.DataContext;
21  import org.orekit.errors.OrekitException;
22  import org.orekit.errors.OrekitMessages;
23  import org.orekit.frames.Frame;
24  import org.orekit.frames.LOFType;
25  import org.orekit.frames.Transform;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.utils.IERSConventions;
28  import org.orekit.utils.PVCoordinatesProvider;
29  
30  /** Facade in front of several frames types in CCSDS messages.
31   *
32   * @author Luc Maisonobe
33   * @author Vincent Cucchietti
34   * @since 11.0
35   */
36  public class FrameFacade {
37  
38      /** Reference to node in Orekit frames tree. */
39      private final Frame frame;
40  
41      /** Reference to celestial body centered frame. */
42      private final CelestialBodyFrame celestialBodyFrame;
43  
44      /** Reference to orbit-relative frame. */
45      private final OrbitRelativeFrame orbitRelativeFrame;
46  
47      /** Reference to spacecraft body frame. */
48      private final SpacecraftBodyFrame spacecraftBodyFrame;
49  
50      /** Name of the frame. */
51      private final String name;
52  
53      /**
54       * Simple constructor.
55       * <p>
56       * At most one of {@code celestialBodyFrame}, {@code orbitRelativeFrame} or {@code spacecraftBodyFrame} may be non
57       * null. They may all be null if frame is unknown, in which case only the name will be available.
58       * </p>
59       *
60       * @param frame reference to node in Orekit frames tree (may be null)
61       * @param celestialBodyFrame reference to celestial body centered frame (may be null)
62       * @param orbitRelativeFrame reference to orbit-relative frame (may be null)
63       * @param spacecraftBodyFrame reference to spacecraft body frame (may be null)
64       * @param name name of the frame
65       */
66      public FrameFacade(final Frame frame,
67                         final CelestialBodyFrame celestialBodyFrame,
68                         final OrbitRelativeFrame orbitRelativeFrame,
69                         final SpacecraftBodyFrame spacecraftBodyFrame,
70                         final String name) {
71          this.frame = frame;
72          this.celestialBodyFrame = celestialBodyFrame;
73          this.orbitRelativeFrame = orbitRelativeFrame;
74          this.spacecraftBodyFrame = spacecraftBodyFrame;
75          this.name = name;
76      }
77  
78      /**
79       * Get the associated frame tree node.
80       *
81       * @return associated frame tree node, or null if none exists
82       */
83      public Frame asFrame() {
84          return frame;
85      }
86  
87      /**
88       * Get the associated {@link CelestialBodyFrame celestial body frame}.
89       *
90       * @return associated celestial body frame, or null if frame is associated to a
91       * {@link #asOrbitRelativeFrame() orbit}, a {@link #asSpacecraftBodyFrame spacecraft} or is not supported
92       */
93      public CelestialBodyFrame asCelestialBodyFrame() {
94          return celestialBodyFrame;
95      }
96  
97      /**
98       * Get the associated {@link OrbitRelativeFrame orbit relative frame}.
99       *
100      * @return associated orbit relative frame, or null if frame is associated to a
101      * {@link #asCelestialBodyFrame() celestial body}, a {@link #asSpacecraftBodyFrame spacecraft} or is not supported
102      */
103     public OrbitRelativeFrame asOrbitRelativeFrame() {
104         return orbitRelativeFrame;
105     }
106 
107     /**
108      * Get the associated {@link SpacecraftBodyFrame spacecraft body frame}.
109      *
110      * @return associated spacecraft body frame, or null if frame is associated to a
111      * {@link #asCelestialBodyFrame() celestial body}, an {@link #asOrbitRelativeFrame orbit} or is not supported
112      */
113     public SpacecraftBodyFrame asSpacecraftBodyFrame() {
114         return spacecraftBodyFrame;
115     }
116 
117     /**
118      * Get the CCSDS name for the frame.
119      *
120      * @return CCSDS name
121      */
122     public String getName() {
123         return name;
124     }
125 
126     /**
127      * Map an Orekit frame to a CCSDS frame facade.
128      *
129      * @param frame a reference frame.
130      * @return the CCSDS frame corresponding to the Orekit frame
131      */
132     public static FrameFacade map(final Frame frame) {
133         final CelestialBodyFrame cbf = CelestialBodyFrame.map(frame);
134         return new FrameFacade(frame, cbf, null, null, cbf.getName());
135     }
136 
137     /**
138      * Simple constructor.
139      *
140      * @param name name of the frame
141      * @param conventions IERS conventions to use
142      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
143      * @param dataContext to use when creating the frame
144      * @param allowCelestial if true, {@link CelestialBodyFrame} are allowed
145      * @param allowOrbit if true, {@link OrbitRelativeFrame} are allowed
146      * @param allowSpacecraft if true, {@link SpacecraftBodyFrame} are allowed
147      * @return frame facade corresponding to the CCSDS name
148      */
149     public static FrameFacade parse(final String name,
150                                     final IERSConventions conventions,
151                                     final boolean simpleEOP,
152                                     final DataContext dataContext,
153                                     final boolean allowCelestial,
154                                     final boolean allowOrbit,
155                                     final boolean allowSpacecraft) {
156         try {
157             final CelestialBodyFrame cbf = CelestialBodyFrame.parse(name);
158             if (allowCelestial) {
159                 return new FrameFacade(cbf.getFrame(conventions, simpleEOP, dataContext),
160                                        cbf, null, null, cbf.getName());
161             }
162         } catch (IllegalArgumentException iaeC) {
163             try {
164                 final OrbitRelativeFrame orf = OrbitRelativeFrame.valueOf(name.replace(' ', '_'));
165                 if (allowOrbit) {
166                     return new FrameFacade(null, null, orf, null, orf.name());
167                 }
168             } catch (IllegalArgumentException iaeO) {
169                 try {
170                     final SpacecraftBodyFrame sbf = SpacecraftBodyFrame.parse(name.replace(' ', '_'));
171                     if (allowSpacecraft) {
172                         return new FrameFacade(null, null, null, sbf, sbf.toString());
173                     }
174                 } catch (OrekitException | IllegalArgumentException e) {
175                     // nothing to do here, use fallback below
176                 }
177             }
178         }
179 
180         // we don't know any frame with this name, just store the name itself
181         return new FrameFacade(null, null, null, null, name);
182 
183     }
184 
185     /**
186      * Get the transform between {@link FrameFacade CCSDS frames}.
187      * <p>
188      * In case both input and output frames are {@link OrbitRelativeFrame orbit relative frame}, the returned transform
189      * will only be composed of a {@link Rotation rotation}. Only {@link LOFType commonly used orbit relative frames}
190      * will be recognized.
191      * <p>
192      * Note that if the input/output {@link FrameFacade CCSDS frame} is defined using a :
193      * <ul>
194      * <li><b>{@link CelestialBodyFrame celestial body frame}</b></li>
195      * <li><b>{@link SpacecraftBodyFrame spacecraft body frame}</b></li>
196      * </ul>
197      * then <b>an exception will be thrown</b> (currently not supported).
198      * <p>
199      * Note that the pivot frame provided <b>must be inertial</b> and <b>consistent</b> to what you are working with
200      * (i.e GCRF if around Earth for example).
201      *
202      * @param frameIn the input {@link FrameFacade CCSDS frame} to convert from
203      * @param frameOut the output {@link FrameFacade CCSDS frame} to convert to
204      * @param inertialPivotFrame <b>inertial</b> frame used as a pivot to create the transform
205      * @param date the date for the transform
206      * @param pv the position and velocity coordinates provider (required in case one of the frames is an
207      * {@link OrbitRelativeFrame orbit relative frame})
208      * @return the transform between {@link FrameFacade CCSDS frames}.
209      */
210     public static Transform getTransform(final FrameFacade frameIn, final FrameFacade frameOut,
211                                          final Frame inertialPivotFrame,
212                                          final AbsoluteDate date, final PVCoordinatesProvider pv) {
213 
214         if (inertialPivotFrame.isPseudoInertial()) {
215             final Transform frameInToPivot = getTransformToPivot(frameIn, inertialPivotFrame, date, pv);
216 
217             final Transform pivotToFrameOut = getTransformToPivot(frameOut, inertialPivotFrame, date, pv).getInverse();
218 
219             return new Transform(date, frameInToPivot, pivotToFrameOut);
220         }
221         else {
222             throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, inertialPivotFrame.getName());
223         }
224 
225     }
226 
227     /**
228      * Get the transform between input {@link FrameFacade CCSDS frame} and an <b>inertial</b>
229      * {@link Frame Orekit frame}.
230      *
231      * @param frameIn the input {@link FrameFacade CCSDS frame} to convert from
232      * @param inertialPivotFrame <b>inertial</b> {@link Frame Orekit frame} to convert to
233      * @param date the date for the transform
234      * @param pv the position and velocity coordinates provider (required in case the input
235      * {@link FrameFacade CCSDS frame} is an {@link OrbitRelativeFrame orbit relative frame})
236      * @return the transform between input {@link FrameFacade CCSDS frame} and an inertial {@link Frame Orekit frame}
237      */
238     private static Transform getTransformToPivot(final FrameFacade frameIn, final Frame inertialPivotFrame,
239                                                  final AbsoluteDate date, final PVCoordinatesProvider pv) {
240         final Transform frameInToPivot;
241 
242         // Orekit frame
243         if (frameIn.asFrame() != null) {
244             frameInToPivot = frameIn.asFrame().getTransformTo(inertialPivotFrame, date);
245         }
246 
247         // Local orbital frame
248         else if (frameIn.asOrbitRelativeFrame() != null) {
249 
250             final LOFType lofIn = frameIn.asOrbitRelativeFrame().getLofType();
251 
252             if (lofIn != null) {
253                 frameInToPivot =
254                         lofIn.transformFromInertial(date, pv.getPVCoordinates(date, inertialPivotFrame)).getInverse();
255             }
256             else {
257                 throw new OrekitException(OrekitMessages.UNSUPPORTED_TRANSFORM, frameIn.getName(),
258                                           inertialPivotFrame.getName());
259             }
260         }
261 
262         //Celestial body frame
263         else if (frameIn.asCelestialBodyFrame() != null) {
264             throw new OrekitException(OrekitMessages.UNSUPPORTED_TRANSFORM, frameIn.asCelestialBodyFrame().getName(),
265                                       inertialPivotFrame.getName());
266         }
267 
268         // Spacecraft body frame
269         else {
270             throw new OrekitException(OrekitMessages.UNSUPPORTED_TRANSFORM, frameIn.getName(),
271                                       inertialPivotFrame.getName());
272         }
273 
274         return frameInToPivot;
275     }
276 
277 }