1 /* Copyright 2002-2024 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
33
34 /**
35 * This class handles yaw steering law.
36
37 * <p>
38 * Yaw steering is mainly used for low Earth orbiting satellites with no
39 * missions-related constraints on yaw angle. It sets the yaw angle in
40 * such a way the solar arrays have maximal lighting without changing the
41 * roll and pitch.
42 * </p>
43 * <p>
44 * The motion in yaw is smooth when the Sun is far from the orbital plane,
45 * but gets more and more <i>square like</i> as the Sun gets closer to the
46 * orbital plane. The degenerate extreme case with the Sun in the orbital
47 * plane leads to a yaw angle switching between two steady states, with
48 * instantaneous π radians rotations at each switch, two times per orbit.
49 * This degenerate case is clearly not operationally sound so another pointing
50 * mode is chosen when Sun comes closer than some predefined threshold to the
51 * orbital plane.
52 * </p>
53 * <p>
54 * This class can handle (for now) only a theoretically perfect yaw steering
55 * (i.e. the yaw angle is exactly the optimal angle). Smoothed yaw steering with a
56 * few sine waves approaching the optimal angle will be added in the future if
57 * needed.
58 * </p>
59 * <p>
60 * This attitude is implemented as a wrapper on top of an underlying ground
61 * pointing law that defines the roll and pitch angles.
62 * </p>
63 * <p>
64 * Instances of this class are guaranteed to be immutable.
65 * </p>
66 * @see GroundPointing
67 * @author Luc Maisonobe
68 */
69 public class YawSteering extends GroundPointingAttitudeModifier implements AttitudeProviderModifier {
70
71 /** Pointing axis. */
72 private static final PVCoordinates PLUS_Z =
73 new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);
74
75 /** Sun motion model. */
76 private final PVCoordinatesProvider sun;
77
78 /** Normal to the plane where the Sun must remain. */
79 private final PVCoordinates phasingNormal;
80
81 /** Creates a new instance.
82 * @param inertialFrame frame in which orbital velocities are computed
83 * @param groundPointingLaw ground pointing attitude provider without yaw compensation
84 * @param sun sun motion model
85 * @param phasingAxis satellite axis that must be roughly in Sun direction
86 * (if solar arrays rotation axis is Y, then this axis should be either +X or -X)
87 * @since 7.1
88 */
89 public YawSteering(final Frame inertialFrame,
90 final GroundPointing groundPointingLaw,
91 final PVCoordinatesProvider sun,
92 final Vector3D phasingAxis) {
93 super(inertialFrame, groundPointingLaw.getBodyFrame(), groundPointingLaw);
94 this.sun = sun;
95 this.phasingNormal = new PVCoordinates(Vector3D.crossProduct(Vector3D.PLUS_K, phasingAxis).normalize(),
96 Vector3D.ZERO,
97 Vector3D.ZERO);
98 }
99
100 /** {@inheritDoc} */
101 @Override
102 public Attitude getAttitude(final PVCoordinatesProvider pvProv,
103 final AbsoluteDate date, final Frame frame) {
104
105 // attitude from base attitude provider
106 final Attitude base = getBaseState(pvProv, date, frame);
107
108 // Compensation rotation definition :
109 // . Z satellite axis is unchanged
110 // . phasing axis shall be aligned to sun direction
111 final PVCoordinates sunDirection = new PVCoordinates(pvProv.getPVCoordinates(date, frame),
112 sun.getPVCoordinates(date, frame));
113 final PVCoordinates sunNormal =
114 PVCoordinates.crossProduct(PLUS_Z, base.getOrientation().applyTo(sunDirection));
115 final TimeStampedAngularCoordinates compensation =
116 new TimeStampedAngularCoordinates(date,
117 PLUS_Z, sunNormal.normalize(),
118 PLUS_Z, phasingNormal,
119 1.0e-9);
120
121 // add compensation
122 return new Attitude(frame, compensation.addOffset(base.getOrientation()));
123
124 }
125
126 /** {@inheritDoc} */
127 @Override
128 public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
129 final FieldAbsoluteDate<T> date, final Frame frame) {
130
131 final Field<T> field = date.getField();
132 final FieldVector3D<T> zero = FieldVector3D.getZero(field);
133 final FieldPVCoordinates<T> plusZ = new FieldPVCoordinates<>(FieldVector3D.getPlusK(field), zero, zero);
134
135 // attitude from base attitude provider
136 final FieldAttitude<T> base = getBaseState(pvProv, date, frame);
137
138 // Compensation rotation definition :
139 // . Z satellite axis is unchanged
140 // . phasing axis shall be aligned to sun direction
141 final FieldPVCoordinates<T> sunDirection =
142 new FieldPVCoordinates<>(pvProv.getPVCoordinates(date, frame),
143 new FieldPVCoordinates<>(field,
144 sun.getPVCoordinates(date.toAbsoluteDate(), frame)));
145 final FieldPVCoordinates<T> sunNormal =
146 plusZ.crossProduct(base.getOrientation().applyTo(sunDirection));
147 final TimeStampedFieldAngularCoordinates<T> compensation =
148 new TimeStampedFieldAngularCoordinates<>(date,
149 plusZ, sunNormal.normalize(),
150 plusZ, new FieldPVCoordinates<>(field, phasingNormal),
151 1.0e-9);
152
153 // add compensation
154 return new FieldAttitude<>(frame, compensation.addOffset(base.getOrientation()));
155
156 }
157
158 }