Attitude.java

/* Copyright 2002-2013 CS Systèmes d'Information
 * Licensed to CS Systèmes d'Information (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.attitudes;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.util.Pair;
import org.orekit.errors.OrekitException;
import org.orekit.frames.Frame;
import org.orekit.frames.Transform;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeInterpolable;
import org.orekit.time.TimeShiftable;
import org.orekit.time.TimeStamped;
import org.orekit.utils.AngularCoordinates;


/** This class handles attitude definition at a given date.

 * <p>This class represents the rotation between a reference frame and
 * the satellite frame, as well as the spin of the satellite (axis and
 * rotation rate).</p>
 * <p>
 * The state can be slightly shifted to close dates. This shift is based on
 * a linear extrapolation for attitude taking the spin rate into account.
 * It is <em>not</em> intended as a replacement for proper attitude propagation
 * but should be sufficient for either small time shifts or coarse accuracy.
 * </p>
 * <p>The instance <code>Attitude</code> is guaranteed to be immutable.</p>
 * @see     org.orekit.orbits.Orbit
 * @see AttitudeProvider
 * @author V&eacute;ronique Pommier-Maurussane
 */

public class Attitude
    implements TimeStamped, TimeShiftable<Attitude>, TimeInterpolable<Attitude>, Serializable {

    /** Serializable UID. */
    private static final long serialVersionUID = -947817502698754209L;

    /** Current date. */
    private final AbsoluteDate date;

    /** Reference frame. */
    private final Frame referenceFrame;

     /** Attitude and spin.  */
    private final AngularCoordinates orientation;

    /** Creates a new instance.
     * @param date date at which attitude is defined
     * @param referenceFrame reference frame from which attitude is defined
     * @param orientation complete orientation between reference frame and satellite frame,
     * including rotation rate
     */
    public Attitude(final AbsoluteDate date, final Frame referenceFrame,
                    final AngularCoordinates orientation) {
        this.date           = date;
        this.referenceFrame = referenceFrame;
        this.orientation    = orientation;
    }

    /** Creates a new instance.
     * @param date date at which attitude is defined
     * @param referenceFrame reference frame from which attitude is defined
     * @param attitude rotation between reference frame and satellite frame
     * @param spin satellite spin (axis and velocity, in <strong>satellite</strong> frame)
     */
    public Attitude(final AbsoluteDate date, final Frame referenceFrame,
                    final Rotation attitude, final Vector3D spin) {
        this(date, referenceFrame, new AngularCoordinates(attitude, spin));
    }

    /** Estimate spin between two orientations.
     * <p>Estimation is based on a simple fixed rate rotation
     * during the time interval between the two attitude.</p>
     * @param start start orientation
     * @param end end orientation
     * @param dt time elapsed between the dates of the two orientations
     * @return spin allowing to go from start to end orientation
     * @deprecated as of 6.0 superseded by {@link
     * AngularCoordinates#estimateRate(Rotation, Rotation, double)}
     */
    @Deprecated
    public static Vector3D estimateSpin(final Rotation start, final Rotation end,
                                        final double dt) {
        return AngularCoordinates.estimateRate(start, end, dt);
    }

    /** Get a time-shifted attitude.
     * <p>
     * The state can be slightly shifted to close dates. This shift is based on
     * a linear extrapolation for attitude taking the spin rate into account.
     * It is <em>not</em> intended as a replacement for proper attitude propagation
     * but should be sufficient for either small time shifts or coarse accuracy.
     * </p>
     * @param dt time shift in seconds
     * @return a new attitude, shifted with respect to the instance (which is immutable)
     */
    public Attitude shiftedBy(final double dt) {
        return new Attitude(date.shiftedBy(dt), referenceFrame, orientation.shiftedBy(dt));
    }

    /** Get a similar attitude with a specific reference frame.
     * <p>
     * If the instance reference frame is already the specified one, the instance
     * itself is returned without any object creation. Otherwise, a new instance
     * will be created with the specified reference frame. In this case, the
     * required intermediate rotation and spin between the specified and the
     * original reference frame will be inserted.
     * </p>
     * @param newReferenceFrame desired reference frame for attitude
     * @return an attitude that has the same orientation and motion as the instance,
     * but guaranteed to have the specified reference frame
     * @exception OrekitException if conversion between reference frames fails
     */
    public Attitude withReferenceFrame(final Frame newReferenceFrame)
        throws OrekitException {

        if (newReferenceFrame == referenceFrame) {
            // simple case, the instance is already compliant
            return this;
        }

        // we have to take an intermediate rotation into account
        final Transform t = newReferenceFrame.getTransformTo(referenceFrame, date);
        return new Attitude(date, newReferenceFrame,
                            orientation.getRotation().applyTo(t.getRotation()),
                            orientation.getRotationRate().add(orientation.getRotation().applyTo(t.getRotationRate())));

    }

    /** Get the date of attitude parameters.
     * @return date of the attitude parameters
     */
    public AbsoluteDate getDate() {
        return date;
    }

    /** Get the reference frame.
     * @return referenceFrame reference frame from which attitude is defined.
     */
    public Frame getReferenceFrame() {
        return referenceFrame;
    }

    /** Get the complete orientation including spin.
     * @return complete orientation including spin
     * @see #getRotation()
     * @see #getSpin()
     */
    public AngularCoordinates getOrientation() {
        return orientation;
    }

    /** Get the attitude rotation.
     * @return attitude satellite rotation from reference frame.
     * @see #getOrientation()
     * @see #getSpin()
     */
    public Rotation getRotation() {
        return orientation.getRotation();
    }

    /** Get the satellite spin.
     * <p>The spin vector is defined in <strong>satellite</strong> frame.</p>
     * @return spin satellite spin (axis and velocity).
     * @see #getOrientation()
     * @see #getRotation()
     */
    public Vector3D getSpin() {
        return orientation.getRotationRate();
    }

    /** {@inheritDoc}
     * <p>
     * The interpolated instance is created by polynomial Hermite interpolation
     * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation.
     * </p>
     * <p>
     * As this implementation of interpolation is polynomial, it should be used only
     * with small samples (about 10-20 points) in order to avoid <a
     * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
     * and numerical problems (including NaN appearing).
     * </p>
     */
    public Attitude interpolate(final AbsoluteDate interpolationDate, final Collection<Attitude> sample) {
        final List<Pair<AbsoluteDate, AngularCoordinates>> datedPV =
                new ArrayList<Pair<AbsoluteDate, AngularCoordinates>>(sample.size());
        for (final Attitude attitude : sample) {
            datedPV.add(new Pair<AbsoluteDate, AngularCoordinates>(attitude.getDate(), attitude.getOrientation()));
        }
        final AngularCoordinates interpolated = AngularCoordinates.interpolate(interpolationDate, true, datedPV);
        return new Attitude(interpolationDate, referenceFrame, interpolated);
    }

}