1   /* Copyright 2022-2025 Luc Maisonobe
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.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
22  import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
24  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Rotation;
27  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.orekit.annotation.DefaultDataContext;
30  import org.orekit.bodies.OneAxisEllipsoid;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitMessages;
33  import org.orekit.frames.Frame;
34  import org.orekit.frames.FramesFactory;
35  import org.orekit.time.AbsoluteDate;
36  import org.orekit.time.FieldAbsoluteDate;
37  import org.orekit.utils.AngularCoordinates;
38  import org.orekit.utils.ExtendedPositionProvider;
39  import org.orekit.utils.FieldAngularCoordinates;
40  import org.orekit.utils.FieldPVCoordinatesProvider;
41  import org.orekit.utils.PVCoordinatesProvider;
42  import org.orekit.utils.TimeStampedFieldPVCoordinates;
43  import org.orekit.utils.TimeStampedPVCoordinates;
44  
45  import java.util.HashMap;
46  import java.util.Map;
47  
48  /**
49   * Attitude provider with one satellite vector aligned and another one constrained to two targets.
50   * @author Luc Maisonobe
51   * @since 12.2
52   */
53  public class AlignedAndConstrained implements AttitudeProvider
54  {
55  
56      /** Satellite vector for primary target. */
57      private final FieldVector3D<UnivariateDerivative2> primarySat;
58  
59      /** Primary target. */
60      private final TargetProvider primaryTarget;
61  
62      /** Satellite vector for secondary target. */
63      private final FieldVector3D<UnivariateDerivative2> secondarySat;
64  
65      /** Secondary target. */
66      private final TargetProvider secondaryTarget;
67  
68      /** Sun model. */
69      private final ExtendedPositionProvider sun;
70  
71      /** Earth model. */
72      private final OneAxisEllipsoid earth;
73  
74      /** Reference inertial frame. */
75      private final Frame inertialFrame;
76  
77      /** Cached field-based satellite vectors. */
78      private final transient Map<Field<? extends CalculusFieldElement<?>>, Cache<? extends CalculusFieldElement<?>>>
79          cachedSatelliteVectors;
80  
81      /**
82       * Simple constructor.
83       * @param primarySat      satellite vector for primary target
84       * @param primaryTarget   primary target
85       * @param secondarySat    satellite vector for secondary target
86       * @param secondaryTarget secondary target
87       * @param inertialFrame   reference inertial frame
88       * @param sun             Sun model
89       * @param earth           Earth model
90       * @since 13.0
91       */
92      public AlignedAndConstrained(final Vector3D primarySat, final TargetProvider primaryTarget,
93                                   final Vector3D secondarySat, final TargetProvider secondaryTarget,
94                                   final Frame inertialFrame, final ExtendedPositionProvider sun,
95                                   final OneAxisEllipsoid earth) {
96          if (!inertialFrame.isPseudoInertial()) {
97              throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, inertialFrame.getName());
98          }
99          this.primarySat             = new FieldVector3D<>(UnivariateDerivative2Field.getInstance(), primarySat);
100         this.primaryTarget          = primaryTarget;
101         this.secondarySat           = new FieldVector3D<>(UnivariateDerivative2Field.getInstance(), secondarySat);
102         this.secondaryTarget        = secondaryTarget;
103         this.inertialFrame          = inertialFrame;
104         this.sun                    = sun;
105         this.earth                  = earth;
106         this.cachedSatelliteVectors = new HashMap<>();
107     }
108 
109     /**
110      * Constructor with default inertial frame.
111      * @param primarySat      satellite vector for primary target
112      * @param primaryTarget   primary target
113      * @param secondarySat    satellite vector for secondary target
114      * @param secondaryTarget secondary target
115      * @param sun             Sun model
116      * @param earth           Earth model
117      */
118     @DefaultDataContext
119     public AlignedAndConstrained(final Vector3D primarySat, final TargetProvider primaryTarget,
120                                  final Vector3D secondarySat, final TargetProvider secondaryTarget,
121                                  final ExtendedPositionProvider sun,
122                                  final OneAxisEllipsoid earth)
123     {
124         this(primarySat, primaryTarget, secondarySat, secondaryTarget, FramesFactory.getGCRF(), sun, earth);
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
130         final TimeStampedPVCoordinates satPV = pvProv.getPVCoordinates(date, inertialFrame);
131 
132         // compute targets references at the specified date
133         final Vector3D primaryDirection   = primaryTarget.getTargetDirection(sun, earth, satPV, inertialFrame);
134         final Vector3D secondaryDirection = secondaryTarget.getTargetDirection(sun, earth, satPV, inertialFrame);
135 
136         // compute transform from inertial frame to satellite frame
137         final Rotation rotation = new Rotation(primaryDirection, secondaryDirection, primarySat.toVector3D(),
138                 secondarySat.toVector3D());
139         if (inertialFrame != frame) {
140             // prepend transform from specified frame to inertial frame
141             final Rotation prepended = frame.getStaticTransformTo(inertialFrame, date).getRotation();
142             return rotation.compose(prepended, RotationConvention.VECTOR_OPERATOR);
143         }
144         return rotation;
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public Attitude getAttitude(final PVCoordinatesProvider pvProv,
150                                 final AbsoluteDate date,
151                                 final Frame frame)
152     {
153         final TimeStampedPVCoordinates satPV = pvProv.getPVCoordinates(date, inertialFrame);
154 
155         // compute targets references at the specified date
156         final FieldVector3D<UnivariateDerivative2> primaryDirection   = primaryTarget.getDerivative2TargetDirection(sun,
157                 earth, satPV, inertialFrame);
158         final FieldVector3D<UnivariateDerivative2> secondaryDirection = secondaryTarget.getDerivative2TargetDirection(sun,
159                 earth, satPV, inertialFrame);
160 
161         // compute transform from inertial frame to satellite frame
162         final FieldRotation<UnivariateDerivative2> inertToSatRotation =
163             new FieldRotation<>(primaryDirection, secondaryDirection, primarySat, secondarySat);
164 
165         // build the angular coordinates
166         final AngularCoordinates angularCoordinates = new AngularCoordinates(inertToSatRotation);
167         final Attitude attitude = new Attitude(date, inertialFrame, angularCoordinates);
168         return attitude.withReferenceFrame(frame);
169     }
170 
171     /** {@inheritDoc} */
172     @Override
173     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
174                                                                                     final FieldAbsoluteDate<T> date,
175                                                                                     final Frame frame) {
176         final TimeStampedFieldPVCoordinates<T> satPV = pvProv.getPVCoordinates(date, inertialFrame);
177 
178         // compute targets references at the specified date
179         final FieldVector3D<T> primaryDirection   = primaryTarget.getTargetDirection(sun, earth, satPV, inertialFrame);
180         final FieldVector3D<T> secondaryDirection = secondaryTarget.getTargetDirection(sun, earth, satPV, inertialFrame);
181 
182         // compute transform from inertial frame to satellite frame
183         final Field<T> field = date.getField();
184         final FieldRotation<T> rotation = new FieldRotation<>(primaryDirection, secondaryDirection,
185                 new FieldVector3D<>(field, primarySat.toVector3D()), new FieldVector3D<>(field, secondarySat.toVector3D()));
186         if (inertialFrame != frame) {
187             // prepend transform from specified frame to inertial frame
188             final FieldRotation<T> prepended = frame.getStaticTransformTo(inertialFrame, date).getRotation();
189             return rotation.compose(prepended, RotationConvention.VECTOR_OPERATOR);
190         }
191         return rotation;
192     }
193 
194     /** {@inheritDoc} */
195     @Override
196     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
197                                                                             final FieldAbsoluteDate<T> date,
198                                                                             final Frame frame)
199     {
200         // get the satellite vectors for specified field
201         @SuppressWarnings("unchecked")
202         final Cache<T> satVectors =
203             (Cache<T>) cachedSatelliteVectors.computeIfAbsent(date.getField(),
204                                                               f -> new Cache<>(date.getField(), primarySat, secondarySat));
205 
206         final TimeStampedFieldPVCoordinates<T> satPV = pvProv.getPVCoordinates(date, inertialFrame);
207 
208         // compute targets references at the specified date
209         final FieldVector3D<FieldUnivariateDerivative2<T>> primaryDirection   = primaryTarget.getDerivative2TargetDirection(sun,
210                 earth, satPV, inertialFrame);
211         final FieldVector3D<FieldUnivariateDerivative2<T>> secondaryDirection = secondaryTarget.getDerivative2TargetDirection(sun,
212                 earth, satPV, inertialFrame);
213 
214         // compute transform from inertial frame to satellite frame
215         final FieldRotation<FieldUnivariateDerivative2<T>> inertToSatRotation =
216             new FieldRotation<>(primaryDirection, secondaryDirection, satVectors.primarySat, satVectors.secondarySat);
217 
218         // build the attitude
219         final FieldAngularCoordinates<T> angularCoordinates = new FieldAngularCoordinates<>(inertToSatRotation);
220         final FieldAttitude<T> attitude = new FieldAttitude<>(date, inertialFrame, angularCoordinates);
221         return attitude.withReferenceFrame(frame);
222     }
223 
224     /** Container for cached satellite vectors. */
225     private static class Cache<T extends CalculusFieldElement<T>> {
226 
227         /** Satellite vector for primary target. */
228         private final FieldVector3D<FieldUnivariateDerivative2<T>> primarySat;
229 
230         /** Satellite vector for primary target. */
231         private final FieldVector3D<FieldUnivariateDerivative2<T>> secondarySat;
232 
233         /** Simple constructor.
234          * @param field field to which the elements belong
235          * @param primarySat satellite vector for primary target
236          * @param secondarySat satellite vector for primary target
237          */
238         Cache(final Field<T> field,
239               final FieldVector3D<UnivariateDerivative2> primarySat,
240               final FieldVector3D<UnivariateDerivative2> secondarySat) {
241             final FieldUnivariateDerivative2<T> zero =
242                 new FieldUnivariateDerivative2<>(field.getZero(), field.getZero(), field.getZero());
243             this.primarySat   = new FieldVector3D<>(zero.newInstance(primarySat.getX().getValue()),
244                                                     zero.newInstance(primarySat.getY().getValue()),
245                                                     zero.newInstance(primarySat.getZ().getValue()));
246             this.secondarySat = new FieldVector3D<>(zero.newInstance(secondarySat.getX().getValue()),
247                                                     zero.newInstance(secondarySat.getY().getValue()),
248                                                     zero.newInstance(secondarySat.getZ().getValue()));
249         }
250 
251     }
252 
253 }