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 java.util.ArrayList;
20  import java.util.List;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.geometry.euclidean.threed.FieldLine;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Line;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.orekit.bodies.BodyShape;
28  import org.orekit.bodies.FieldGeodeticPoint;
29  import org.orekit.bodies.GeodeticPoint;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.frames.FieldTransform;
33  import org.orekit.frames.Frame;
34  import org.orekit.frames.Transform;
35  import org.orekit.time.AbsoluteDate;
36  import org.orekit.time.FieldAbsoluteDate;
37  import org.orekit.utils.CartesianDerivativesFilter;
38  import org.orekit.utils.Constants;
39  import org.orekit.utils.FieldPVCoordinatesProvider;
40  import org.orekit.utils.PVCoordinatesProvider;
41  import org.orekit.utils.TimeStampedFieldPVCoordinates;
42  import org.orekit.utils.TimeStampedPVCoordinates;
43  
44  
45  /**
46   * This class provides a default attitude provider.
47  
48   * <p>
49   * The attitude pointing law is defined by an attitude provider and
50   * the satellite axis vector chosen for pointing.
51   * <p>
52   * @author V&eacute;ronique Pommier-Maurussane
53   */
54  public class LofOffsetPointing extends GroundPointing {
55  
56      /** Rotation from local orbital frame. */
57      private final AttitudeProvider attitudeLaw;
58  
59      /** Body shape. */
60      private final BodyShape shape;
61  
62      /** Chosen satellite axis for pointing, given in satellite frame. */
63      private final Vector3D satPointingVector;
64  
65      /** Creates new instance.
66       * @param inertialFrame frame in which orbital velocities are computed
67       * @param shape Body shape
68       * @param attLaw Attitude law
69       * @param satPointingVector satellite vector defining the pointing direction
70       * @since 7.1
71       */
72      public LofOffsetPointing(final Frame inertialFrame, final BodyShape shape,
73                               final AttitudeProvider attLaw, final Vector3D satPointingVector) {
74          super(inertialFrame, shape.getBodyFrame());
75          this.shape = shape;
76          this.attitudeLaw = attLaw;
77          this.satPointingVector = satPointingVector;
78      }
79  
80      /** {@inheritDoc} */
81      @Override
82      public Attitude getAttitude(final PVCoordinatesProvider pvProv,
83                                  final AbsoluteDate date, final Frame frame) {
84          return attitudeLaw.getAttitude(pvProv, date, frame);
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
90                                                                          final FieldAbsoluteDate<T> date, final Frame frame) {
91          return attitudeLaw.getAttitude(pvProv, date, frame);
92      }
93  
94      /** {@inheritDoc} */
95      public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
96                                                  final AbsoluteDate date, final Frame frame) {
97  
98          // sample intersection points in current date neighborhood
99          final double h  = 0.1;
100         final List<TimeStampedPVCoordinates> sample = new ArrayList<>();
101         Transform centralRefToBody = null;
102         for (int i = -1; i < 2; ++i) {
103 
104             final AbsoluteDate shifted = date.shiftedBy(i * h);
105 
106             // transform from specified reference frame to spacecraft frame
107             final Transform refToSc =
108                             new Transform(shifted,
109                                           new Transform(shifted, pvProv.getPVCoordinates(shifted, frame).negate()),
110                                           new Transform(shifted, attitudeLaw.getAttitude(pvProv, shifted, frame).getOrientation()));
111 
112             // transform from specified reference frame to body frame
113             final Transform refToBody = frame.getTransformTo(shape.getBodyFrame(), shifted);
114             if (i == 0) {
115                 centralRefToBody = refToBody;
116             }
117 
118             sample.add(losIntersectionWithBody(new Transform(shifted, refToSc.getInverse(), refToBody)));
119 
120         }
121 
122         // use interpolation to compute properly the time-derivatives
123         final TimeStampedPVCoordinates targetBody =
124                 TimeStampedPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample);
125 
126         // convert back to caller specified frame
127         return centralRefToBody.getInverse().transformPVCoordinates(targetBody);
128 
129     }
130 
131     /** {@inheritDoc} */
132     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
133                                                                                         final FieldAbsoluteDate<T> date,
134                                                                                         final Frame frame) {
135 
136         // sample intersection points in current date neighborhood
137         final double h  = 0.1;
138         final List<TimeStampedFieldPVCoordinates<T>> sample = new ArrayList<>();
139         FieldTransform<T> centralRefToBody = null;
140         for (int i = -1; i < 2; ++i) {
141 
142             final FieldAbsoluteDate<T> shifted = date.shiftedBy(i * h);
143 
144             // transform from specified reference frame to spacecraft frame
145             final FieldTransform<T> refToSc =
146                             new FieldTransform<>(shifted,
147                                                  new FieldTransform<>(shifted, pvProv.getPVCoordinates(shifted, frame).negate()),
148                                                  new FieldTransform<>(shifted, attitudeLaw.getAttitude(pvProv, shifted, frame).getOrientation()));
149 
150             // transform from specified reference frame to body frame
151             final FieldTransform<T> refToBody = frame.getTransformTo(shape.getBodyFrame(), shifted);
152             if (i == 0) {
153                 centralRefToBody = refToBody;
154             }
155 
156             sample.add(losIntersectionWithBody(new FieldTransform<>(shifted, refToSc.getInverse(), refToBody)));
157 
158         }
159 
160         // use interpolation to compute properly the time-derivatives
161         final TimeStampedFieldPVCoordinates<T> targetBody =
162                         TimeStampedFieldPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample);
163 
164         // convert back to caller specified frame
165         return centralRefToBody.getInverse().transformPVCoordinates(targetBody);
166 
167     }
168 
169     /** Compute line of sight intersection with body.
170      * @param scToBody transform from spacecraft frame to body frame
171      * @return intersection point in body frame (only the position is set!)
172      */
173     private TimeStampedPVCoordinates losIntersectionWithBody(final Transform scToBody) {
174 
175         // compute satellite pointing axis and position/velocity in body frame
176         final Vector3D pointingBodyFrame = scToBody.transformVector(satPointingVector);
177         final Vector3D pBodyFrame        = scToBody.transformPosition(Vector3D.ZERO);
178 
179         // Line from satellite following pointing direction
180         // we use arbitrarily the Earth radius as a scaling factor, it could be anything else
181         final Line pointingLine = new Line(pBodyFrame,
182                                            pBodyFrame.add(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
183                                                           pointingBodyFrame),
184                                            1.0e-10);
185 
186         // Intersection with body shape
187         final GeodeticPoint gpIntersection =
188             shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), scToBody.getDate());
189         final Vector3D pIntersection =
190             (gpIntersection == null) ? null : shape.transform(gpIntersection);
191 
192         // Check there is an intersection and it is not in the reverse pointing direction
193         if (pIntersection == null ||
194             Vector3D.dotProduct(pIntersection.subtract(pBodyFrame), pointingBodyFrame) < 0) {
195             throw new OrekitException(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND);
196         }
197 
198         return new TimeStampedPVCoordinates(scToBody.getDate(),
199                                             pIntersection, Vector3D.ZERO, Vector3D.ZERO);
200 
201     }
202 
203     /** Compute line of sight intersection with body.
204      * @param scToBody transform from spacecraft frame to body frame
205      * @param <T> type of the field elements
206      * @return intersection point in body frame (only the position is set!)
207      */
208     private <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> losIntersectionWithBody(final FieldTransform<T> scToBody) {
209 
210         // compute satellite pointing axis and position/velocity in body frame
211         final FieldVector3D<T> pointingBodyFrame = scToBody.transformVector(satPointingVector);
212         final FieldVector3D<T> pBodyFrame        = scToBody.transformPosition(Vector3D.ZERO);
213 
214         // Line from satellite following pointing direction
215         // we use arbitrarily the Earth radius as a scaling factor, it could be anything else
216         final FieldLine<T> pointingLine = new FieldLine<>(pBodyFrame,
217                                                           pBodyFrame.add(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
218                                                                          pointingBodyFrame),
219                                                           1.0e-10);
220 
221         // Intersection with body shape
222         final FieldGeodeticPoint<T> gpIntersection =
223             shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), scToBody.getFieldDate());
224         final FieldVector3D<T> pIntersection =
225             (gpIntersection == null) ? null : shape.transform(gpIntersection);
226 
227         // Check there is an intersection and it is not in the reverse pointing direction
228         if (pIntersection == null ||
229             FieldVector3D.dotProduct(pIntersection.subtract(pBodyFrame), pointingBodyFrame).getReal() < 0) {
230             throw new OrekitException(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND);
231         }
232 
233         final FieldVector3D<T> zero = FieldVector3D.getZero(scToBody.getFieldDate().getField());
234         return new TimeStampedFieldPVCoordinates<>(scToBody.getDate(),
235                                                    pIntersection, zero, zero);
236 
237     }
238 
239 }