1 /* Copyright 2002-2025 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.attitudes;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
22 import org.hipparchus.geometry.euclidean.threed.Rotation;
23 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
24 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27 import org.orekit.frames.FieldTransform;
28 import org.orekit.frames.Frame;
29 import org.orekit.frames.LOF;
30 import org.orekit.frames.Transform;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.FieldAbsoluteDate;
33 import org.orekit.utils.FieldPVCoordinates;
34 import org.orekit.utils.FieldPVCoordinatesProvider;
35 import org.orekit.utils.PVCoordinates;
36 import org.orekit.utils.PVCoordinatesProvider;
37
38
39 /**
40 * Attitude law defined by fixed Roll, Pitch and Yaw angles (in any order)
41 * with respect to a local orbital frame.
42
43 * <p>
44 * The attitude provider is defined as a rotation offset from some local orbital frame.
45 * @author Véronique Pommier-Maurussane
46 */
47 public class LofOffset implements AttitudeProvider {
48
49 /** Local Orbital Frame. */
50 private final LOF lof;
51
52 /** Rotation from local orbital frame. */
53 private final Rotation offset;
54
55 /** Inertial frame with respect to which orbit should be computed. */
56 private final Frame inertialFrame;
57
58 /** Create a LOF-aligned attitude.
59 * <p>
60 * Calling this constructor is equivalent to call
61 * {@code LofOffset(inertialFrame, LOF, RotationOrder.XYZ, 0, 0, 0)}
62 * </p>
63 * @param inertialFrame inertial frame with respect to which orbit should be computed
64 * @param lof local orbital frame
65 */
66 public LofOffset(final Frame inertialFrame, final LOF lof) {
67 this(inertialFrame, lof, RotationOrder.XYZ, 0, 0, 0);
68 }
69
70 /** Creates new instance.
71 * <p>
72 * An important thing to note is that the rotation order and angles signs used here
73 * are compliant with an <em>attitude</em> definition, i.e. they correspond to
74 * a frame that rotate in a field of fixed vectors. So to retrieve the angles
75 * provided here from the Hipparchus underlying rotation, one has to either use the
76 * {@link RotationConvention#VECTOR_OPERATOR} and <em>revert</em> the rotation, or
77 * to use {@link RotationConvention#FRAME_TRANSFORM} as in the following code snippet:
78 * </p>
79 * <pre>
80 * LofOffset law = new LofOffset(inertial, LOF, order, alpha1, alpha2, alpha3);
81 * Rotation offsetAtt = law.getAttitude(orbit).getRotation();
82 * Rotation alignedAtt = new LofOffset(inertial, LOF).getAttitude(orbit).getRotation();
83 * Rotation offsetProper = offsetAtt.compose(alignedAtt.revert(), RotationConvention.VECTOR_OPERATOR);
84 *
85 * // note the call to revert and the conventions in the following statement
86 * double[] anglesV = offsetProper.revert().getAngles(order, RotationConvention.VECTOR_OPERATOR);
87 * System.out.format(Locale.US, "%f == %f%n", alpha1, anglesV[0]);
88 * System.out.format(Locale.US, "%f == %f%n", alpha2, anglesV[1]);
89 * System.out.format(Locale.US, "%f == %f%n", alpha3, anglesV[2]);
90 *
91 * // note the conventions in the following statement
92 * double[] anglesF = offsetProper.getAngles(order, RotationConvention.FRAME_TRANSFORM);
93 * System.out.format(Locale.US, "%f == %f%n", alpha1, anglesF[0]);
94 * System.out.format(Locale.US, "%f == %f%n", alpha2, anglesF[1]);
95 * System.out.format(Locale.US, "%f == %f%n", alpha3, anglesF[2]);
96 * </pre>
97 * @param inertialFrame inertial frame with respect to which orbit should be computed
98 * @param lof local orbital frame
99 * @param order order of rotations to use for (alpha1, alpha2, alpha3) composition
100 * @param alpha1 angle of the first elementary rotation
101 * @param alpha2 angle of the second elementary rotation
102 * @param alpha3 angle of the third elementary rotation
103 */
104 public LofOffset(final Frame inertialFrame, final LOF lof,
105 final RotationOrder order, final double alpha1,
106 final double alpha2, final double alpha3) {
107 this.lof = lof;
108 this.offset = new Rotation(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3).revert();
109 if (!inertialFrame.isPseudoInertial()) {
110 throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
111 inertialFrame.getName());
112 }
113 this.inertialFrame = inertialFrame;
114 }
115
116 /**
117 * Get the local orbital frame.
118 * @return the local orbital frame.
119 */
120 public LOF getLof() {
121 return this.lof;
122 }
123
124 /**
125 * Get the rotational offset.
126 * @return the rotational offset.
127 */
128 public Rotation getOffset() {
129 return this.offset;
130 }
131
132 /**
133 * Get the inertial frame.
134 * @return the inertial frame.
135 */
136 public Frame getInertialFrame() {
137 return this.inertialFrame;
138 }
139
140 /** {@inheritDoc} */
141 @Override
142 public Attitude getAttitude(final PVCoordinatesProvider pvProv,
143 final AbsoluteDate date, final Frame frame) {
144
145 // construction of the local orbital frame, using PV from inertial frame
146 final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame);
147 final Transform inertialToLof = lof.transformFromInertial(date, pv);
148
149 // take into account the specified start frame (which may not be an inertial one)
150 final Transform frameToInertial = frame.getTransformTo(inertialFrame, date);
151 final Transform frameToLof = new Transform(date, frameToInertial, inertialToLof);
152
153 // compose with offset rotation
154 return new Attitude(date, frame,
155 offset.compose(frameToLof.getRotation(), RotationConvention.VECTOR_OPERATOR),
156 offset.applyTo(frameToLof.getRotationRate()),
157 offset.applyTo(frameToLof.getRotationAcceleration()));
158
159 }
160
161 /** {@inheritDoc} */
162 @Override
163 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
164 final FieldAbsoluteDate<T> date,
165 final Frame frame) {
166
167 // construction of the local orbital frame, using PV from inertial frame
168 final FieldPVCoordinates<T> pv = pvProv.getPVCoordinates(date, inertialFrame);
169 final FieldTransform<T> inertialToLof = lof.transformFromInertial(date, pv);
170
171 // take into account the specified start frame (which may not be an inertial one)
172 final FieldTransform<T> frameToInertial = frame.getTransformTo(inertialFrame, date);
173 final FieldTransform<T> frameToLof = new FieldTransform<>(date, frameToInertial, inertialToLof);
174
175 // compose with offset rotation
176 return new FieldAttitude<>(date, frame,
177 frameToLof.getRotation().compose(offset, RotationConvention.FRAME_TRANSFORM),
178 FieldRotation.applyTo(offset, frameToLof.getRotationRate()),
179 FieldRotation.applyTo(offset, frameToLof.getRotationAcceleration()));
180
181 }
182
183 /** {@inheritDoc} */
184 @Override
185 public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
186 // construction of the local orbital frame, using PV from inertial frame
187 final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame);
188 final Rotation inertialToLof = lof.rotationFromInertial(date, pv);
189
190 // take into account the specified start frame (which may not be an inertial one)
191 final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM;
192 final Rotation frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation();
193 final Rotation frameToLof = frameToInertial.compose(inertialToLof, rotationConvention);
194
195 // compose with offset rotation
196 return frameToLof.compose(offset, rotationConvention);
197 }
198
199 /** {@inheritDoc} */
200 @Override
201 public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
202 final FieldAbsoluteDate<T> date,
203 final Frame frame) {
204 // construction of the local orbital frame, using PV from inertial frame
205 final FieldPVCoordinates<T> pv = pvProv.getPVCoordinates(date, inertialFrame);
206 final Field<T> field = date.getField();
207 final FieldRotation<T> inertialToLof = lof.rotationFromInertial(field, date, pv);
208
209 // take into account the specified start frame (which may not be an inertial one)
210 final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM;
211 final FieldRotation<T> frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation();
212 final FieldRotation<T> frameToLof = frameToInertial.compose(inertialToLof, rotationConvention);
213
214 // compose with offset rotation
215 return frameToLof.compose(offset, rotationConvention);
216 }
217 }