FieldAngularCoordinates.java

  1. /* Copyright 2002-2016 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.utils;

  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.List;

  22. import org.apache.commons.math3.RealFieldElement;
  23. import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
  24. import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
  25. import org.apache.commons.math3.geometry.euclidean.threed.RotationConvention;
  26. import org.apache.commons.math3.util.Pair;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.TimeShiftable;

  30. /** Simple container for FieldRotation<T>/FieldRotation<T> rate pairs, using {@link RealFieldElement}.
  31.  * <p>
  32.  * The state can be slightly shifted to close dates. This shift is based on
  33.  * a simple quadratic model. It is <em>not</em> intended as a replacement for
  34.  * proper attitude propagation but should be sufficient for either small
  35.  * time shifts or coarse accuracy.
  36.  * </p>
  37.  * <p>
  38.  * This class is the angular counterpart to {@link FieldPVCoordinates}.
  39.  * </p>
  40.  * <p>Instances of this class are guaranteed to be immutable.</p>
  41.  * @param <T> the type of the field elements
  42.  * @author Luc Maisonobe
  43.  * @since 6.0
  44.  * @see AngularCoordinates
  45.  */
  46. public class FieldAngularCoordinates<T extends RealFieldElement<T>>
  47.      implements TimeShiftable<FieldAngularCoordinates<T>>, Serializable {

  48.     /** Serializable UID. */
  49.     private static final long serialVersionUID = 20140414L;

  50.     /** FieldRotation<T>. */
  51.     private final FieldRotation<T> rotation;

  52.     /** FieldRotation<T> rate. */
  53.     private final FieldVector3D<T> rotationRate;

  54.     /** FieldRotation<T> acceleration. */
  55.     private final FieldVector3D<T> rotationAcceleration;

  56.     /** Builds a rotation/rotation rate pair.
  57.      * @param rotation rotation
  58.      * @param rotationRate rotation rate Ω (rad/s)
  59.      */
  60.     public FieldAngularCoordinates(final FieldRotation<T> rotation, final FieldVector3D<T> rotationRate) {
  61.         this(rotation, rotationRate,
  62.              new FieldVector3D<T>(rotation.getQ0().getField().getZero(),
  63.                                   rotation.getQ0().getField().getZero(),
  64.                                   rotation.getQ0().getField().getZero()));
  65.     }

  66.     /** Builds a FieldRotation<T>/FieldRotation<T> rate/FieldRotation<T> acceleration triplet.
  67.      * @param rotation FieldRotation<T>
  68.      * @param rotationRate FieldRotation<T> rate Ω (rad/s)
  69.      * @param rotationAcceleration FieldRotation<T> acceleration dΩ/dt (rad²/s²)
  70.      */
  71.     public FieldAngularCoordinates(final FieldRotation<T> rotation, final FieldVector3D<T> rotationRate,
  72.                                    final FieldVector3D<T> rotationAcceleration) {
  73.         this.rotation             = rotation;
  74.         this.rotationRate         = rotationRate;
  75.         this.rotationAcceleration = rotationAcceleration;
  76.     }

  77.     /** Estimate FieldRotation<T> rate between two orientations.
  78.      * <p>Estimation is based on a simple fixed rate FieldRotation<T>
  79.      * during the time interval between the two orientations.</p>
  80.      * @param start start orientation
  81.      * @param end end orientation
  82.      * @param dt time elapsed between the dates of the two orientations
  83.      * @param <T> the type of the field elements
  84.      * @return FieldRotation<T> rate allowing to go from start to end orientations
  85.      */
  86.     public static <T extends RealFieldElement<T>>
  87.         FieldVector3D<T> estimateRate(final FieldRotation<T> start,
  88.                                       final FieldRotation<T> end,
  89.                                       final double dt) {
  90.         final FieldRotation<T> evolution = start.compose(end.revert(), RotationConvention.VECTOR_OPERATOR);
  91.         return new FieldVector3D<T>(evolution.getAngle().divide(dt),
  92.                                     evolution.getAxis(RotationConvention.VECTOR_OPERATOR));
  93.     }

  94.     /** Revert a FieldRotation<T>/FieldRotation<T>/FieldRotation<T> acceleration triplet.
  95.      * Build a triplet which reverse the effect of another triplet.
  96.      * @return a new triplet whose effect is the reverse of the effect
  97.      * of the instance
  98.      */
  99.     public FieldAngularCoordinates<T> revert() {
  100.         return new FieldAngularCoordinates<T>(rotation.revert(),
  101.                                               rotation.applyInverseTo(rotationRate.negate()),
  102.                                               rotation.applyInverseTo(rotationAcceleration.negate()));
  103.     }

  104.     /** Get a time-shifted state.
  105.      * <p>
  106.      * The state can be slightly shifted to close dates. This shift is based on
  107.      * a simple quadratic model. It is <em>not</em> intended as a replacement for
  108.      * proper attitude propagation but should be sufficient for either small
  109.      * time shifts or coarse accuracy.
  110.      * </p>
  111.      * @param dt time shift in seconds
  112.      * @return a new state, shifted with respect to the instance (which is immutable)
  113.      */
  114.     public FieldAngularCoordinates<T> shiftedBy(final double dt) {

  115.         // the shiftedBy method is based on a local approximation.
  116.         // It considers separately the contribution of the constant
  117.         // rotation, the linear contribution or the rate and the
  118.         // quadratic contribution of the acceleration. The rate
  119.         // and acceleration contributions are small rotations as long
  120.         // as the time shift is small, which is the crux of the algorithm.
  121.         // Small rotations are almost commutative, so we append these small
  122.         // contributions one after the other, as if they really occurred
  123.         // successively, despite this is not what really happens.

  124.         // compute the linear contribution first, ignoring acceleration
  125.         // BEWARE: there is really a minus sign here, because if
  126.         // the target frame rotates in one direction, the vectors in the origin
  127.         // frame seem to rotate in the opposite direction
  128.         final T rate = rotationRate.getNorm();
  129.         final T zero = rate.getField().getZero();
  130.         final T one  = rate.getField().getOne();
  131.         final FieldRotation<T> rateContribution = (rate.getReal() == 0.0) ?
  132.                                                   new FieldRotation<T>(one, zero, zero, zero, false) :
  133.                                                   new FieldRotation<T>(rotationRate,
  134.                                                                        rate.multiply(dt),
  135.                                                                        RotationConvention.FRAME_TRANSFORM);

  136.         // append rotation and rate contribution
  137.         final FieldAngularCoordinates<T> linearPart =
  138.                 new FieldAngularCoordinates<T>(rateContribution.compose(rotation, RotationConvention.VECTOR_OPERATOR),
  139.                                                rotationRate);

  140.         final T acc  = rotationAcceleration.getNorm();
  141.         if (acc.getReal() == 0.0) {
  142.             // no acceleration, the linear part is sufficient
  143.             return linearPart;
  144.         }

  145.         // compute the quadratic contribution, ignoring initial rotation and rotation rate
  146.         // BEWARE: there is really a minus sign here, because if
  147.         // the target frame rotates in one direction, the vectors in the origin
  148.         // frame seem to rotate in the opposite direction
  149.         final FieldAngularCoordinates<T> quadraticContribution =
  150.                 new FieldAngularCoordinates<T>(new FieldRotation<T>(rotationAcceleration,
  151.                                                                     acc.multiply(0.5 * dt * dt),
  152.                                                                     RotationConvention.FRAME_TRANSFORM),
  153.                                                new FieldVector3D<T>(dt, rotationAcceleration),
  154.                                                rotationAcceleration);

  155.         // the quadratic contribution is a small rotation:
  156.         // its initial angle and angular rate are both zero.
  157.         // small rotations are almost commutative, so we append the small
  158.         // quadratic part after the linear part as a simple offset
  159.         return quadraticContribution.addOffset(linearPart);

  160.     }

  161.     /** Get the FieldRotation<T>.
  162.      * @return the FieldRotation<T>.
  163.      */
  164.     public FieldRotation<T> getRotation() {
  165.         return rotation;
  166.     }

  167.     /** Get the FieldRotation<T> rate.
  168.      * @return the FieldRotation<T> rate vector (rad/s).
  169.      */
  170.     public FieldVector3D<T> getRotationRate() {
  171.         return rotationRate;
  172.     }

  173.     /** Get the rotation acceleration.
  174.      * @return the rotation acceleration vector dΩ/dt (rad²/s²).
  175.      */
  176.     public FieldVector3D<T> getRotationAcceleration() {
  177.         return rotationAcceleration;
  178.     }

  179.     /** Add an offset from the instance.
  180.      * <p>
  181.      * We consider here that the offset FieldRotation<T> is applied first and the
  182.      * instance is applied afterward. Note that angular coordinates do <em>not</em>
  183.      * commute under this operation, i.e. {@code a.addOffset(b)} and {@code
  184.      * b.addOffset(a)} lead to <em>different</em> results in most cases.
  185.      * </p>
  186.      * <p>
  187.      * The two methods {@link #addOffset(FieldAngularCoordinates) addOffset} and
  188.      * {@link #subtractOffset(FieldAngularCoordinates) subtractOffset} are designed
  189.      * so that round trip applications are possible. This means that both {@code
  190.      * ac1.subtractOffset(ac2).addOffset(ac2)} and {@code
  191.      * ac1.addOffset(ac2).subtractOffset(ac2)} return angular coordinates equal to ac1.
  192.      * </p>
  193.      * @param offset offset to subtract
  194.      * @return new instance, with offset subtracted
  195.      * @see #subtractOffset(FieldAngularCoordinates)
  196.      */
  197.     public FieldAngularCoordinates<T> addOffset(final FieldAngularCoordinates<T> offset) {
  198.         final FieldVector3D<T> rOmega    = rotation.applyTo(offset.rotationRate);
  199.         final FieldVector3D<T> rOmegaDot = rotation.applyTo(offset.rotationAcceleration);
  200.         return new FieldAngularCoordinates<T>(rotation.compose(offset.rotation, RotationConvention.VECTOR_OPERATOR),
  201.                                               rotationRate.add(rOmega),
  202.                                               new FieldVector3D<T>( 1.0, rotationAcceleration,
  203.                                                                     1.0, rOmegaDot,
  204.                                                                    -1.0, FieldVector3D.crossProduct(rotationRate, rOmega)));
  205.     }

  206.     /** Subtract an offset from the instance.
  207.      * <p>
  208.      * We consider here that the offset Rotation is applied first and the
  209.      * instance is applied afterward. Note that angular coordinates do <em>not</em>
  210.      * commute under this operation, i.e. {@code a.subtractOffset(b)} and {@code
  211.      * b.subtractOffset(a)} lead to <em>different</em> results in most cases.
  212.      * </p>
  213.      * <p>
  214.      * The two methods {@link #addOffset(FieldAngularCoordinates) addOffset} and
  215.      * {@link #subtractOffset(FieldAngularCoordinates) subtractOffset} are designed
  216.      * so that round trip applications are possible. This means that both {@code
  217.      * ac1.subtractOffset(ac2).addOffset(ac2)} and {@code
  218.      * ac1.addOffset(ac2).subtractOffset(ac2)} return angular coordinates equal to ac1.
  219.      * </p>
  220.      * @param offset offset to subtract
  221.      * @return new instance, with offset subtracted
  222.      * @see #addOffset(FieldAngularCoordinates)
  223.      */
  224.     public FieldAngularCoordinates<T> subtractOffset(final FieldAngularCoordinates<T> offset) {
  225.         return addOffset(offset.revert());
  226.     }

  227.     /** Convert to a regular angular coordinates.
  228.      * @return a regular angular coordinates
  229.      */
  230.     public AngularCoordinates toAngularCoordinates() {
  231.         return new AngularCoordinates(rotation.toRotation(), rotationRate.toVector3D(),
  232.                                       rotationAcceleration.toVector3D());
  233.     }

  234.     /** Interpolate angular coordinates.
  235.      * <p>
  236.      * The interpolated instance is created by polynomial Hermite interpolation
  237.      * on Rodrigues vector ensuring FieldRotation<T> rate remains the exact derivative of FieldRotation<T>.
  238.      * </p>
  239.      * <p>
  240.      * This method is based on Sergei Tanygin's paper <a
  241.      * href="http://www.agi.com/downloads/resources/white-papers/Attitude-interpolation.pdf">Attitude
  242.      * Interpolation</a>, changing the norm of the vector to match the modified Rodrigues
  243.      * vector as described in Malcolm D. Shuster's paper <a
  244.      * href="http://www.ladispe.polito.it/corsi/Meccatronica/02JHCOR/2011-12/Slides/Shuster_Pub_1993h_J_Repsurv_scan.pdf">A
  245.      * Survey of Attitude Representations</a>. This change avoids the singularity at π.
  246.      * There is still a singularity at 2π, which is handled by slightly offsetting all FieldRotation<T>s
  247.      * when this singularity is detected.
  248.      * </p>
  249.      * <p>
  250.      * Note that even if first time derivatives (FieldRotation<T> rates)
  251.      * from sample can be ignored, the interpolated instance always includes
  252.      * interpolated derivatives. This feature can be used explicitly to
  253.      * compute these derivatives when it would be too complex to compute them
  254.      * from an analytical formula: just compute a few sample points from the
  255.      * explicit formula and set the derivatives to zero in these sample points,
  256.      * then use interpolation to add derivatives consistent with the FieldRotation<T>s.
  257.      * </p>
  258.      * @param date interpolation date
  259.      * @param useRotationRates if true, use sample points FieldRotation<T> rates,
  260.      * otherwise ignore them and use only FieldRotation<T>s
  261.      * @param sample sample points on which interpolation should be done
  262.      * @param <T> the type of the field elements
  263.      * @return a new position-velocity, interpolated at specified date
  264.      * @exception OrekitException if the number of point is too small for interpolating
  265.      * @deprecated since 7.0 replaced with {@link TimeStampedFieldAngularCoordinates#interpolate(AbsoluteDate,
  266.      * AngularDerivativesFilter, Collection)}
  267.      */
  268.     @Deprecated
  269.     public static <T extends RealFieldElement<T>>
  270.         FieldAngularCoordinates<T> interpolate(final AbsoluteDate date,
  271.                                                final boolean useRotationRates,
  272.                                                final Collection<Pair<AbsoluteDate,
  273.                                                FieldAngularCoordinates<T>>> sample)
  274.         throws OrekitException {
  275.         final List<TimeStampedFieldAngularCoordinates<T>> list = new ArrayList<TimeStampedFieldAngularCoordinates<T>>(sample.size());
  276.         for (final Pair<AbsoluteDate, FieldAngularCoordinates<T>> pair : sample) {
  277.             list.add(new TimeStampedFieldAngularCoordinates<T>(pair.getFirst(),
  278.                                                                pair.getSecond().getRotation(),
  279.                                                                pair.getSecond().getRotationRate(),
  280.                                                                pair.getSecond().getRotationAcceleration()));
  281.         }
  282.         return TimeStampedFieldAngularCoordinates.interpolate(date,
  283.                                                               useRotationRates ? AngularDerivativesFilter.USE_RR : AngularDerivativesFilter.USE_R,
  284.                                                               list);
  285.     }

  286. }