1 /* Copyright 2002-2021 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.Field;
20 import org.hipparchus.CalculusFieldElement;
21 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
22 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23 import org.hipparchus.geometry.euclidean.threed.Rotation;
24 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.orekit.frames.FieldTransform;
27 import org.orekit.frames.Frame;
28 import org.orekit.frames.Transform;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.FieldAbsoluteDate;
31 import org.orekit.utils.FieldPVCoordinates;
32 import org.orekit.utils.FieldPVCoordinatesProvider;
33 import org.orekit.utils.PVCoordinates;
34 import org.orekit.utils.PVCoordinatesProvider;
35 import org.orekit.utils.TimeStampedAngularCoordinates;
36 import org.orekit.utils.TimeStampedFieldAngularCoordinates;
37 import org.orekit.utils.TimeStampedFieldPVCoordinates;
38 import org.orekit.utils.TimeStampedPVCoordinates;
39
40
41 /**
42 * This class handles yaw compensation attitude provider.
43
44 * <p>
45 * Yaw compensation is mainly used for Earth observation satellites. As a
46 * satellites moves along its track, the image of ground points move on
47 * the focal point of the optical sensor. This motion is a combination of
48 * the satellite motion, but also on the Earth rotation and on the current
49 * attitude (in particular if the pointing includes Roll or Pitch offset).
50 * In order to reduce geometrical distortion, the yaw angle is changed a
51 * little from the simple ground pointing attitude such that the apparent
52 * motion of ground points is along a prescribed axis (orthogonal to the
53 * optical sensors rows), taking into account all effects.
54 * </p>
55 * <p>
56 * This attitude is implemented as a wrapper on top of an underlying ground
57 * pointing law that defines the roll and pitch angles.
58 * </p>
59 * <p>
60 * Instances of this class are guaranteed to be immutable.
61 * </p>
62 * @see GroundPointing
63 * @author Véronique Pommier-Maurussane
64 */
65 public class YawCompensation extends GroundPointing implements AttitudeProviderModifier {
66
67 /** J axis. */
68 private static final PVCoordinates PLUS_J =
69 new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
70
71 /** K axis. */
72 private static final PVCoordinates PLUS_K =
73 new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);
74
75 /** Underlying ground pointing attitude provider. */
76 private final GroundPointing groundPointingLaw;
77
78 /** Creates a new instance.
79 * @param inertialFrame frame in which orbital velocities are computed
80 * @param groundPointingLaw ground pointing attitude provider without yaw compensation
81 * @since 7.1
82 */
83 public YawCompensation(final Frame inertialFrame, final GroundPointing groundPointingLaw) {
84 super(inertialFrame, groundPointingLaw.getBodyFrame());
85 this.groundPointingLaw = groundPointingLaw;
86 }
87
88 /** Get the underlying (ground pointing) attitude provider.
89 * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance
90 */
91 public AttitudeProvider getUnderlyingAttitudeProvider() {
92 return groundPointingLaw;
93 }
94
95 /** {@inheritDoc} */
96 public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
97 final AbsoluteDate date, final Frame frame) {
98 return groundPointingLaw.getTargetPV(pvProv, date, frame);
99 }
100
101 /** {@inheritDoc} */
102 public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
103 final FieldAbsoluteDate<T> date,
104 final Frame frame) {
105 return groundPointingLaw.getTargetPV(pvProv, date, frame);
106 }
107
108 /** Compute the base system state at given date, without compensation.
109 * @param pvProv provider for PV coordinates
110 * @param date date at which state is requested
111 * @param frame reference frame from which attitude is computed
112 * @return satellite base attitude state, i.e without compensation.
113 */
114 public Attitude getBaseState(final PVCoordinatesProvider pvProv,
115 final AbsoluteDate date, final Frame frame) {
116 return groundPointingLaw.getAttitude(pvProv, date, frame);
117 }
118
119 /** Compute the base system state at given date, without compensation.
120 * @param pvProv provider for PV coordinates
121 * @param date date at which state is requested
122 * @param frame reference frame from which attitude is computed
123 * @param <T> type of the field elements
124 * @return satellite base attitude state, i.e without compensation.
125 * @since 9.0
126 */
127 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getBaseState(final FieldPVCoordinatesProvider<T> pvProv,
128 final FieldAbsoluteDate<T> date, final Frame frame) {
129 return groundPointingLaw.getAttitude(pvProv, date, frame);
130 }
131
132 /** {@inheritDoc} */
133 @Override
134 public Attitude getAttitude(final PVCoordinatesProvider pvProv,
135 final AbsoluteDate date, final Frame frame) {
136
137 final Transform bodyToRef = getBodyFrame().getTransformTo(frame, date);
138
139 // compute sliding target ground point
140 final PVCoordinates slidingRef = getTargetPV(pvProv, date, frame);
141 final PVCoordinates slidingBody = bodyToRef.getInverse().transformPVCoordinates(slidingRef);
142
143 // compute relative position of sliding ground point with respect to satellite
144 final PVCoordinates relativePosition =
145 new PVCoordinates(pvProv.getPVCoordinates(date, frame), slidingRef);
146
147 // compute relative velocity of fixed ground point with respect to sliding ground point
148 // the velocity part of the transformPVCoordinates for the sliding point ps
149 // from body frame to reference frame is:
150 // d(ps_ref)/dt = r(d(ps_body)/dt + dq/dt) - Ω ⨯ ps_ref
151 // where r is the rotation part of the transform, Ω is the corresponding
152 // angular rate, and dq/dt is the derivative of the translation part of the
153 // transform (the translation itself, without derivative, is hidden in the
154 // ps_ref part in the cross product).
155 // The sliding point ps is co-located to a fixed ground point pf (i.e. they have the
156 // same position at time of computation), but this fixed point as zero velocity
157 // with respect to the ground. So the velocity part of the transformPVCoordinates
158 // for this fixed point can be computed using the same formula as above:
159 // from body frame to reference frame is:
160 // d(pf_ref)/dt = r(0 + dq/dt) - Ω ⨯ pf_ref
161 // so remembering that the two points are at the same location at computation time,
162 // i.e. that at t=0 pf_ref=ps_ref, the relative velocity between the fixed point
163 // and the sliding point is given by the simple expression:
164 // d(ps_ref)/dt - d(pf_ref)/dt = r(d(ps_body)/dt)
165 // the acceleration is computed by differentiating the expression, which gives:
166 // d²(ps_ref)/dt² - d²(pf_ref)/dt² = r(d²(ps_body)/dt²) - Ω ⨯ [d(ps_ref)/dt - d(pf_ref)/dt]
167 final Vector3D v = bodyToRef.getRotation().applyTo(slidingBody.getVelocity());
168 final Vector3D a = new Vector3D(+1, bodyToRef.getRotation().applyTo(slidingBody.getAcceleration()),
169 -1, Vector3D.crossProduct(bodyToRef.getRotationRate(), v));
170 final PVCoordinates relativeVelocity = new PVCoordinates(v, a, Vector3D.ZERO);
171
172 final PVCoordinates relativeNormal =
173 PVCoordinates.crossProduct(relativePosition, relativeVelocity).normalize();
174
175 // attitude definition :
176 // . Z satellite axis points to sliding target
177 // . target relative velocity is in (Z, X) plane, in the -X half plane part
178 return new Attitude(frame,
179 new TimeStampedAngularCoordinates(date,
180 relativePosition.normalize(),
181 relativeNormal.normalize(),
182 PLUS_K, PLUS_J,
183 1.0e-9));
184
185 }
186
187 /** {@inheritDoc} */
188 @Override
189 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
190 final FieldAbsoluteDate<T> date, final Frame frame) {
191
192 final Field<T> field = date.getField();
193 final FieldVector3D<T> zero = FieldVector3D.getZero(field);
194 final FieldPVCoordinates<T> plusJ = new FieldPVCoordinates<>(FieldVector3D.getPlusJ(field), zero, zero);
195 final FieldPVCoordinates<T> plusK = new FieldPVCoordinates<>(FieldVector3D.getPlusK(field), zero, zero);
196
197 final FieldTransform<T> bodyToRef = getBodyFrame().getTransformTo(frame, date);
198
199 // compute sliding target ground point
200 final FieldPVCoordinates<T> slidingRef = getTargetPV(pvProv, date, frame);
201 final FieldPVCoordinates<T> slidingBody = bodyToRef.getInverse().transformPVCoordinates(slidingRef);
202
203 // compute relative position of sliding ground point with respect to satellite
204 final FieldPVCoordinates<T> relativePosition =
205 new FieldPVCoordinates<>(pvProv.getPVCoordinates(date, frame), slidingRef);
206
207 // compute relative velocity of fixed ground point with respect to sliding ground point
208 // the velocity part of the transformPVCoordinates for the sliding point ps
209 // from body frame to reference frame is:
210 // d(ps_ref)/dt = r(d(ps_body)/dt + dq/dt) - Ω ⨯ ps_ref
211 // where r is the rotation part of the transform, Ω is the corresponding
212 // angular rate, and dq/dt is the derivative of the translation part of the
213 // transform (the translation itself, without derivative, is hidden in the
214 // ps_ref part in the cross product).
215 // The sliding point ps is co-located to a fixed ground point pf (i.e. they have the
216 // same position at time of computation), but this fixed point as zero velocity
217 // with respect to the ground. So the velocity part of the transformPVCoordinates
218 // for this fixed point can be computed using the same formula as above:
219 // from body frame to reference frame is:
220 // d(pf_ref)/dt = r(0 + dq/dt) - Ω ⨯ pf_ref
221 // so remembering that the two points are at the same location at computation time,
222 // i.e. that at t=0 pf_ref=ps_ref, the relative velocity between the fixed point
223 // and the sliding point is given by the simple expression:
224 // d(ps_ref)/dt - d(pf_ref)/dt = r(d(ps_body)/dt)
225 // the acceleration is computed by differentiating the expression, which gives:
226 // d²(ps_ref)/dt² - d²(pf_ref)/dt² = r(d²(ps_body)/dt²) - Ω ⨯ [d(ps_ref)/dt - d(pf_ref)/dt]
227 final FieldVector3D<T> v = bodyToRef.getRotation().applyTo(slidingBody.getVelocity());
228 final FieldVector3D<T> a = new FieldVector3D<>(+1, bodyToRef.getRotation().applyTo(slidingBody.getAcceleration()),
229 -1, FieldVector3D.crossProduct(bodyToRef.getRotationRate(), v));
230 final FieldPVCoordinates<T> relativeVelocity = new FieldPVCoordinates<>(v, a, FieldVector3D.getZero(date.getField()));
231
232 final FieldPVCoordinates<T> relativeNormal =
233 relativePosition.crossProduct(relativeVelocity).normalize();
234
235 // attitude definition :
236 // . Z satellite axis points to sliding target
237 // . target relative velocity is in (Z, X) plane, in the -X half plane part
238 return new FieldAttitude<>(frame,
239 new TimeStampedFieldAngularCoordinates<>(date,
240 relativePosition.normalize(),
241 relativeNormal.normalize(),
242 plusK, plusJ,
243 1.0e-9));
244
245 }
246
247 /** Compute the yaw compensation angle at date.
248 * @param pvProv provider for PV coordinates
249 * @param date date at which compensation is requested
250 * @param frame reference frame from which attitude is computed
251 * @return yaw compensation angle for orbit.
252 */
253 public double getYawAngle(final PVCoordinatesProvider pvProv,
254 final AbsoluteDate date, final Frame frame) {
255 final Rotation rBase = getBaseState(pvProv, date, frame).getRotation();
256 final Rotation rCompensated = getAttitude(pvProv, date, frame).getRotation();
257 final Rotation compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
258 return -compensation.applyTo(Vector3D.PLUS_I).getAlpha();
259 }
260
261 /** Compute the yaw compensation angle at date.
262 * @param pvProv provider for PV coordinates
263 * @param date date at which compensation is requested
264 * @param frame reference frame from which attitude is computed
265 * @param <T> type of the field elements
266 * @return yaw compensation angle for orbit.
267 * @since 9.0
268 */
269 public <T extends CalculusFieldElement<T>> T getYawAngle(final FieldPVCoordinatesProvider<T> pvProv,
270 final FieldAbsoluteDate<T> date,
271 final Frame frame) {
272 final FieldRotation<T> rBase = getBaseState(pvProv, date, frame).getRotation();
273 final FieldRotation<T> rCompensated = getAttitude(pvProv, date, frame).getRotation();
274 final FieldRotation<T> compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
275 return compensation.applyTo(Vector3D.PLUS_I).getAlpha().negate();
276 }
277
278 }