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.CalculusFieldElement;
20  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.FastMath;
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.frames.Frame;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.FieldAbsoluteDate;
28  import org.orekit.utils.AngularCoordinates;
29  import org.orekit.utils.FieldAngularCoordinates;
30  import org.orekit.utils.FieldPVCoordinates;
31  import org.orekit.utils.FieldPVCoordinatesProvider;
32  import org.orekit.utils.PVCoordinates;
33  import org.orekit.utils.PVCoordinatesProvider;
34  import org.orekit.utils.TimeStampedFieldPVCoordinates;
35  import org.orekit.utils.TimeStampedPVCoordinates;
36  
37  
38  /**
39   * Base class for ground pointing attitude providers.
40   *
41   * <p>This class is a basic model for different kind of ground pointing
42   * attitude providers, such as : body center pointing, nadir pointing,
43   * target pointing, etc...
44   * </p>
45   * <p>
46   * The object <code>GroundPointing</code> is guaranteed to be immutable.
47   * </p>
48   * @see     AttitudeProvider
49   * @author V&eacute;ronique Pommier-Maurussane
50   */
51  public abstract class GroundPointing implements AttitudeProvider {
52  
53      /** J axis. */
54      private static final PVCoordinates PLUS_J =
55              new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
56  
57      /** K axis. */
58      private static final PVCoordinates PLUS_K =
59              new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);
60  
61      /** Inertial frame. */
62      private final Frame inertialFrame;
63  
64      /** Body frame. */
65      private final Frame bodyFrame;
66  
67      /** Default constructor.
68       * Build a new instance with arbitrary default elements.
69       * @param inertialFrame frame in which orbital velocities are computed
70       * @param bodyFrame the frame that rotates with the body
71       * @since 7.1
72       */
73      protected GroundPointing(final Frame inertialFrame, final Frame bodyFrame) {
74          if (!inertialFrame.isPseudoInertial()) {
75              throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
76                                        inertialFrame.getName());
77          }
78          this.inertialFrame = inertialFrame;
79          this.bodyFrame     = bodyFrame;
80      }
81  
82      /** Get the body frame.
83       * @return body frame
84       */
85      public Frame getBodyFrame() {
86          return bodyFrame;
87      }
88  
89      /** Compute the target point position/velocity in specified frame.
90       * <p>
91       * This method is {@code public} only to allow users to subclass this
92       * abstract class from other packages. It is <em>not</em> intended to
93       * be used directly.
94       * </p>
95       * @param pvProv provider for PV coordinates
96       * @param date date at which target point is requested
97       * @param frame frame in which observed ground point should be provided
98       * @return observed ground point position (element 0) and velocity (at index 1)
99       * in specified frame
100      */
101     public abstract TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv,
102                                                          AbsoluteDate date, Frame frame);
103 
104     /** Compute the target point position/velocity in specified frame.
105      * @param pvProv provider for PV coordinates
106      * @param date date at which target point is requested
107      * @param frame frame in which observed ground point should be provided
108      * @param <T> type of the fiels elements
109      * @return observed ground point position (element 0) and velocity (at index 1)
110      * in specified frame
111      * @since 9.0
112      */
113     public abstract <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(FieldPVCoordinatesProvider<T> pvProv,
114                                                                                                  FieldAbsoluteDate<T> date,
115                                                                                                  Frame frame);
116 
117     /** {@inheritDoc} */
118     public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date,
119                                 final Frame frame) {
120 
121         // satellite-target relative vector
122         final PVCoordinates pva  = pvProv.getPVCoordinates(date, inertialFrame);
123         final TimeStampedPVCoordinates delta =
124                 new TimeStampedPVCoordinates(date, pva, getTargetPV(pvProv, date, inertialFrame));
125 
126         // spacecraft and target should be away from each other to define a pointing direction
127         if (delta.getPosition().getNorm() == 0.0) {
128             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
129         }
130 
131         // attitude definition:
132         // line of sight    -> +z satellite axis,
133         // orbital velocity -> (z, +x) half plane
134         final Vector3D p  = pva.getPosition();
135         final Vector3D v  = pva.getVelocity();
136         final Vector3D a  = pva.getAcceleration();
137         final double   r2 = p.getNormSq();
138         final double   r  = FastMath.sqrt(r2);
139         final Vector3D keplerianJerk = new Vector3D(-3 * Vector3D.dotProduct(p, v) / r2, a, -a.getNorm() / r, v);
140         final PVCoordinates velocity = new PVCoordinates(v, a, keplerianJerk);
141 
142         final PVCoordinates los    = delta.normalize();
143         final PVCoordinates normal = PVCoordinates.crossProduct(delta, velocity).normalize();
144 
145         AngularCoordinates ac = new AngularCoordinates(los, normal, PLUS_K, PLUS_J, 1.0e-9);
146 
147         if (frame != inertialFrame) {
148             // prepend transform from specified frame to inertial frame
149             ac = ac.addOffset(frame.getTransformTo(inertialFrame, date).getAngular());
150         }
151 
152         // build the attitude
153         return new Attitude(date, frame, ac);
154 
155     }
156 
157     /** {@inheritDoc} */
158     public <T extends CalculusFieldElement<T>>FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
159                                                                        final FieldAbsoluteDate<T> date,
160                                                                        final Frame frame) {
161 
162         // satellite-target relative vector
163         final FieldPVCoordinates<T> pva  = pvProv.getPVCoordinates(date, inertialFrame);
164         final TimeStampedFieldPVCoordinates<T> delta =
165                 new TimeStampedFieldPVCoordinates<>(date, pva, getTargetPV(pvProv, date, inertialFrame));
166 
167         // spacecraft and target should be away from each other to define a pointing direction
168         if (delta.getPosition().getNorm().getReal() == 0.0) {
169             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
170         }
171 
172         // attitude definition:
173         // line of sight    -> +z satellite axis,
174         // orbital velocity -> (z, +x) half plane
175         final FieldVector3D<T> p  = pva.getPosition();
176         final FieldVector3D<T> v  = pva.getVelocity();
177         final FieldVector3D<T> a  = pva.getAcceleration();
178         final T   r2 = p.getNormSq();
179         final T   r  = r2.sqrt();
180         final FieldVector3D<T> keplerianJerk = new FieldVector3D<>(FieldVector3D.dotProduct(p, v).multiply(-3).divide(r2), a, a.getNorm().divide(r).multiply(-1), v);
181         final FieldPVCoordinates<T> velocity = new FieldPVCoordinates<>(v, a, keplerianJerk);
182 
183 
184         final FieldPVCoordinates<T> los    = delta.normalize();
185 
186         final FieldPVCoordinates<T> normal = (delta.crossProduct(velocity)).normalize();
187 
188         final FieldVector3D<T> zero  = FieldVector3D.getZero(r.getField());
189         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(r.getField());
190         final FieldVector3D<T> plusJ = FieldVector3D.getPlusJ(r.getField());
191         FieldAngularCoordinates<T> ac =
192                         new FieldAngularCoordinates<>(los, normal,
193                                                       new FieldPVCoordinates<>(plusK, zero, zero),
194                                                       new FieldPVCoordinates<>(plusJ, zero, zero),
195                                                       1.0e-6);
196 
197         if (frame != inertialFrame) {
198             // prepend transform from specified frame to inertial frame
199             ac = ac.addOffset(new FieldAngularCoordinates<>(r.getField(),
200                                                             frame.getTransformTo(inertialFrame, date.toAbsoluteDate()).getAngular()));
201         }
202 
203         // build the attitude
204         return new FieldAttitude<>(date, frame, ac);
205 
206     }
207 
208 }