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 }