LofOffset.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.FieldRotation;
  21. import org.hipparchus.geometry.euclidean.threed.Rotation;
  22. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  23. import org.hipparchus.geometry.euclidean.threed.RotationOrder;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.frames.FieldTransform;
  27. import org.orekit.frames.Frame;
  28. import org.orekit.frames.LOF;
  29. import org.orekit.frames.Transform;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.time.FieldAbsoluteDate;
  32. import org.orekit.utils.FieldPVCoordinates;
  33. import org.orekit.utils.FieldPVCoordinatesProvider;
  34. import org.orekit.utils.PVCoordinates;
  35. import org.orekit.utils.PVCoordinatesProvider;


  36. /**
  37.  * Attitude law defined by fixed Roll, Pitch and Yaw angles (in any order)
  38.  * with respect to a local orbital frame.

  39.  * <p>
  40.  * The attitude provider is defined as a rotation offset from some local orbital frame.
  41.  * @author V&eacute;ronique Pommier-Maurussane
  42.  */
  43. public class LofOffset implements AttitudeProvider {

  44.     /** Local Orbital Frame. */
  45.     private final LOF lof;

  46.     /** Rotation from local orbital frame.  */
  47.     private final Rotation offset;

  48.     /** Inertial frame with respect to which orbit should be computed. */
  49.     private final Frame inertialFrame;

  50.     /** Create a LOF-aligned attitude.
  51.      * <p>
  52.      * Calling this constructor is equivalent to call
  53.      * {@code LofOffset(inertialFrame, LOF, RotationOrder.XYZ, 0, 0, 0)}
  54.      * </p>
  55.      * @param inertialFrame inertial frame with respect to which orbit should be computed
  56.      * @param lof local orbital frame
  57.      */
  58.     public LofOffset(final Frame inertialFrame, final LOF lof) {
  59.         this(inertialFrame, lof, RotationOrder.XYZ, 0, 0, 0);
  60.     }

  61.     /** Creates new instance.
  62.      * <p>
  63.      * An important thing to note is that the rotation order and angles signs used here
  64.      * are compliant with an <em>attitude</em> definition, i.e. they correspond to
  65.      * a frame that rotate in a field of fixed vectors. So to retrieve the angles
  66.      * provided here from the Hipparchus underlying rotation, one has to either use the
  67.      * {@link RotationConvention#VECTOR_OPERATOR} and <em>revert</em> the rotation, or
  68.      * to use {@link RotationConvention#FRAME_TRANSFORM} as in the following code snippet:
  69.      * </p>
  70.      * <pre>
  71.      *   LofOffset law          = new LofOffset(inertial, LOF, order, alpha1, alpha2, alpha3);
  72.      *   Rotation  offsetAtt    = law.getAttitude(orbit).getRotation();
  73.      *   Rotation  alignedAtt   = new LofOffset(inertial, LOF).getAttitude(orbit).getRotation();
  74.      *   Rotation  offsetProper = offsetAtt.compose(alignedAtt.revert(), RotationConvention.VECTOR_OPERATOR);
  75.      *
  76.      *   // note the call to revert and the conventions in the following statement
  77.      *   double[] anglesV = offsetProper.revert().getAngles(order, RotationConvention.VECTOR_OPERATOR);
  78.      *   System.out.format(Locale.US, "%f == %f%n", alpha1, anglesV[0]);
  79.      *   System.out.format(Locale.US, "%f == %f%n", alpha2, anglesV[1]);
  80.      *   System.out.format(Locale.US, "%f == %f%n", alpha3, anglesV[2]);
  81.      *
  82.      *   // note the conventions in the following statement
  83.      *   double[] anglesF = offsetProper.getAngles(order, RotationConvention.FRAME_TRANSFORM);
  84.      *   System.out.format(Locale.US, "%f == %f%n", alpha1, anglesF[0]);
  85.      *   System.out.format(Locale.US, "%f == %f%n", alpha2, anglesF[1]);
  86.      *   System.out.format(Locale.US, "%f == %f%n", alpha3, anglesF[2]);
  87.      * </pre>
  88.      * @param inertialFrame inertial frame with respect to which orbit should be computed
  89.      * @param lof local orbital frame
  90.      * @param order order of rotations to use for (alpha1, alpha2, alpha3) composition
  91.      * @param alpha1 angle of the first elementary rotation
  92.      * @param alpha2 angle of the second elementary rotation
  93.      * @param alpha3 angle of the third elementary rotation
  94.      */
  95.     public LofOffset(final Frame inertialFrame, final LOF lof,
  96.                      final RotationOrder order, final double alpha1,
  97.                      final double alpha2, final double alpha3) {
  98.         this.lof    = lof;
  99.         this.offset = new Rotation(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3).revert();
  100.         if (!inertialFrame.isPseudoInertial()) {
  101.             throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
  102.                                       inertialFrame.getName());
  103.         }
  104.         this.inertialFrame = inertialFrame;
  105.     }

  106.     /**
  107.      * Get the local orbital frame.
  108.      * @return the local orbital frame.
  109.      */
  110.     public LOF getLof() {
  111.         return this.lof;
  112.     }

  113.     /**
  114.      * Get the rotational offset.
  115.      * @return the rotational offset.
  116.      */
  117.     public Rotation getOffset() {
  118.         return this.offset;
  119.     }

  120.     /**
  121.      * Get the inertial frame.
  122.      * @return the inertial frame.
  123.      */
  124.     public Frame getInertialFrame() {
  125.         return this.inertialFrame;
  126.     }

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

  131.         // construction of the local orbital frame, using PV from inertial frame
  132.         final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame);
  133.         final Transform inertialToLof = lof.transformFromInertial(date, pv);

  134.         // take into account the specified start frame (which may not be an inertial one)
  135.         final Transform frameToInertial = frame.getTransformTo(inertialFrame, date);
  136.         final Transform frameToLof = new Transform(date, frameToInertial, inertialToLof);

  137.         // compose with offset rotation
  138.         return new Attitude(date, frame,
  139.                             offset.compose(frameToLof.getRotation(), RotationConvention.VECTOR_OPERATOR),
  140.                             offset.applyTo(frameToLof.getRotationRate()),
  141.                             offset.applyTo(frameToLof.getRotationAcceleration()));

  142.     }

  143.     /** {@inheritDoc} */
  144.     @Override
  145.     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
  146.                                                                             final FieldAbsoluteDate<T> date,
  147.                                                                             final Frame frame) {

  148.         // construction of the local orbital frame, using PV from inertial frame
  149.         final FieldPVCoordinates<T> pv = pvProv.getPVCoordinates(date, inertialFrame);
  150.         final FieldTransform<T> inertialToLof = lof.transformFromInertial(date, pv);

  151.         // take into account the specified start frame (which may not be an inertial one)
  152.         final FieldTransform<T> frameToInertial = frame.getTransformTo(inertialFrame, date);
  153.         final FieldTransform<T> frameToLof = new FieldTransform<>(date, frameToInertial, inertialToLof);

  154.         // compose with offset rotation
  155.         return new FieldAttitude<>(date, frame,
  156.                                    frameToLof.getRotation().compose(offset, RotationConvention.FRAME_TRANSFORM),
  157.                                    FieldRotation.applyTo(offset, frameToLof.getRotationRate()),
  158.                                    FieldRotation.applyTo(offset, frameToLof.getRotationAcceleration()));

  159.     }

  160.     /** {@inheritDoc} */
  161.     @Override
  162.     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
  163.         // construction of the local orbital frame, using PV from inertial frame
  164.         final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame);
  165.         final Rotation inertialToLof = lof.rotationFromInertial(date, pv);

  166.         // take into account the specified start frame (which may not be an inertial one)
  167.         final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM;
  168.         final Rotation frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation();
  169.         final Rotation frameToLof = frameToInertial.compose(inertialToLof, rotationConvention);

  170.         // compose with offset rotation
  171.         return frameToLof.compose(offset, rotationConvention);
  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
  176.                                                                                     final FieldAbsoluteDate<T> date,
  177.                                                                                     final Frame frame) {
  178.         // construction of the local orbital frame, using PV from inertial frame
  179.         final FieldPVCoordinates<T> pv = pvProv.getPVCoordinates(date, inertialFrame);
  180.         final Field<T> field = date.getField();
  181.         final FieldRotation<T> inertialToLof = lof.rotationFromInertial(field, date, pv);

  182.         // take into account the specified start frame (which may not be an inertial one)
  183.         final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM;
  184.         final FieldRotation<T> frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation();
  185.         final FieldRotation<T> frameToLof = frameToInertial.compose(inertialToLof, rotationConvention);

  186.         // compose with offset rotation
  187.         return frameToLof.compose(offset, rotationConvention);
  188.     }
  189. }