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.FieldVector3D;
22 import org.hipparchus.geometry.euclidean.threed.Vector3D;
23 import org.orekit.frames.Frame;
24 import org.orekit.time.AbsoluteDate;
25 import org.orekit.time.FieldAbsoluteDate;
26 import org.orekit.utils.FieldPVCoordinates;
27 import org.orekit.utils.FieldPVCoordinatesProvider;
28 import org.orekit.utils.PVCoordinates;
29 import org.orekit.utils.PVCoordinatesProvider;
30 import org.orekit.utils.TimeStampedAngularCoordinates;
31 import org.orekit.utils.TimeStampedFieldAngularCoordinates;
32 import org.orekit.utils.TimeStampedFieldPVCoordinates;
33 import org.orekit.utils.TimeStampedPVCoordinates;
34
35
36 /**
37 * This class handles yaw steering law.
38
39 * <p>
40 * Yaw steering is mainly used for low Earth orbiting satellites with no
41 * missions-related constraints on yaw angle. It sets the yaw angle in
42 * such a way the solar arrays have maximal lighting without changing the
43 * roll and pitch.
44 * </p>
45 * <p>
46 * The motion in yaw is smooth when the Sun is far from the orbital plane,
47 * but gets more and more <i>square like</i> as the Sun gets closer to the
48 * orbital plane. The degenerate extreme case with the Sun in the orbital
49 * plane leads to a yaw angle switching between two steady states, with
50 * instantaneaous π radians rotations at each switch, two times per orbit.
51 * This degenerate case is clearly not operationally sound so another pointing
52 * mode is chosen when Sun comes closer than some predefined threshold to the
53 * orbital plane.
54 * </p>
55 * <p>
56 * This class can handle (for now) only a theoretically perfect yaw steering
57 * (i.e. the yaw angle is exactly the optimal angle). Smoothed yaw steering with a
58 * few sine waves approaching the optimal angle will be added in the future if
59 * needed.
60 * </p>
61 * <p>
62 * This attitude is implemented as a wrapper on top of an underlying ground
63 * pointing law that defines the roll and pitch angles.
64 * </p>
65 * <p>
66 * Instances of this class are guaranteed to be immutable.
67 * </p>
68 * @see GroundPointing
69 * @author Luc Maisonobe
70 */
71 public class YawSteering extends GroundPointing implements AttitudeProviderModifier {
72
73 /** Pointing axis. */
74 private static final PVCoordinates PLUS_Z =
75 new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);
76
77 /** Underlying ground pointing attitude provider. */
78 private final GroundPointing groundPointingLaw;
79
80 /** Sun motion model. */
81 private final PVCoordinatesProvider sun;
82
83 /** Normal to the plane where the Sun must remain. */
84 private final PVCoordinates phasingNormal;
85
86 /** Creates a new instance.
87 * @param inertialFrame frame in which orbital velocities are computed
88 * @param groundPointingLaw ground pointing attitude provider without yaw compensation
89 * @param sun sun motion model
90 * @param phasingAxis satellite axis that must be roughly in Sun direction
91 * (if solar arrays rotation axis is Y, then this axis should be either +X or -X)
92 * @since 7.1
93 */
94 public YawSteering(final Frame inertialFrame,
95 final GroundPointing groundPointingLaw,
96 final PVCoordinatesProvider sun,
97 final Vector3D phasingAxis) {
98 super(inertialFrame, groundPointingLaw.getBodyFrame());
99 this.groundPointingLaw = groundPointingLaw;
100 this.sun = sun;
101 this.phasingNormal = new PVCoordinates(Vector3D.crossProduct(Vector3D.PLUS_K, phasingAxis).normalize(),
102 Vector3D.ZERO,
103 Vector3D.ZERO);
104 }
105
106 /** Get the underlying (ground pointing) attitude provider.
107 * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance
108 */
109 public AttitudeProvider getUnderlyingAttitudeProvider() {
110 return groundPointingLaw;
111 }
112
113 /** {@inheritDoc} */
114 public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
115 final AbsoluteDate date, final Frame frame) {
116 return groundPointingLaw.getTargetPV(pvProv, date, frame);
117 }
118
119 /** {@inheritDoc} */
120 public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
121 final FieldAbsoluteDate<T> date,
122 final Frame frame) {
123 return groundPointingLaw.getTargetPV(pvProv, date, frame);
124 }
125
126 /** Compute the base system state at given date, without compensation.
127 * @param pvProv provider for PV coordinates
128 * @param date date at which state is requested
129 * @param frame reference frame from which attitude is computed
130 * @return satellite base attitude state, i.e without compensation.
131 */
132 public Attitude getBaseState(final PVCoordinatesProvider pvProv,
133 final AbsoluteDate date, final Frame frame) {
134 return groundPointingLaw.getAttitude(pvProv, date, frame);
135 }
136
137 /** Compute the base system state at given date, without compensation.
138 * @param pvProv provider for PV coordinates
139 * @param date date at which state is requested
140 * @param frame reference frame from which attitude is computed
141 * @param <T> type of the field elements
142 * @return satellite base attitude state, i.e without compensation.
143 * @since 9.0
144 */
145 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getBaseState(final FieldPVCoordinatesProvider<T> pvProv,
146 final FieldAbsoluteDate<T> date, final Frame frame) {
147 return groundPointingLaw.getAttitude(pvProv, date, frame);
148 }
149
150 /** {@inheritDoc} */
151 @Override
152 public Attitude getAttitude(final PVCoordinatesProvider pvProv,
153 final AbsoluteDate date, final Frame frame) {
154
155 // attitude from base attitude provider
156 final Attitude base = getBaseState(pvProv, date, frame);
157
158 // Compensation rotation definition :
159 // . Z satellite axis is unchanged
160 // . phasing axis shall be aligned to sun direction
161 final PVCoordinates sunDirection = new PVCoordinates(pvProv.getPVCoordinates(date, frame),
162 sun.getPVCoordinates(date, frame));
163 final PVCoordinates sunNormal =
164 PVCoordinates.crossProduct(PLUS_Z, base.getOrientation().applyTo(sunDirection));
165 final TimeStampedAngularCoordinates compensation =
166 new TimeStampedAngularCoordinates(date,
167 PLUS_Z, sunNormal.normalize(),
168 PLUS_Z, phasingNormal,
169 1.0e-9);
170
171 // add compensation
172 return new Attitude(frame, compensation.addOffset(base.getOrientation()));
173
174 }
175
176 /** {@inheritDoc} */
177 @Override
178 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
179 final FieldAbsoluteDate<T> date, final Frame frame) {
180
181 final Field<T> field = date.getField();
182 final FieldVector3D<T> zero = FieldVector3D.getZero(field);
183 final FieldPVCoordinates<T> plusZ = new FieldPVCoordinates<>(FieldVector3D.getPlusK(field), zero, zero);
184
185 // attitude from base attitude provider
186 final FieldAttitude<T> base = getBaseState(pvProv, date, frame);
187
188 // Compensation rotation definition :
189 // . Z satellite axis is unchanged
190 // . phasing axis shall be aligned to sun direction
191 final FieldPVCoordinates<T> sunDirection =
192 new FieldPVCoordinates<>(pvProv.getPVCoordinates(date, frame),
193 new FieldPVCoordinates<>(field,
194 sun.getPVCoordinates(date.toAbsoluteDate(), frame)));
195 final FieldPVCoordinates<T> sunNormal =
196 plusZ.crossProduct(base.getOrientation().applyTo(sunDirection));
197 final TimeStampedFieldAngularCoordinates<T> compensation =
198 new TimeStampedFieldAngularCoordinates<>(date,
199 plusZ, sunNormal.normalize(),
200 plusZ, new FieldPVCoordinates<>(field, phasingNormal),
201 1.0e-9);
202
203 // add compensation
204 return new FieldAttitude<>(frame, compensation.addOffset(base.getOrientation()));
205
206 }
207
208 }