1   /* Copyright 2002-2025 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.hipparchus.util.MathArrays;
24  import org.orekit.bodies.Ellipsoid;
25  import org.orekit.frames.Frame;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.FieldAbsoluteDate;
28  import org.orekit.utils.FieldPVCoordinatesProvider;
29  import org.orekit.utils.PVCoordinatesProvider;
30  import org.orekit.utils.TimeStampedFieldPVCoordinates;
31  import org.orekit.utils.TimeStampedPVCoordinates;
32  
33  /**
34   * This class handles body center pointing attitude provider.
35  
36   * <p>
37   * This class represents the attitude provider where the satellite z axis is
38   * pointing to the body frame center.</p>
39   * <p>
40   * The object <code>BodyCenterPointing</code> is guaranteed to be immutable.
41   * </p>
42   * @see     GroundPointing
43   * @author V&eacute;ronique Pommier-Maurussane
44   */
45  public class BodyCenterPointing extends GroundPointing {
46  
47      /** Body ellipsoid.  */
48      private final Ellipsoid ellipsoid;
49  
50      /** Creates new instance.
51       * @param inertialFrame frame in which orbital velocities are computed
52       * @param shape Body shape
53       * @since 7.1
54       */
55      public BodyCenterPointing(final Frame inertialFrame, final Ellipsoid shape) {
56          super(inertialFrame, shape.getFrame());
57          this.ellipsoid = shape;
58      }
59  
60      /** {@inheritDoc} */
61      @Override
62      public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
63                                                  final AbsoluteDate date, final Frame frame) {
64  
65          // spacecraft coordinates in body frame
66          final TimeStampedPVCoordinates scInBodyFrame = pvProv.getPVCoordinates(date, getBodyFrame());
67  
68          // central projection to ground (NOT the classical nadir point)
69          final double u     = scInBodyFrame.getPosition().getX() / ellipsoid.getA();
70          final double v     = scInBodyFrame.getPosition().getY() / ellipsoid.getB();
71          final double w     = scInBodyFrame.getPosition().getZ() / ellipsoid.getC();
72          final double d2    = u * u + v * v + w * w;
73          final double d     = FastMath.sqrt(d2);
74          final double ratio = 1.0 / d;
75          final Vector3D projectedP = new Vector3D(ratio, scInBodyFrame.getPosition());
76  
77          // velocity
78          final double uDot     = scInBodyFrame.getVelocity().getX() / ellipsoid.getA();
79          final double vDot     = scInBodyFrame.getVelocity().getY() / ellipsoid.getB();
80          final double wDot     = scInBodyFrame.getVelocity().getZ() / ellipsoid.getC();
81          final double dDot     = MathArrays.linearCombination(u, uDot, v, vDot, w, wDot) / d;
82          final double ratioDot = -dDot / d2;
83          final Vector3D projectedV = new Vector3D(ratio,    scInBodyFrame.getVelocity(),
84                                                   ratioDot, scInBodyFrame.getPosition());
85  
86          // acceleration
87          final double uDotDot      = scInBodyFrame.getAcceleration().getX() / ellipsoid.getA();
88          final double vDotDot      = scInBodyFrame.getAcceleration().getY() / ellipsoid.getB();
89          final double wDotDot      = scInBodyFrame.getAcceleration().getZ() / ellipsoid.getC();
90          final double dDotDot      = (MathArrays.linearCombination(u, uDotDot, v, vDotDot, w, wDotDot) +
91                                       uDot * uDot + vDot * vDot + wDot * wDot - dDot * dDot) / d;
92          final double ratioDotDot  = (2 * dDot * dDot - d * dDotDot) / (d * d2);
93          final Vector3D projectedA = new Vector3D(ratio,        scInBodyFrame.getAcceleration(),
94                                                   2 * ratioDot, scInBodyFrame.getVelocity(),
95                                                   ratioDotDot,  scInBodyFrame.getPosition());
96  
97          final TimeStampedPVCoordinates projected =
98                  new TimeStampedPVCoordinates(date, projectedP, projectedV, projectedA);
99          return getBodyFrame().getTransformTo(frame, date).transformPVCoordinates(projected);
100 
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
106         // spacecraft coordinates in body frame
107         final Vector3D scPositionInBodyFrame = pvProv.getPosition(date, getBodyFrame());
108 
109         // central projection to ground (NOT the classical nadir point)
110         final double u     = scPositionInBodyFrame.getX() / ellipsoid.getA();
111         final double v     = scPositionInBodyFrame.getY() / ellipsoid.getB();
112         final double w     = scPositionInBodyFrame.getZ() / ellipsoid.getC();
113         final double d2    = u * u + v * v + w * w;
114         final double d     = FastMath.sqrt(d2);
115         final double ratio = 1.0 / d;
116         final Vector3D projectedP = new Vector3D(ratio, scPositionInBodyFrame);
117 
118         return getBodyFrame().getStaticTransformTo(frame, date).transformPosition(projectedP);
119     }
120 
121     /** {@inheritDoc} */
122     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
123                                                                                             final FieldAbsoluteDate<T> date,
124                                                                                             final Frame frame) {
125 
126         // spacecraft coordinates in body frame
127         final TimeStampedFieldPVCoordinates<T> scInBodyFrame = pvProv.getPVCoordinates(date, getBodyFrame());
128 
129         // central projection to ground (NOT the classical nadir point)
130         final T u     = scInBodyFrame.getPosition().getX().divide(ellipsoid.getA());
131         final T v     = scInBodyFrame.getPosition().getY().divide(ellipsoid.getB());
132         final T w     = scInBodyFrame.getPosition().getZ().divide(ellipsoid.getC());
133         final T d2    = u.pow(2).add(v.pow(2)).add(w.pow(2));
134         final T d     = d2.sqrt();
135         final T ratio = d.reciprocal();
136         final FieldVector3D<T> projectedP = new FieldVector3D<>(ratio, scInBodyFrame.getPosition());
137 
138         // velocity
139         final T uDot     = scInBodyFrame.getVelocity().getX().divide(ellipsoid.getA());
140         final T vDot     = scInBodyFrame.getVelocity().getY().divide(ellipsoid.getB());
141         final T wDot     = scInBodyFrame.getVelocity().getZ().divide(ellipsoid.getC());
142         //we aren't using the linearCombination in the library
143         final T dDot     = (u.multiply(uDot).add(v.multiply(vDot)).add(w.multiply(wDot))).divide(d);
144         final T ratioDot = dDot.multiply(-1).divide(d2);
145         final FieldVector3D<T> projectedV = new FieldVector3D<>(ratio,    scInBodyFrame.getVelocity(),
146                                                                 ratioDot, scInBodyFrame.getPosition());
147 
148         // acceleration
149         final T uDotDot      = scInBodyFrame.getAcceleration().getX().divide(ellipsoid.getA());
150         final T vDotDot      = scInBodyFrame.getAcceleration().getY().divide(ellipsoid.getB());
151         final T wDotDot      = scInBodyFrame.getAcceleration().getZ().divide(ellipsoid.getC());
152         final T dDotDot      = u.multiply(uDotDot).add(v.multiply(vDotDot)).add(w.multiply( wDotDot)
153                                          .add(uDot.pow(2).add(vDot.pow(2)).add(wDot.pow(2)).subtract(dDot.pow(2))))
154                                          .divide(d);
155         final T ratioDotDot  = (dDot.pow(2).multiply(2).subtract(d.multiply(dDotDot))).divide(d.multiply(d2));
156         final FieldVector3D<T> projectedA = new FieldVector3D<>(ratio,                scInBodyFrame.getAcceleration(),
157                                                                 ratioDot.multiply(2), scInBodyFrame.getVelocity(),
158                                                                 ratioDotDot,          scInBodyFrame.getPosition());
159         final TimeStampedFieldPVCoordinates<T> projected =
160                 new TimeStampedFieldPVCoordinates<>(date, projectedP, projectedV, projectedA);
161         return getBodyFrame().getTransformTo(frame, date).transformPVCoordinates(projected);
162 
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     protected <T extends CalculusFieldElement<T>> FieldVector3D<T> getTargetPosition(final FieldPVCoordinatesProvider<T> pvProv,
168                                                                                      final FieldAbsoluteDate<T> date,
169                                                                                      final Frame frame) {
170         // spacecraft coordinates in body frame
171         final FieldVector3D<T> scPositionInBodyFrame = pvProv.getPosition(date, getBodyFrame());
172 
173         // central projection to ground (NOT the classical nadir point)
174         final T u     = scPositionInBodyFrame.getX().divide(ellipsoid.getA());
175         final T v     = scPositionInBodyFrame.getY().divide(ellipsoid.getB());
176         final T w     = scPositionInBodyFrame.getZ().divide(ellipsoid.getC());
177         final T d     = new FieldVector3D<>(u, v, w).getNorm();
178         final FieldVector3D<T> projectedP = new FieldVector3D<>(d.reciprocal(), scPositionInBodyFrame);
179 
180         return getBodyFrame().getStaticTransformTo(frame, date).transformPosition(projectedP);
181     }
182 }