LofOffsetPointing.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 java.util.ArrayList;
  19. import java.util.List;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.geometry.euclidean.threed.FieldLine;
  22. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  23. import org.hipparchus.geometry.euclidean.threed.Line;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.geometry.euclidean.threed.Rotation;
  26. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  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.FieldStaticTransform;
  33. import org.orekit.frames.FieldTransform;
  34. import org.orekit.frames.Frame;
  35. import org.orekit.frames.StaticTransform;
  36. import org.orekit.frames.Transform;
  37. import org.orekit.time.AbsoluteDate;
  38. import org.orekit.time.FieldAbsoluteDate;
  39. import org.orekit.time.FieldTimeInterpolator;
  40. import org.orekit.time.TimeOffset;
  41. import org.orekit.time.TimeInterpolator;
  42. import org.orekit.utils.CartesianDerivativesFilter;
  43. import org.orekit.utils.Constants;
  44. import org.orekit.utils.FieldPVCoordinatesProvider;
  45. import org.orekit.utils.PVCoordinatesProvider;
  46. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  47. import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator;
  48. import org.orekit.utils.TimeStampedPVCoordinates;
  49. import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;

  50. /**
  51.  * This class provides a default attitude provider.

  52.  * <p>
  53.  * The attitude pointing law is defined by an attitude provider and
  54.  * the satellite axis vector chosen for pointing.
  55.  * </p>
  56.  * @author V&eacute;ronique Pommier-Maurussane
  57.  */
  58. public class LofOffsetPointing extends GroundPointing {

  59.     /** Rotation from local orbital frame. */
  60.     private final AttitudeProvider attitudeLaw;

  61.     /** Body shape. */
  62.     private final BodyShape shape;

  63.     /** Chosen satellite axis for pointing, given in satellite frame. */
  64.     private final Vector3D satPointingVector;

  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.     /** {@inheritDoc} */
  80.     @Override
  81.     public Attitude getAttitude(final PVCoordinatesProvider pvProv,
  82.                                 final AbsoluteDate date, final Frame frame) {
  83.         return attitudeLaw.getAttitude(pvProv, date, frame);
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
  88.                                                                             final FieldAbsoluteDate<T> date, final Frame frame) {
  89.         return attitudeLaw.getAttitude(pvProv, date, frame);
  90.     }

  91.     /** {@inheritDoc} */
  92.     @Override
  93.     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv,
  94.                                         final AbsoluteDate date, final Frame frame) {
  95.         return attitudeLaw.getAttitudeRotation(pvProv, date, frame);
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
  100.                                                                                     final FieldAbsoluteDate<T> date, final Frame frame) {
  101.         return attitudeLaw.getAttitudeRotation(pvProv, date, frame);
  102.     }

  103.     /** {@inheritDoc} */
  104.     @Override
  105.     public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
  106.                                                 final AbsoluteDate date, final Frame frame) {

  107.         // sample intersection points in current date neighborhood
  108.         final List<TimeStampedPVCoordinates> sample = new ArrayList<>();
  109.         Transform centralRefToBody = null;
  110.         for (int i = -1; i < 2; ++i) {

  111.             final AbsoluteDate shifted = date.shiftedBy(new TimeOffset(i * 100, TimeOffset.MILLISECOND));

  112.             // transform from specified reference frame to spacecraft frame
  113.             final StaticTransform refToSc = StaticTransform.of(shifted, pvProv.getPosition(shifted, frame).negate(),
  114.                 attitudeLaw.getAttitudeRotation(pvProv, shifted, frame));

  115.             // transform from specified reference frame to body frame
  116.             final StaticTransform refToBody;
  117.             if (i == 0) {
  118.                 refToBody = centralRefToBody = frame.getTransformTo(shape.getBodyFrame(), shifted);
  119.             } else {
  120.                 refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), shifted);
  121.             }

  122.             sample.add(losIntersectionWithBody(StaticTransform.compose(shifted, refToSc.getInverse(), refToBody)));

  123.         }

  124.         // create interpolator
  125.         final TimeInterpolator<TimeStampedPVCoordinates> interpolator =
  126.                 new TimeStampedPVCoordinatesHermiteInterpolator(sample.size(), CartesianDerivativesFilter.USE_P);

  127.         // use interpolation to compute properly the time-derivatives
  128.         final TimeStampedPVCoordinates targetBody =
  129.                 interpolator.interpolate(date, sample);

  130.         // convert back to caller specified frame
  131.         return centralRefToBody.getInverse().transformPVCoordinates(targetBody);

  132.     }

  133.     /** {@inheritDoc} */
  134.     @Override
  135.     protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {

  136.         // transform from specified reference frame to spacecraft frame
  137.         final StaticTransform refToSc = StaticTransform.of(date, pvProv.getPosition(date, frame).negate(),
  138.             attitudeLaw.getAttitudeRotation(pvProv, date, frame));

  139.         // transform from specified reference frame to body frame
  140.         final StaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date);
  141.         final Vector3D targetBody = losIntersectionWithBody(StaticTransform.compose(date, refToSc.getInverse(), refToBody)).getPosition();

  142.         // convert back to caller specified frame
  143.         return refToBody.getInverse().transformPosition(targetBody);
  144.     }

  145.     /** {@inheritDoc} */
  146.     @Override
  147.     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
  148.                                                                                             final FieldAbsoluteDate<T> date,
  149.                                                                                             final Frame frame) {

  150.         // sample intersection points in current date neighborhood
  151.         final List<TimeStampedFieldPVCoordinates<T>> sample = new ArrayList<>();
  152.         FieldTransform<T> centralRefToBody = null;
  153.         for (int i = -1; i < 2; ++i) {

  154.             final FieldAbsoluteDate<T> shifted = date.shiftedBy(new TimeOffset(i * 100, TimeOffset.MILLISECOND));

  155.             // transform from specified reference frame to spacecraft frame
  156.             final FieldStaticTransform<T> refToSc = FieldStaticTransform.of(shifted,
  157.                 pvProv.getPVCoordinates(shifted, frame).getPosition().negate(), attitudeLaw.getAttitudeRotation(pvProv, shifted, frame));

  158.             // transform from specified reference frame to body frame
  159.             final FieldStaticTransform<T> refToBody;
  160.             if (i == 0) {
  161.                 refToBody = centralRefToBody = frame.getTransformTo(shape.getBodyFrame(), shifted);
  162.             } else {
  163.                 refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), shifted);
  164.             }

  165.             sample.add(losIntersectionWithBody(FieldStaticTransform.compose(shifted, refToSc.getInverse(), refToBody)));

  166.         }

  167.         // create interpolator
  168.         final FieldTimeInterpolator<TimeStampedFieldPVCoordinates<T>, T> interpolator =
  169.                 new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(sample.size(), CartesianDerivativesFilter.USE_P);

  170.         // use interpolation to compute properly the time-derivatives
  171.         final TimeStampedFieldPVCoordinates<T> targetBody =
  172.                 interpolator.interpolate(date, sample);

  173.         // convert back to caller specified frame
  174.         return centralRefToBody.getInverse().transformPVCoordinates(targetBody);

  175.     }

  176.     /** {@inheritDoc} */
  177.     @Override
  178.     protected <T extends CalculusFieldElement<T>> FieldVector3D<T> getTargetPosition(final FieldPVCoordinatesProvider<T> pvProv,
  179.                                                                                      final FieldAbsoluteDate<T> date,
  180.                                                                                      final Frame frame) {

  181.         // transform from specified reference frame to spacecraft frame
  182.         final FieldStaticTransform<T> refToSc = FieldStaticTransform.of(date, pvProv.getPosition(date, frame).negate(),
  183.                 attitudeLaw.getAttitudeRotation(pvProv, date, frame));

  184.         // transform from specified reference frame to body frame
  185.         final FieldStaticTransform<T> refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date);
  186.         final FieldVector3D<T> targetBody = losIntersectionWithBody(FieldStaticTransform.compose(date, refToSc.getInverse(), refToBody)).getPosition();

  187.         // convert back to caller specified frame
  188.         return refToBody.getInverse().transformPosition(targetBody);
  189.     }

  190.     /** Compute line of sight intersection with body.
  191.      * @param scToBody transform from spacecraft frame to body frame
  192.      * @return intersection point in body frame (only the position is set!)
  193.      */
  194.     private TimeStampedPVCoordinates losIntersectionWithBody(final StaticTransform scToBody) {

  195.         // compute satellite pointing axis and position/velocity in body frame
  196.         final Vector3D pointingBodyFrame = scToBody.transformVector(satPointingVector);
  197.         final Vector3D pBodyFrame        = scToBody.transformPosition(Vector3D.ZERO);

  198.         // Line from satellite following pointing direction
  199.         // we use arbitrarily the Earth radius as a scaling factor, it could be anything else
  200.         final Line pointingLine = new Line(pBodyFrame,
  201.                                            pBodyFrame.add(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
  202.                                                           pointingBodyFrame),
  203.                                            1.0e-10);

  204.         // Intersection with body shape
  205.         final GeodeticPoint gpIntersection =
  206.             shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), scToBody.getDate());
  207.         final Vector3D pIntersection =
  208.             (gpIntersection == null) ? null : shape.transform(gpIntersection);

  209.         // Check there is an intersection and it is not in the reverse pointing direction
  210.         if (pIntersection == null ||
  211.             Vector3D.dotProduct(pIntersection.subtract(pBodyFrame), pointingBodyFrame) < 0) {
  212.             throw new OrekitException(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND);
  213.         }

  214.         return new TimeStampedPVCoordinates(scToBody.getDate(),
  215.                                             pIntersection, Vector3D.ZERO, Vector3D.ZERO);

  216.     }

  217.     /** Compute line of sight intersection with body.
  218.      * @param scToBody transform from spacecraft frame to body frame
  219.      * @param <T> type of the field elements
  220.      * @return intersection point in body frame (only the position is set!)
  221.      */
  222.     private <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> losIntersectionWithBody(final FieldStaticTransform<T> scToBody) {

  223.         // compute satellite pointing axis and position/velocity in body frame
  224.         final FieldVector3D<T> pointingBodyFrame = scToBody.transformVector(satPointingVector);
  225.         final FieldVector3D<T> pBodyFrame        = scToBody.transformPosition(Vector3D.ZERO);

  226.         // Line from satellite following pointing direction
  227.         // we use arbitrarily the Earth radius as a scaling factor, it could be anything else
  228.         final FieldLine<T> pointingLine = new FieldLine<>(pBodyFrame,
  229.                                                           pBodyFrame.add(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
  230.                                                                          pointingBodyFrame),
  231.                                                           1.0e-10);

  232.         // Intersection with body shape
  233.         final FieldGeodeticPoint<T> gpIntersection =
  234.             shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), new FieldAbsoluteDate<>(pBodyFrame.getX().getField(), scToBody.getDate()));
  235.         final FieldVector3D<T> pIntersection =
  236.             (gpIntersection == null) ? null : shape.transform(gpIntersection);

  237.         // Check there is an intersection and it is not in the reverse pointing direction
  238.         if (pIntersection == null ||
  239.             FieldVector3D.dotProduct(pIntersection.subtract(pBodyFrame), pointingBodyFrame).getReal() < 0) {
  240.             throw new OrekitException(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND);
  241.         }

  242.         final FieldVector3D<T> zero = FieldVector3D.getZero(pBodyFrame.getX().getField());
  243.         return new TimeStampedFieldPVCoordinates<>(scToBody.getDate(),
  244.                                                    pIntersection, zero, zero);

  245.     }

  246. }