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.utils;
18
19 import org.hipparchus.analysis.differentiation.Derivative;
20 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
21 import org.hipparchus.geometry.euclidean.threed.Rotation;
22 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.orekit.time.AbsoluteDate;
25 import org.orekit.time.TimeOffset;
26 import org.orekit.time.TimeStamped;
27
28 /** {@link TimeStamped time-stamped} version of {@link AngularCoordinates}.
29 * <p>Instances of this class are guaranteed to be immutable.</p>
30 * @author Luc Maisonobe
31 * @since 7.0
32 */
33 public class TimeStampedAngularCoordinates extends AngularCoordinates implements TimeStamped {
34
35 /** The date. */
36 private final AbsoluteDate date;
37
38 /** Builds a rotation/rotation rate pair.
39 * @param date coordinates date
40 * @param coordinates coordinates
41 * @since 14.0
42 */
43 public TimeStampedAngularCoordinates(final AbsoluteDate date,
44 final AngularCoordinates coordinates) {
45 this(date, coordinates.getRotation(), coordinates.getRotationRate(), coordinates.getRotationAcceleration());
46 }
47
48 /** Builds a rotation/rotation rate pair.
49 * @param date coordinates date
50 * @param rotation rotation
51 * @param rotationRate rotation rate Ω (rad/s)
52 * @param rotationAcceleration rotation acceleration dΩ/dt (rad²/s²)
53 */
54 public TimeStampedAngularCoordinates(final AbsoluteDate date,
55 final Rotation rotation,
56 final Vector3D rotationRate,
57 final Vector3D rotationAcceleration) {
58 super(rotation, rotationRate, rotationAcceleration);
59 this.date = date;
60 }
61
62 /** Build the rotation that transforms a pair of pv coordinates into another pair.
63
64 * <p><em>WARNING</em>! This method requires much more stringent assumptions on
65 * its parameters than the similar {@link Rotation#Rotation(Vector3D, Vector3D,
66 * Vector3D, Vector3D) constructor} from the {@link Rotation Rotation} class.
67 * As far as the Rotation constructor is concerned, the {@code v₂} vector from
68 * the second pair can be slightly misaligned. The Rotation constructor will
69 * compensate for this misalignment and create a rotation that ensure {@code
70 * v₁ = r(u₁)} and {@code v₂ ∈ plane (r(u₁), r(u₂))}. <em>THIS IS NOT
71 * TRUE ANYMORE IN THIS CLASS</em>! As derivatives are involved and must be
72 * preserved, this constructor works <em>only</em> if the two pairs are fully
73 * consistent, i.e. if a rotation exists that fulfill all the requirements: {@code
74 * v₁ = r(u₁)}, {@code v₂ = r(u₂)}, {@code dv₁/dt = dr(u₁)/dt}, {@code dv₂/dt
75 * = dr(u₂)/dt}, {@code d²v₁/dt² = d²r(u₁)/dt²}, {@code d²v₂/dt² = d²r(u₂)/dt²}.</p>
76
77 * @param date coordinates date
78 * @param u1 first vector of the origin pair
79 * @param u2 second vector of the origin pair
80 * @param v1 desired image of u1 by the rotation
81 * @param v2 desired image of u2 by the rotation
82 * @param tolerance relative tolerance factor used to check singularities
83 */
84 public TimeStampedAngularCoordinates(final AbsoluteDate date,
85 final PVCoordinates u1, final PVCoordinates u2,
86 final PVCoordinates v1, final PVCoordinates v2,
87 final double tolerance) {
88 super(u1, u2, v1, v2, tolerance);
89 this.date = date;
90 }
91
92 /** Build one of the rotations that transform one pv coordinates into another one.
93
94 * <p>Except for a possible scale factor, if the instance were
95 * applied to the vector u it will produce the vector v. There is an
96 * infinite number of such rotations, this constructor choose the
97 * one with the smallest associated angle (i.e. the one whose axis
98 * is orthogonal to the (u, v) plane). If u and v are collinear, an
99 * arbitrary rotation axis is chosen.</p>
100
101 * @param date coordinates date
102 * @param u origin vector
103 * @param v desired image of u by the rotation
104 */
105 public TimeStampedAngularCoordinates(final AbsoluteDate date,
106 final PVCoordinates u, final PVCoordinates v) {
107 super(u, v);
108 this.date = date;
109 }
110
111 /** Builds a TimeStampedAngularCoordinates from a {@link FieldRotation}<{@link Derivative}>.
112 * <p>
113 * The rotation components must have time as their only derivation parameter and
114 * have consistent derivation orders.
115 * </p>
116 * @param date coordinates date
117 * @param r rotation with time-derivatives embedded within the coordinates
118 * @param <U> type of the derivative
119 */
120 public <U extends Derivative<U>>TimeStampedAngularCoordinates(final AbsoluteDate date,
121 final FieldRotation<U> r) {
122 super(r);
123 this.date = date;
124 }
125
126 /** {@inheritDoc} */
127 public AbsoluteDate getDate() {
128 return date;
129 }
130
131 /** Revert a rotation/rotation rate pair.
132 * Build a pair which reverse the effect of another pair.
133 * @return a new pair whose effect is the reverse of the effect
134 * of the instance
135 */
136 @Override
137 public TimeStampedAngularCoordinates revert() {
138 return new TimeStampedAngularCoordinates(date, super.revert());
139 }
140
141 /** Get a time-shifted state.
142 * <p>
143 * The state can be slightly shifted to close dates. This shift is based on
144 * a simple linear model. It is <em>not</em> intended as a replacement for
145 * proper attitude propagation but should be sufficient for either small
146 * time shifts or coarse accuracy.
147 * </p>
148 * @param dt time shift in seconds
149 * @return a new state, shifted with respect to the instance (which is immutable)
150 */
151 @Override
152 public TimeStampedAngularCoordinates shiftedBy(final double dt) {
153 final AngularCoordinates sac = super.shiftedBy(dt);
154 return new TimeStampedAngularCoordinates(date.shiftedBy(dt),
155 sac.getRotation(), sac.getRotationRate(), sac.getRotationAcceleration());
156
157 }
158
159 /** Get a time-shifted state.
160 * <p>
161 * The state can be slightly shifted to close dates. This shift is based on
162 * a simple linear model. It is <em>not</em> intended as a replacement for
163 * proper attitude propagation but should be sufficient for either small
164 * time shifts or coarse accuracy.
165 * </p>
166 * @param dt time shift in seconds
167 * @return a new state, shifted with respect to the instance (which is immutable)
168 * @since 13.0
169 */
170 @Override
171 public TimeStampedAngularCoordinates shiftedBy(final TimeOffset dt) {
172 final AngularCoordinates sac = super.shiftedBy(dt);
173 return new TimeStampedAngularCoordinates(date.shiftedBy(dt),
174 sac.getRotation(), sac.getRotationRate(), sac.getRotationAcceleration());
175
176 }
177
178 /** Add an offset from the instance.
179 * <p>
180 * We consider here that the offset rotation is applied first and the
181 * instance is applied afterward. Note that angular coordinates do <em>not</em>
182 * commute under this operation, i.e. {@code a.addOffset(b)} and {@code
183 * b.addOffset(a)} lead to <em>different</em> results in most cases.
184 * </p>
185 * <p>
186 * The two methods {@link #addOffset(AngularCoordinates) addOffset} and
187 * {@link #subtractOffset(AngularCoordinates) subtractOffset} are designed
188 * so that round trip applications are possible. This means that both {@code
189 * ac1.subtractOffset(ac2).addOffset(ac2)} and {@code
190 * ac1.addOffset(ac2).subtractOffset(ac2)} return angular coordinates equal to ac1.
191 * </p>
192 * @param offset offset to subtract
193 * @return new instance, with offset subtracted
194 * @see #subtractOffset(AngularCoordinates)
195 */
196 @Override
197 public TimeStampedAngularCoordinates addOffset(final AngularCoordinates offset) {
198 final Vector3D rOmega = getRotation().applyTo(offset.getRotationRate());
199 final Vector3D rOmegaDot = getRotation().applyTo(offset.getRotationAcceleration());
200 return new TimeStampedAngularCoordinates(date,
201 getRotation().compose(offset.getRotation(), RotationConvention.VECTOR_OPERATOR),
202 getRotationRate().add(rOmega),
203 new Vector3D( 1.0, getRotationAcceleration(),
204 1.0, rOmegaDot,
205 -1.0, Vector3D.crossProduct(getRotationRate(), rOmega)));
206 }
207
208 /** Subtract an offset from the instance.
209 * <p>
210 * We consider here that the offset rotation is applied first and the
211 * instance is applied afterward. Note that angular coordinates do <em>not</em>
212 * commute under this operation, i.e. {@code a.subtractOffset(b)} and {@code
213 * b.subtractOffset(a)} lead to <em>different</em> results in most cases.
214 * </p>
215 * <p>
216 * The two methods {@link #addOffset(AngularCoordinates) addOffset} and
217 * {@link #subtractOffset(AngularCoordinates) subtractOffset} are designed
218 * so that round trip applications are possible. This means that both {@code
219 * ac1.subtractOffset(ac2).addOffset(ac2)} and {@code
220 * ac1.addOffset(ac2).subtractOffset(ac2)} return angular coordinates equal to ac1.
221 * </p>
222 * @param offset offset to subtract
223 * @return new instance, with offset subtracted
224 * @see #addOffset(AngularCoordinates)
225 */
226 @Override
227 public TimeStampedAngularCoordinates subtractOffset(final AngularCoordinates offset) {
228 return addOffset(offset.revert());
229 }
230
231 }