ConstantThrustManeuver.java
/* Copyright 2002-2015 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.forces.maneuvers;
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.ode.AbstractParameterizable;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.forces.ForceModel;
import org.orekit.frames.Frame;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.DateDetector;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.propagation.numerical.TimeDerivativesEquations;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.Constants;
/** This class implements a simple maneuver with constant thrust.
* <p>The maneuver is defined by a direction in satelliteframe.
* The current attitude of the spacecraft, defined by the current
* spacecraft state, will be used to compute the thrust direction in
* inertial frame. A typical case for tangential maneuvers is to use a
* {@link org.orekit.attitudes.LofOffset LOF aligned} attitude provider for state propagation and a
* velocity increment along the +X satellite axis.</p>
* @author Fabien Maussion
* @author Véronique Pommier-Maurussane
* @author Luc Maisonobe
*/
public class ConstantThrustManeuver extends AbstractParameterizable implements ForceModel {
/** Parameter name for thrust. */
private static final String THRUST = "thrust";
/** Parameter name for flow rate. */
private static final String FLOW_RATE = "flow rate";
/** State of the engine. */
private boolean firing;
/** Start of the maneuver. */
private final AbsoluteDate startDate;
/** End of the maneuver. */
private final AbsoluteDate endDate;
/** Engine thrust. */
private double thrust;
/** Engine flow-rate. */
private double flowRate;
/** Direction of the acceleration in satellite frame. */
private final Vector3D direction;
/** Simple constructor for a constant direction and constant thrust.
* @param date maneuver date
* @param duration the duration of the thrust (s) (if negative,
* the date is considered to be the stop date)
* @param thrust the thrust force (N)
* @param isp engine specific impulse (s)
* @param direction the acceleration direction in satellite frame.
*/
public ConstantThrustManeuver(final AbsoluteDate date, final double duration,
final double thrust, final double isp,
final Vector3D direction) {
super(THRUST, FLOW_RATE);
if (duration >= 0) {
this.startDate = date;
this.endDate = date.shiftedBy(duration);
} else {
this.endDate = date;
this.startDate = endDate.shiftedBy(duration);
}
this.thrust = thrust;
this.flowRate = -thrust / (Constants.G0_STANDARD_GRAVITY * isp);
this.direction = direction.normalize();
firing = false;
}
/** Get the thrust.
* @return thrust force (N).
*/
public double getThrust() {
return thrust;
}
/** Get the specific impulse.
* @return specific impulse (s).
*/
public double getISP() {
return -thrust / (Constants.G0_STANDARD_GRAVITY * flowRate);
}
/** Get the flow rate.
* @return flow rate (negative, kg/s).
*/
public double getFlowRate() {
return flowRate;
}
/** {@inheritDoc} */
public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder)
throws OrekitException {
if (firing) {
// compute thrust acceleration in inertial frame
adder.addAcceleration(new Vector3D(thrust / s.getMass(),
s.getAttitude().getRotation().applyInverseTo(direction)),
s.getFrame());
// compute flow rate
adder.addMassDerivative(flowRate);
}
}
/** {@inheritDoc} */
public FieldVector3D<DerivativeStructure> accelerationDerivatives(final AbsoluteDate date, final Frame frame,
final FieldVector3D<DerivativeStructure> position,
final FieldVector3D<DerivativeStructure> velocity,
final FieldRotation<DerivativeStructure> rotation,
final DerivativeStructure mass)
throws OrekitException {
if (firing) {
return new FieldVector3D<DerivativeStructure>(mass.reciprocal().multiply(thrust),
rotation.applyInverseTo(direction));
} else {
// constant (and null) acceleration when not firing
final int parameters = mass.getFreeParameters();
final int order = mass.getOrder();
return new FieldVector3D<DerivativeStructure>(new DerivativeStructure(parameters, order, 0.0),
new DerivativeStructure(parameters, order, 0.0),
new DerivativeStructure(parameters, order, 0.0));
}
}
/** {@inheritDoc} */
public FieldVector3D<DerivativeStructure> accelerationDerivatives(final SpacecraftState s, final String paramName)
throws OrekitException {
complainIfNotSupported(paramName);
if (firing) {
if (THRUST.equals(paramName)) {
final DerivativeStructure thrustDS = new DerivativeStructure(1, 1, 0, thrust);
return new FieldVector3D<DerivativeStructure>(thrustDS.divide(s.getMass()),
s.getAttitude().getRotation().applyInverseTo(direction));
} else if (FLOW_RATE.equals(paramName)) {
// acceleration does not depend on flow rate (only mass decrease does)
final DerivativeStructure zero = new DerivativeStructure(1, 1, 0.0);
return new FieldVector3D<DerivativeStructure>(zero, zero, zero);
} else {
throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, paramName,
THRUST + ", " + FLOW_RATE);
}
} else {
// constant (and null) acceleration when not firing
final DerivativeStructure zero = new DerivativeStructure(1, 1, 0.0);
return new FieldVector3D<DerivativeStructure>(zero, zero, zero);
}
}
/** {@inheritDoc} */
public EventDetector[] getEventsDetectors() {
return new EventDetector[] {
new DateDetector(startDate).withHandler(new FiringStartHandler()),
new DateDetector(endDate).withHandler(new FiringStopHandler())
};
}
/** {@inheritDoc} */
public double getParameter(final String name)
throws IllegalArgumentException {
complainIfNotSupported(name);
if (name.equals(THRUST)) {
return thrust;
}
return flowRate;
}
/** {@inheritDoc} */
public void setParameter(final String name, final double value)
throws IllegalArgumentException {
complainIfNotSupported(name);
if (name.equals(THRUST)) {
thrust = value;
} else {
flowRate = value;
}
}
/** Handler for start of maneuver. */
private class FiringStartHandler implements EventHandler<DateDetector> {
/** {@inheritDoc} */
@Override
public EventHandler.Action eventOccurred(final SpacecraftState s,
final DateDetector detector,
final boolean increasing) {
// start the maneuver
firing = true;
return EventHandler.Action.RESET_DERIVATIVES;
}
/** {@inheritDoc} */
@Override
public SpacecraftState resetState(final DateDetector detector, final SpacecraftState oldState) {
return oldState;
}
}
/** Handler for end of maneuver. */
private class FiringStopHandler implements EventHandler<DateDetector> {
/** {@inheritDoc} */
public EventHandler.Action eventOccurred(final SpacecraftState s,
final DateDetector detector,
final boolean increasing) {
// stop the maneuver
firing = false;
return EventHandler.Action.RESET_DERIVATIVES;
}
/** {@inheritDoc} */
@Override
public SpacecraftState resetState(final DateDetector detector, final SpacecraftState oldState) {
return oldState;
}
}
}