GroundPointing.java

  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. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.geometry.euclidean.threed.Rotation;
  23. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  24. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  25. import org.hipparchus.util.FastMath;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.FieldAbsoluteDate;
  31. import org.orekit.utils.AngularCoordinates;
  32. import org.orekit.utils.FieldAngularCoordinates;
  33. import org.orekit.utils.FieldPVCoordinates;
  34. import org.orekit.utils.FieldPVCoordinatesProvider;
  35. import org.orekit.utils.PVCoordinates;
  36. import org.orekit.utils.PVCoordinatesProvider;
  37. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  38. import org.orekit.utils.TimeStampedPVCoordinates;


  39. /**
  40.  * Base class for ground pointing attitude providers.
  41.  *
  42.  * <p>This class is a basic model for different kind of ground pointing
  43.  * attitude providers, such as : body center pointing, nadir pointing,
  44.  * target pointing, etc...
  45.  * </p>
  46.  * <p>
  47.  * The object <code>GroundPointing</code> is guaranteed to be immutable.
  48.  * </p>
  49.  * @see     AttitudeProvider
  50.  * @author V&eacute;ronique Pommier-Maurussane
  51.  */
  52. public abstract class GroundPointing implements AttitudeProvider {

  53.     /** J axis. */
  54.     private static final PVCoordinates PLUS_J =
  55.             new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);

  56.     /** K axis. */
  57.     private static final PVCoordinates PLUS_K =
  58.             new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);

  59.     /** Inertial frame. */
  60.     private final Frame inertialFrame;

  61.     /** Body frame. */
  62.     private final Frame bodyFrame;

  63.     /** Default constructor.
  64.      * Build a new instance with arbitrary default elements.
  65.      * @param inertialFrame frame in which orbital velocities are computed
  66.      * @param bodyFrame the frame that rotates with the body
  67.      * @since 7.1
  68.      */
  69.     protected GroundPointing(final Frame inertialFrame, final Frame bodyFrame) {
  70.         if (!inertialFrame.isPseudoInertial()) {
  71.             throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
  72.                                       inertialFrame.getName());
  73.         }
  74.         this.inertialFrame = inertialFrame;
  75.         this.bodyFrame     = bodyFrame;
  76.     }

  77.     /** Get the body frame.
  78.      * @return body frame
  79.      */
  80.     public Frame getBodyFrame() {
  81.         return bodyFrame;
  82.     }

  83.     /** Compute the target point position/velocity in specified frame.
  84.      * @param pvProv provider for PV coordinates
  85.      * @param date date at which target point is requested
  86.      * @param frame frame in which observed ground point should be provided
  87.      * @return observed ground point position (element 0) and velocity (at index 1)
  88.      * in specified frame
  89.      */
  90.     protected abstract TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame);

  91.     /** Compute the target point position/velocity in specified frame.
  92.      * @param pvProv provider for PV coordinates
  93.      * @param date date at which target point is requested
  94.      * @param frame frame in which observed ground point should be provided
  95.      * @param <T> type of the field elements
  96.      * @return observed ground point position (element 0) and velocity (at index 1)
  97.      * in specified frame
  98.      * @since 9.0
  99.      */
  100.     protected abstract <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(FieldPVCoordinatesProvider<T> pvProv,
  101.                                                                                                         FieldAbsoluteDate<T> date,
  102.                                                                                                         Frame frame);

  103.     /** Compute the target point position in specified frame.
  104.      * @param pvProv provider for PV coordinates
  105.      * @param date date at which target point is requested
  106.      * @param frame frame in which observed ground point should be provided
  107.      * @return observed ground point position in specified frame
  108.      * @since 12.0
  109.      */
  110.     protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
  111.         return getTargetPV(pvProv, date, frame).getPosition();
  112.     }

  113.     /** Compute the target point position in specified frame.
  114.      * @param pvProv provider for PV coordinates
  115.      * @param date date at which target point is requested
  116.      * @param frame frame in which observed ground point should be provided
  117.      * @param <T> type of the field elements
  118.      * @return observed ground point position in specified frame
  119.      * @since 12.0
  120.      */
  121.     protected <T extends CalculusFieldElement<T>> FieldVector3D<T> getTargetPosition(final FieldPVCoordinatesProvider<T> pvProv,
  122.                                                                                      final FieldAbsoluteDate<T> date,
  123.                                                                                      final Frame frame) {
  124.         return getTargetPV(pvProv, date, frame).getPosition();
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date,
  129.                                 final Frame frame) {

  130.         // satellite-target relative vector
  131.         final PVCoordinates pva  = pvProv.getPVCoordinates(date, inertialFrame);
  132.         final TimeStampedPVCoordinates delta =
  133.                 new TimeStampedPVCoordinates(date, pva, getTargetPV(pvProv, date, inertialFrame));

  134.         // spacecraft and target should be away from each other to define a pointing direction
  135.         if (delta.getPosition().getNorm() == 0.0) {
  136.             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
  137.         }

  138.         // attitude definition:
  139.         // line of sight    -> +z satellite axis,
  140.         // orbital velocity -> (z, +x) half plane
  141.         final Vector3D p  = pva.getPosition();
  142.         final Vector3D v  = pva.getVelocity();
  143.         final Vector3D a  = pva.getAcceleration();
  144.         final double   r2 = p.getNormSq();
  145.         final double   r  = FastMath.sqrt(r2);
  146.         final Vector3D keplerianJerk = new Vector3D(-3 * Vector3D.dotProduct(p, v) / r2, a, -a.getNorm() / r, v);
  147.         final PVCoordinates velocity = new PVCoordinates(v, a, keplerianJerk);

  148.         final PVCoordinates los    = delta.normalize();
  149.         final PVCoordinates normal = PVCoordinates.crossProduct(delta, velocity).normalize();

  150.         final AngularCoordinates ac = new AngularCoordinates(los, normal, PLUS_K, PLUS_J, 1.0e-9);
  151.         final Attitude attitude = new Attitude(date, inertialFrame, ac);
  152.         return attitude.withReferenceFrame(frame);
  153.     }

  154.     /** {@inheritDoc} */
  155.     @Override
  156.     public <T extends CalculusFieldElement<T>>FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
  157.                                                                            final FieldAbsoluteDate<T> date,
  158.                                                                            final Frame frame) {

  159.         // satellite-target relative vector
  160.         final FieldPVCoordinates<T> pva  = pvProv.getPVCoordinates(date, inertialFrame);
  161.         final TimeStampedFieldPVCoordinates<T> delta =
  162.                 new TimeStampedFieldPVCoordinates<>(date, pva, getTargetPV(pvProv, date, inertialFrame));

  163.         // spacecraft and target should be away from each other to define a pointing direction
  164.         if (delta.getPosition().getNorm().getReal() == 0.0) {
  165.             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
  166.         }

  167.         // attitude definition:
  168.         // line of sight    -> +z satellite axis,
  169.         // orbital velocity -> (z, +x) half plane
  170.         final FieldVector3D<T> p  = pva.getPosition();
  171.         final FieldVector3D<T> v  = pva.getVelocity();
  172.         final FieldVector3D<T> a  = pva.getAcceleration();
  173.         final T   r2 = p.getNormSq();
  174.         final T   r  = r2.sqrt();
  175.         final FieldVector3D<T> keplerianJerk = new FieldVector3D<>(FieldVector3D.dotProduct(p, v).multiply(-3).divide(r2), a, a.getNorm().divide(r).multiply(-1), v);
  176.         final FieldPVCoordinates<T> velocity = new FieldPVCoordinates<>(v, a, keplerianJerk);


  177.         final FieldPVCoordinates<T> los    = delta.normalize();

  178.         final FieldPVCoordinates<T> normal = (delta.crossProduct(velocity)).normalize();

  179.         final FieldVector3D<T> zero  = FieldVector3D.getZero(r.getField());
  180.         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(r.getField());
  181.         final FieldVector3D<T> plusJ = FieldVector3D.getPlusJ(r.getField());
  182.         final FieldAngularCoordinates<T> ac =
  183.                         new FieldAngularCoordinates<>(los, normal,
  184.                                                       new FieldPVCoordinates<>(plusK, zero, zero),
  185.                                                       new FieldPVCoordinates<>(plusJ, zero, zero),
  186.                                                       1.0e-6);
  187.         final FieldAttitude<T> attitude = new FieldAttitude<>(date, inertialFrame, ac);
  188.         return attitude.withReferenceFrame(frame);
  189.     }

  190.     /** {@inheritDoc} */
  191.     @Override
  192.     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date,
  193.                                         final Frame frame) {

  194.         // satellite-target relative vector
  195.         final PVCoordinates pva  = pvProv.getPVCoordinates(date, inertialFrame);
  196.         final Vector3D targetPosition = getTargetPosition(pvProv, date, inertialFrame);
  197.         final Vector3D deltaPosition = targetPosition.subtract(pva.getPosition());

  198.         // spacecraft and target should be away from each other to define a pointing direction
  199.         if (deltaPosition.getNorm() == 0.0) {
  200.             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
  201.         }

  202.         final Vector3D los    = deltaPosition.normalize();
  203.         final Vector3D normal = Vector3D.crossProduct(los, pva.getVelocity()).normalize();

  204.         final Rotation rotation = new Rotation(los, normal, PLUS_K.getPosition(), PLUS_J.getPosition());

  205.         if (frame != inertialFrame) {
  206.             // prepend transform from specified frame to inertial frame
  207.             return rotation.compose(frame.getStaticTransformTo(inertialFrame, date).getRotation(),
  208.                     RotationConvention.VECTOR_OPERATOR);
  209.         }

  210.         return rotation;

  211.     }


  212.     /** {@inheritDoc} */
  213.     @Override
  214.     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
  215.                                                                                     final FieldAbsoluteDate<T> date,
  216.                                                                                     final Frame frame) {
  217.         // satellite-target relative vector
  218.         final FieldPVCoordinates<T> pva  = pvProv.getPVCoordinates(date, inertialFrame);
  219.         final FieldVector3D<T> targetPosition = getTargetPosition(pvProv, date, inertialFrame);
  220.         final FieldVector3D<T> deltaPosition = targetPosition.subtract(pva.getPosition());

  221.         // spacecraft and target should be away from each other to define a pointing direction
  222.         if (deltaPosition.getNorm().getReal() == 0.0) {
  223.             throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET);
  224.         }

  225.         final FieldVector3D<T> los    = deltaPosition.normalize();
  226.         final FieldVector3D<T> normal = FieldVector3D.crossProduct(los, pva.getVelocity()).normalize();

  227.         final Field<T> field = date.getField();
  228.         final FieldRotation<T> rotation = new FieldRotation<>(los, normal,
  229.                 new FieldVector3D<>(field, PLUS_K.getPosition()), new FieldVector3D<>(field, PLUS_J.getPosition()));

  230.         if (frame != inertialFrame) {
  231.             // prepend transform from specified frame to inertial frame
  232.             return rotation.compose(frame.getStaticTransformTo(inertialFrame, date).getRotation(),
  233.                     RotationConvention.VECTOR_OPERATOR);
  234.         }

  235.         return rotation;

  236.     }

  237. }