1 /* Copyright 2022-2024 Romain Serra
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.frames;
18
19 import org.hipparchus.geometry.euclidean.threed.Vector3D;
20 import org.hipparchus.geometry.euclidean.threed.Rotation;
21 import org.orekit.time.AbsoluteDate;
22 import org.orekit.utils.PVCoordinates;
23 import org.orekit.utils.TimeStampedPVCoordinates;
24
25 /**
26 * A transform that only includes translation and rotation as well as their respective rates.
27 * It is kinematic in the sense that it cannot transform an acceleration vector.
28 *
29 * @author Romain Serra
30 * @see StaticTransform
31 * @see Transform
32 * @since 12.1
33 */
34 public interface KinematicTransform extends StaticTransform {
35
36 /**
37 * Get the identity kinematic transform.
38 *
39 * @return identity transform.
40 */
41 static KinematicTransform getIdentity() {
42 return Transform.IDENTITY;
43 }
44
45 /** Compute a composite velocity.
46 * @param first first applied transform
47 * @param second second applied transform
48 * @return velocity part of the composite transform
49 */
50 static Vector3D compositeVelocity(final KinematicTransform first, final KinematicTransform second) {
51
52 final Vector3D v1 = first.getVelocity();
53 final Rotation r1 = first.getRotation();
54 final Vector3D o1 = first.getRotationRate();
55 final Vector3D p2 = second.getTranslation();
56 final Vector3D v2 = second.getVelocity();
57
58 final Vector3D crossP = Vector3D.crossProduct(o1, p2);
59
60 return v1.add(r1.applyInverseTo(v2.add(crossP)));
61 }
62
63 /** Compute a composite rotation rate.
64 * @param first first applied transform
65 * @param second second applied transform
66 * @return rotation rate part of the composite transform
67 */
68 static Vector3D compositeRotationRate(final KinematicTransform first, final KinematicTransform second) {
69
70 final Vector3D o1 = first.getRotationRate();
71 final Rotation r2 = second.getRotation();
72 final Vector3D o2 = second.getRotationRate();
73
74 return o2.add(r2.applyTo(o1));
75 }
76
77 /** Transform {@link PVCoordinates}, without the acceleration vector.
78 * @param pv the position-velocity couple to transform.
79 * @return transformed position-velocity
80 */
81 default PVCoordinates transformOnlyPV(final PVCoordinates pv) {
82 final Vector3D transformedP = transformPosition(pv.getPosition());
83 final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), transformedP);
84 final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
85 return new PVCoordinates(transformedP, transformedV);
86 }
87
88 /** Transform {@link TimeStampedPVCoordinates}, without the acceleration vector.
89 * <p>
90 * In order to allow the user more flexibility, this method does <em>not</em> check for
91 * consistency between the transform {@link #getDate() date} and the time-stamped
92 * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
93 * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
94 * the input argument, regardless of the instance {@link #getDate() date}.
95 * </p>
96 * @param pv the position-velocity couple to transform.
97 * @return transformed position-velocity
98 */
99 default TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) {
100 final Vector3D transformedP = transformPosition(pv.getPosition());
101 final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), transformedP);
102 final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
103 return new TimeStampedPVCoordinates(pv.getDate(), transformedP, transformedV);
104 }
105
106 /** Get the first time derivative of the translation.
107 * @return first time derivative of the translation
108 * @see #getTranslation()
109 */
110 Vector3D getVelocity();
111
112 /** Get the first time derivative of the rotation.
113 * <p>The norm represents the angular rate.</p>
114 * @return First time derivative of the rotation
115 * @see #getRotation()
116 */
117 Vector3D getRotationRate();
118
119 /**
120 * Get the inverse transform of the instance.
121 *
122 * @return inverse transform of the instance
123 */
124 KinematicTransform getInverse();
125
126 /**
127 * Build a transform by combining two existing ones.
128 * <p>
129 * Note that the dates of the two existing transformed are <em>ignored</em>,
130 * and the combined transform date is set to the date supplied in this
131 * constructor without any attempt to shift the raw transforms. This is a
132 * design choice allowing user full control of the combination.
133 * </p>
134 *
135 * @param date date of the transform
136 * @param first first transform applied
137 * @param second second transform applied
138 * @return the newly created kinematic transform that has the same effect as
139 * applying {@code first}, then {@code second}.
140 * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
141 */
142 static KinematicTransform compose(final AbsoluteDate date,
143 final KinematicTransform first,
144 final KinematicTransform second) {
145 final Vector3D composedTranslation = StaticTransform.compositeTranslation(first, second);
146 final Vector3D composedTranslationRate = KinematicTransform.compositeVelocity(first, second);
147 return of(date, new PVCoordinates(composedTranslation, composedTranslationRate),
148 StaticTransform.compositeRotation(first, second),
149 KinematicTransform.compositeRotationRate(first, second));
150 }
151
152 /**
153 * Create a new kinematic transform from a rotation and zero, constant translation.
154 *
155 * @param date of translation.
156 * @param rotation to apply after the translation. That is after translating
157 * applying this rotation produces positions expressed in
158 * the new frame.
159 * @param rotationRate rate of rotation
160 * @return the newly created kinematic transform.
161 * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
162 */
163 static KinematicTransform of(final AbsoluteDate date,
164 final Rotation rotation,
165 final Vector3D rotationRate) {
166 return of(date, PVCoordinates.ZERO, rotation, rotationRate);
167 }
168
169 /**
170 * Create a new kinematic transform from a translation and its rate.
171 *
172 * @param date of translation.
173 * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
174 * opposite of the coordinates of the new origin in the
175 * old frame.
176 * @return the newly created kinematic transform.
177 * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
178 */
179 static KinematicTransform of(final AbsoluteDate date,
180 final PVCoordinates pvCoordinates) {
181 return of(date, pvCoordinates, Rotation.IDENTITY, Vector3D.ZERO);
182 }
183
184 /**
185 * Create a new kinematic transform from a translation and rotation.
186 *
187 * @param date of translation.
188 * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
189 * opposite of the coordinates of the new origin in the
190 * old frame.
191 * @param rotation to apply after the translation. That is after
192 * translating applying this rotation produces positions
193 * expressed in the new frame.
194 * @param rotationRate rate of rotation
195 * @return the newly created kinematic transform.
196 * @see #compose(AbsoluteDate, KinematicTransform, KinematicTransform)
197 * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
198 * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
199 */
200 static KinematicTransform of(final AbsoluteDate date, final PVCoordinates pvCoordinates,
201 final Rotation rotation, final Vector3D rotationRate) {
202 return new KinematicTransform() {
203
204 @Override
205 public KinematicTransform getInverse() {
206 final Rotation r = getRotation();
207 final Vector3D rp = r.applyTo(getTranslation());
208 final Vector3D pInv = rp.negate();
209 final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), rp);
210 final Vector3D vInv = crossP.subtract(getRotation().applyTo(getVelocity()));
211 final Rotation rInv = r.revert();
212 return KinematicTransform.of(getDate(), new PVCoordinates(pInv, vInv),
213 rInv, rInv.applyTo(getRotationRate()).negate());
214 }
215
216 @Override
217 public AbsoluteDate getDate() {
218 return date;
219 }
220
221 @Override
222 public Vector3D getTranslation() {
223 return pvCoordinates.getPosition();
224 }
225
226 @Override
227 public Rotation getRotation() {
228 return rotation;
229 }
230
231 @Override
232 public Vector3D getVelocity() {
233 return pvCoordinates.getVelocity();
234 }
235
236 @Override
237 public Vector3D getRotationRate() {
238 return rotationRate;
239 }
240 };
241 }
242
243 }