YawCompensation.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.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. /**
  37.  * This class handles yaw compensation attitude provider.

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

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

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

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

  74.     /** {@inheritDoc} */
  75.     @Override
  76.     public Attitude getAttitude(final PVCoordinatesProvider pvProv,
  77.                                 final AbsoluteDate date, final Frame frame) {

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

  79.         // compute sliding target ground point
  80.         final PVCoordinates slidingRef  = getTargetPV(pvProv, date, frame);
  81.         final PVCoordinates slidingBody = bodyToRef.getInverse().transformPVCoordinates(slidingRef);

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

  85.         // compute relative velocity of fixed ground point with respect to sliding ground point
  86.         // the velocity part of the transformPVCoordinates for the sliding point ps
  87.         // from body frame to reference frame is:
  88.         //     d(ps_ref)/dt = r(d(ps_body)/dt + dq/dt) - Ω ⨯ ps_ref
  89.         // where r is the rotation part of the transform, Ω is the corresponding
  90.         // angular rate, and dq/dt is the derivative of the translation part of the
  91.         // transform (the translation itself, without derivative, is hidden in the
  92.         // ps_ref part in the cross product).
  93.         // The sliding point ps is co-located to a fixed ground point pf (i.e. they have the
  94.         // same position at time of computation), but this fixed point as zero velocity
  95.         // with respect to the ground. So the velocity part of the transformPVCoordinates
  96.         // for this fixed point can be computed using the same formula as above:
  97.         // from body frame to reference frame is:
  98.         //     d(pf_ref)/dt = r(0 + dq/dt) - Ω ⨯ pf_ref
  99.         // so remembering that the two points are at the same location at computation time,
  100.         // i.e. that at t=0 pf_ref=ps_ref, the relative velocity between the fixed point
  101.         // and the sliding point is given by the simple expression:
  102.         //     d(ps_ref)/dt - d(pf_ref)/dt = r(d(ps_body)/dt)
  103.         // the acceleration is computed by differentiating the expression, which gives:
  104.         //    d²(ps_ref)/dt² - d²(pf_ref)/dt² = r(d²(ps_body)/dt²) - Ω ⨯ [d(ps_ref)/dt - d(pf_ref)/dt]
  105.         final Vector3D v = bodyToRef.getRotation().applyTo(slidingBody.getVelocity());
  106.         final Vector3D a = new Vector3D(+1, bodyToRef.getRotation().applyTo(slidingBody.getAcceleration()),
  107.                                         -1, Vector3D.crossProduct(bodyToRef.getRotationRate(), v));
  108.         final PVCoordinates relativeVelocity = new PVCoordinates(v, a, Vector3D.ZERO);

  109.         final PVCoordinates relativeNormal =
  110.                 PVCoordinates.crossProduct(relativePosition, relativeVelocity).normalize();

  111.         // attitude definition :
  112.         //  . Z satellite axis points to sliding target
  113.         //  . target relative velocity is in (Z, X) plane, in the -X half plane part
  114.         return new Attitude(frame,
  115.                             new TimeStampedAngularCoordinates(date,
  116.                                                               relativePosition.normalize(),
  117.                                                               relativeNormal.normalize(),
  118.                                                               PLUS_K, PLUS_J,
  119.                                                               1.0e-9));

  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
  124.                                                                         final FieldAbsoluteDate<T> date, final Frame frame) {

  125.         final Field<T>              field = date.getField();
  126.         final FieldVector3D<T>      zero  = FieldVector3D.getZero(field);
  127.         final FieldPVCoordinates<T> plusJ = new FieldPVCoordinates<>(FieldVector3D.getPlusJ(field), zero, zero);
  128.         final FieldPVCoordinates<T> plusK = new FieldPVCoordinates<>(FieldVector3D.getPlusK(field), zero, zero);

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

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

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

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

  160.         final FieldPVCoordinates<T> relativeNormal =
  161.                         relativePosition.crossProduct(relativeVelocity).normalize();

  162.         // attitude definition :
  163.         //  . Z satellite axis points to sliding target
  164.         //  . target relative velocity is in (Z, X) plane, in the -X half plane part
  165.         return new FieldAttitude<>(frame,
  166.                                    new TimeStampedFieldAngularCoordinates<>(date,
  167.                                                                             relativePosition.normalize(),
  168.                                                                             relativeNormal.normalize(),
  169.                                                                             plusK, plusJ,
  170.                                                                             1.0e-9));

  171.     }

  172.     /** Compute the yaw compensation angle at date.
  173.      * @param pvProv provider for PV coordinates
  174.      * @param date date at which compensation is requested
  175.      * @param frame reference frame from which attitude is computed
  176.      * @return yaw compensation angle for orbit.
  177.      */
  178.     public double getYawAngle(final PVCoordinatesProvider pvProv,
  179.                               final AbsoluteDate date, final Frame frame) {
  180.         final Rotation rBase        = getBaseState(pvProv, date, frame).getRotation();
  181.         final Rotation rCompensated = getAttitude(pvProv, date, frame).getRotation();
  182.         final Rotation compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
  183.         return -compensation.applyTo(Vector3D.PLUS_I).getAlpha();
  184.     }

  185.     /** Compute the yaw compensation angle at date.
  186.      * @param pvProv provider for PV coordinates
  187.      * @param date date at which compensation is requested
  188.      * @param frame reference frame from which attitude is computed
  189.      * @param <T> type of the field elements
  190.      * @return yaw compensation angle for orbit.
  191.      * @since 9.0
  192.      */
  193.     public <T extends CalculusFieldElement<T>> T getYawAngle(final FieldPVCoordinatesProvider<T> pvProv,
  194.                                                          final FieldAbsoluteDate<T> date,
  195.                                                          final Frame frame) {
  196.         final FieldRotation<T> rBase        = getBaseState(pvProv, date, frame).getRotation();
  197.         final FieldRotation<T> rCompensated = getAttitude(pvProv, date, frame).getRotation();
  198.         final FieldRotation<T> compensation = rCompensated.compose(rBase.revert(), RotationConvention.VECTOR_OPERATOR);
  199.         return compensation.applyTo(Vector3D.PLUS_I).getAlpha().negate();
  200.     }

  201. }