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 }