YawCompensation.java

  1. /* Copyright 2002-2023 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.Field;
  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.geometry.euclidean.threed.Rotation;
  23. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.orekit.frames.FieldTransform;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.frames.Transform;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.FieldAbsoluteDate;
  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.TimeStampedAngularCoordinates;
  35. import org.orekit.utils.TimeStampedFieldAngularCoordinates;
  36. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  37. import org.orekit.utils.TimeStampedPVCoordinates;


  38. /**
  39.  * This class handles yaw compensation attitude provider.

  40.  * <p>
  41.  * Yaw compensation is mainly used for Earth observation satellites. As a
  42.  * satellites moves along its track, the image of ground points move on
  43.  * the focal point of the optical sensor. This motion is a combination of
  44.  * the satellite motion, but also on the Earth rotation and on the current
  45.  * attitude (in particular if the pointing includes Roll or Pitch offset).
  46.  * In order to reduce geometrical distortion, the yaw angle is changed a
  47.  * little from the simple ground pointing attitude such that the apparent
  48.  * motion of ground points is along a prescribed axis (orthogonal to the
  49.  * optical sensors rows), taking into account all effects.
  50.  * </p>
  51.  * <p>
  52.  * This attitude is implemented as a wrapper on top of an underlying ground
  53.  * pointing law that defines the roll and pitch angles.
  54.  * </p>
  55.  * <p>
  56.  * Instances of this class are guaranteed to be immutable.
  57.  * </p>
  58.  * @see     GroundPointing
  59.  * @author V&eacute;ronique Pommier-Maurussane
  60.  */
  61. public class YawCompensation extends GroundPointing implements AttitudeProviderModifier {

  62.     /** J axis. */
  63.     private static final PVCoordinates PLUS_J =
  64.             new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);

  65.     /** K axis. */
  66.     private static final PVCoordinates PLUS_K =
  67.             new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);

  68.     /** Underlying ground pointing attitude provider.  */
  69.     private final GroundPointing groundPointingLaw;

  70.     /** Creates a new instance.
  71.      * @param inertialFrame frame in which orbital velocities are computed
  72.      * @param groundPointingLaw ground pointing attitude provider without yaw compensation
  73.      * @since 7.1
  74.      */
  75.     public YawCompensation(final Frame inertialFrame, final GroundPointing groundPointingLaw) {
  76.         super(inertialFrame, groundPointingLaw.getBodyFrame());
  77.         this.groundPointingLaw = groundPointingLaw;
  78.     }

  79.     /** Get the underlying (ground pointing) attitude provider.
  80.      * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance
  81.      */
  82.     public AttitudeProvider getUnderlyingAttitudeProvider() {
  83.         return groundPointingLaw;
  84.     }

  85.     /** {@inheritDoc} */
  86.     public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv,
  87.                                                 final AbsoluteDate date, final Frame frame) {
  88.         return groundPointingLaw.getTargetPV(pvProv, date, frame);
  89.     }

  90.     /** {@inheritDoc} */
  91.     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getTargetPV(final FieldPVCoordinatesProvider<T> pvProv,
  92.                                                                                         final FieldAbsoluteDate<T> date,
  93.                                                                                         final Frame frame) {
  94.         return groundPointingLaw.getTargetPV(pvProv, date, frame);
  95.     }

  96.     /** Compute the base system state at given date, without compensation.
  97.      * @param pvProv provider for PV coordinates
  98.      * @param date date at which state is requested
  99.      * @param frame reference frame from which attitude is computed
  100.      * @return satellite base attitude state, i.e without compensation.
  101.      */
  102.     public Attitude getBaseState(final PVCoordinatesProvider pvProv,
  103.                                  final AbsoluteDate date, final Frame frame) {
  104.         return groundPointingLaw.getAttitude(pvProv, date, frame);
  105.     }

  106.     /** Compute the base system state at given date, without compensation.
  107.      * @param pvProv provider for PV coordinates
  108.      * @param date date at which state is requested
  109.      * @param frame reference frame from which attitude is computed
  110.      * @param <T> type of the field elements
  111.      * @return satellite base attitude state, i.e without compensation.
  112.      * @since 9.0
  113.      */
  114.     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getBaseState(final FieldPVCoordinatesProvider<T> pvProv,
  115.                                                                          final FieldAbsoluteDate<T> date, final Frame frame) {
  116.         return groundPointingLaw.getAttitude(pvProv, date, frame);
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public Attitude getAttitude(final PVCoordinatesProvider pvProv,
  121.                                 final AbsoluteDate date, final Frame frame) {

  122.         final Transform bodyToRef = getBodyFrame().getTransformTo(frame, date);

  123.         // compute sliding target ground point
  124.         final PVCoordinates slidingRef  = getTargetPV(pvProv, date, frame);
  125.         final PVCoordinates slidingBody = bodyToRef.getInverse().transformPVCoordinates(slidingRef);

  126.         // compute relative position of sliding ground point with respect to satellite
  127.         final PVCoordinates relativePosition =
  128.                 new PVCoordinates(pvProv.getPVCoordinates(date, frame), slidingRef);

  129.         // compute relative velocity of fixed ground point with respect to sliding ground point
  130.         // the velocity part of the transformPVCoordinates for the sliding point ps
  131.         // from body frame to reference frame is:
  132.         //     d(ps_ref)/dt = r(d(ps_body)/dt + dq/dt) - Ω ⨯ ps_ref
  133.         // where r is the rotation part of the transform, Ω is the corresponding
  134.         // angular rate, and dq/dt is the derivative of the translation part of the
  135.         // transform (the translation itself, without derivative, is hidden in the
  136.         // ps_ref part in the cross product).
  137.         // The sliding point ps is co-located to a fixed ground point pf (i.e. they have the
  138.         // same position at time of computation), but this fixed point as zero velocity
  139.         // with respect to the ground. So the velocity part of the transformPVCoordinates
  140.         // for this fixed point can be computed using the same formula as above:
  141.         // from body frame to reference frame is:
  142.         //     d(pf_ref)/dt = r(0 + dq/dt) - Ω ⨯ pf_ref
  143.         // so remembering that the two points are at the same location at computation time,
  144.         // i.e. that at t=0 pf_ref=ps_ref, the relative velocity between the fixed point
  145.         // and the sliding point is given by the simple expression:
  146.         //     d(ps_ref)/dt - d(pf_ref)/dt = r(d(ps_body)/dt)
  147.         // the acceleration is computed by differentiating the expression, which gives:
  148.         //    d²(ps_ref)/dt² - d²(pf_ref)/dt² = r(d²(ps_body)/dt²) - Ω ⨯ [d(ps_ref)/dt - d(pf_ref)/dt]
  149.         final Vector3D v = bodyToRef.getRotation().applyTo(slidingBody.getVelocity());
  150.         final Vector3D a = new Vector3D(+1, bodyToRef.getRotation().applyTo(slidingBody.getAcceleration()),
  151.                                         -1, Vector3D.crossProduct(bodyToRef.getRotationRate(), v));
  152.         final PVCoordinates relativeVelocity = new PVCoordinates(v, a, Vector3D.ZERO);

  153.         final PVCoordinates relativeNormal =
  154.                 PVCoordinates.crossProduct(relativePosition, relativeVelocity).normalize();

  155.         // attitude definition :
  156.         //  . Z satellite axis points to sliding target
  157.         //  . target relative velocity is in (Z, X) plane, in the -X half plane part
  158.         return new Attitude(frame,
  159.                             new TimeStampedAngularCoordinates(date,
  160.                                                               relativePosition.normalize(),
  161.                                                               relativeNormal.normalize(),
  162.                                                               PLUS_K, PLUS_J,
  163.                                                               1.0e-9));

  164.     }

  165.     /** {@inheritDoc} */
  166.     @Override
  167.     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
  168.                                                                         final FieldAbsoluteDate<T> date, final Frame frame) {

  169.         final Field<T>              field = date.getField();
  170.         final FieldVector3D<T>      zero  = FieldVector3D.getZero(field);
  171.         final FieldPVCoordinates<T> plusJ = new FieldPVCoordinates<>(FieldVector3D.getPlusJ(field), zero, zero);
  172.         final FieldPVCoordinates<T> plusK = new FieldPVCoordinates<>(FieldVector3D.getPlusK(field), zero, zero);

  173.         final FieldTransform<T> bodyToRef = getBodyFrame().getTransformTo(frame, date);

  174.         // compute sliding target ground point
  175.         final FieldPVCoordinates<T> slidingRef  = getTargetPV(pvProv, date, frame);
  176.         final FieldPVCoordinates<T> slidingBody = bodyToRef.getInverse().transformPVCoordinates(slidingRef);

  177.         // compute relative position of sliding ground point with respect to satellite
  178.         final FieldPVCoordinates<T> relativePosition =
  179.                 new FieldPVCoordinates<>(pvProv.getPVCoordinates(date, frame), slidingRef);

  180.         // compute relative velocity of fixed ground point with respect to sliding ground point
  181.         // the velocity part of the transformPVCoordinates for the sliding point ps
  182.         // from body frame to reference frame is:
  183.         //     d(ps_ref)/dt = r(d(ps_body)/dt + dq/dt) - Ω ⨯ ps_ref
  184.         // where r is the rotation part of the transform, Ω is the corresponding
  185.         // angular rate, and dq/dt is the derivative of the translation part of the
  186.         // transform (the translation itself, without derivative, is hidden in the
  187.         // ps_ref part in the cross product).
  188.         // The sliding point ps is co-located to a fixed ground point pf (i.e. they have the
  189.         // same position at time of computation), but this fixed point as zero velocity
  190.         // with respect to the ground. So the velocity part of the transformPVCoordinates
  191.         // for this fixed point can be computed using the same formula as above:
  192.         // from body frame to reference frame is:
  193.         //     d(pf_ref)/dt = r(0 + dq/dt) - Ω ⨯ pf_ref
  194.         // so remembering that the two points are at the same location at computation time,
  195.         // i.e. that at t=0 pf_ref=ps_ref, the relative velocity between the fixed point
  196.         // and the sliding point is given by the simple expression:
  197.         //     d(ps_ref)/dt - d(pf_ref)/dt = r(d(ps_body)/dt)
  198.         // the acceleration is computed by differentiating the expression, which gives:
  199.         //    d²(ps_ref)/dt² - d²(pf_ref)/dt² = r(d²(ps_body)/dt²) - Ω ⨯ [d(ps_ref)/dt - d(pf_ref)/dt]
  200.         final FieldVector3D<T> v = bodyToRef.getRotation().applyTo(slidingBody.getVelocity());
  201.         final FieldVector3D<T> a = new FieldVector3D<>(+1, bodyToRef.getRotation().applyTo(slidingBody.getAcceleration()),
  202.                                                        -1, FieldVector3D.crossProduct(bodyToRef.getRotationRate(), v));
  203.         final FieldPVCoordinates<T> relativeVelocity = new FieldPVCoordinates<>(v, a, FieldVector3D.getZero(date.getField()));

  204.         final FieldPVCoordinates<T> relativeNormal =
  205.                         relativePosition.crossProduct(relativeVelocity).normalize();

  206.         // attitude definition :
  207.         //  . Z satellite axis points to sliding target
  208.         //  . target relative velocity is in (Z, X) plane, in the -X half plane part
  209.         return new FieldAttitude<>(frame,
  210.                                    new TimeStampedFieldAngularCoordinates<>(date,
  211.                                                                             relativePosition.normalize(),
  212.                                                                             relativeNormal.normalize(),
  213.                                                                             plusK, plusJ,
  214.                                                                             1.0e-9));

  215.     }

  216.     /** Compute the yaw compensation angle at date.
  217.      * @param pvProv provider for PV coordinates
  218.      * @param date date at which compensation is requested
  219.      * @param frame reference frame from which attitude is computed
  220.      * @return yaw compensation angle for orbit.
  221.      */
  222.     public double getYawAngle(final PVCoordinatesProvider pvProv,
  223.                               final AbsoluteDate date, final Frame frame) {
  224.         final Rotation rBase        = getBaseState(pvProv, date, frame).getRotation();
  225.         final Rotation rCompensated = getAttitude(pvProv, date, frame).getRotation();
  226.         final Rotation compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
  227.         return -compensation.applyTo(Vector3D.PLUS_I).getAlpha();
  228.     }

  229.     /** Compute the yaw compensation angle at date.
  230.      * @param pvProv provider for PV coordinates
  231.      * @param date date at which compensation is requested
  232.      * @param frame reference frame from which attitude is computed
  233.      * @param <T> type of the field elements
  234.      * @return yaw compensation angle for orbit.
  235.      * @since 9.0
  236.      */
  237.     public <T extends CalculusFieldElement<T>> T getYawAngle(final FieldPVCoordinatesProvider<T> pvProv,
  238.                                                          final FieldAbsoluteDate<T> date,
  239.                                                          final Frame frame) {
  240.         final FieldRotation<T> rBase        = getBaseState(pvProv, date, frame).getRotation();
  241.         final FieldRotation<T> rCompensated = getAttitude(pvProv, date, frame).getRotation();
  242.         final FieldRotation<T> compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
  243.         return compensation.applyTo(Vector3D.PLUS_I).getAlpha().negate();
  244.     }

  245. }