Commit f3bf486e authored by Mikael Fillastre's avatar Mikael Fillastre
Browse files

Initial version of configurable low thrust maneuver

parent 605b07b9
Pipeline #421 failed with stage
......@@ -21,6 +21,9 @@
</properties>
<body>
<release version="10.2" date="TBD" description="TBD">
<action dev="mikael" type="add">
Added a configurable low thrust maneuver based on detectors.
</action>
<action dev="bryan" type="fix" issue="605">
Added support for Rinex C0, L0, S0 and D0 observation types.
</action>
......
......@@ -259,7 +259,11 @@ public enum OrekitMessages implements Localizable {
EXCEPTIONAL_DATA_CONTEXT("Use of the ExceptionalDataContext detected. This is typically used to detect developer errors."),
NON_DIFFERENT_DATES_FOR_OBSERVATIONS("observations {0}, {1} and {2} must have different dates"),
NON_COPLANAR_POINTS("observations are not in the same plane"),
INVALID_PARAMETER_RANGE("invalid parameter {0}: {1} not in range [{2}, {3}]");
INVALID_PARAMETER_RANGE("invalid parameter {0}: {1} not in range [{2}, {3}]"),
PARAMETER_NOT_SET("The parameter {0} should not be null in {1}"),
FUNCTION_NOT_IMPLEMENTED("{0} is not implemented"),
INVALID_TYPE_FOR_FUNCTION("Impossible to execute {0} with {1} set to {2}"),
;
// CHECKSTYLE: resume JavadocVariable check
......
/* Copyright 2002-2020 CS Group
* Licensed to CS Group (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.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.forces.maneuvers.propulsion.AbstractConstantThrustPropulsionModel;
import org.orekit.forces.maneuvers.propulsion.BasicConstantThrustPropulsionModel;
import org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers;
import org.orekit.propagation.events.AbstractDetector;
import org.orekit.propagation.events.EventDetector;
/**
* This class implements a configurable low thrust maneuver.
* <p>The maneuver is composed of succession of a burn interval.
* Burn intervals are defined by two detectors.
* See {@link org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers EventBasedManeuverTriggers}
* for more details on the detectors.
* The attitude and the thrust direction are provided by an instance of ThrustDirectionProvider
* See {@link org.orekit.forces.maneuvers.ThrustDirectionProvider ThrustDirectionProvider}
* for more details on thrust direction and attitude.
* @author Mikael Fillastre
* @author Andrea Fiorentino
*/
public class ConfigurableLowThrustManeuver extends Maneuver {
/**
* to be used for ParameterDriver to make thrust non constant
*/
public static String THRUST_MODEL_IDENTIFIER = "ConfigurableLowThrustManeuver";
/** thrust direction and spaceraft attitude provided by an external object */
private final ThrustDirectionProvider thrustDirectionProvider;
/** Constructor
* See {@link org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers EventBasedManeuverTriggers}
* for requirements on detectors
* @param thrustDirectionProvider thrust direction and attitude provider
* @param startFiringDetector detector to start thrusting (start when increasing)
* @param stopFiringDetector detector to stop thrusting (stop when increasing)
* @param thrust the thrust force (N)
* @param isp engine specific impulse (s)
*/
public ConfigurableLowThrustManeuver (final ThrustDirectionProvider thrustDirectionProvider,
final AbstractDetector<? extends EventDetector> startFiringDetector,
final AbstractDetector<? extends EventDetector> stopFiringDetector, final double thrust,
final double isp) {
super(thrustDirectionProvider.getManeuverAttitudeProvider(),
new EventBasedManeuverTriggers(startFiringDetector, stopFiringDetector),
buildBasicConstantThrustPropulsionModel(thrust, isp,
thrustDirectionProvider.getFixedDirection()));
this.thrustDirectionProvider = thrustDirectionProvider;
}
private static BasicConstantThrustPropulsionModel buildBasicConstantThrustPropulsionModel(
final double thrust, final double isp, final Vector3D fixedDirectionForAttitude) {
return new BasicConstantThrustPropulsionModel(thrust, isp, fixedDirectionForAttitude,
THRUST_MODEL_IDENTIFIER);
}
public ThrustDirectionProvider getThrustDirectionProvider() {
return thrustDirectionProvider;
}
/**
* Get the thrust vector (N) in S/C frame.
*
* @return thrust vector (N) in S/C frame.
*/
public Vector3D getThrustVector() {
return ((AbstractConstantThrustPropulsionModel) (getPropulsionModel())).getThrustVector();
}
/**
* Get the thrust.
*
* @return thrust force (N).
*/
public double getThrust() {
return getThrustVector().getNorm();
}
/**
* Get the specific impulse.
*
* @return specific impulse (s).
*/
public double getISP() {
return ((AbstractConstantThrustPropulsionModel) (getPropulsionModel())).getIsp();
}
}
/* Copyright 2002-2020 CS Group
* Licensed to CS Group (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.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.frames.Frame;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.PVCoordinatesProvider;
/**
* Simple implementation of VariableThrustDirectionVector, providing a constant direction
* @author Mikael Fillastre
* @author Andrea Fiorentino
*/
public class ConstantThrustDirectionVector implements VariableThrustDirectionVector {
private final Vector3D direction;
public ConstantThrustDirectionVector (final Vector3D constantDirection) {
direction = constantDirection;
}
@Override
public Vector3D computeThrustDirection(PVCoordinatesProvider pvProv, AbsoluteDate date,
Frame frame) {
return direction;
}
}
/* Copyright 2002-2020 CS Group
* Licensed to CS Group (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.hipparchus.RealFieldElement;
import org.hipparchus.geometry.euclidean.threed.Rotation;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.attitudes.Attitude;
import org.orekit.attitudes.AttitudeProvider;
import org.orekit.attitudes.FieldAttitude;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.frames.Frame;
import org.orekit.frames.LOFType;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
import org.orekit.utils.FieldPVCoordinatesProvider;
import org.orekit.utils.PVCoordinatesProvider;
/** This class is used to both manage the attitude of the satellite and the direction of thrust.
* @author Mikael Fillastre
* @author Andrea Fiorentino
*/
public class ThrustDirectionProvider implements AttitudeProvider {
/** types, see builders for details */
private enum ThrustDirectionProviderType {
SATELLITE_ATTITUDE,
CUSTOM_ATTITUDE,
DIRECTION_IN_LOF,
DIRECTION_IN_FRAME
}
private final ThrustDirectionProviderType type;
/** external attitude provider, for CUSTOM_ATTITUDE type. Set to null otherwise. */
private final AttitudeProvider attitudeProvider;
/** direction provider, for DIRECTION_IN_LOF and DIRECTION_IN_FRAME types. Set to null otherwise. */
private final VariableThrustDirectionVector variableDirectionInFrame;
/** thruster axis in satellite frame */
private final Vector3D fixedDirection;
/** reference frame for thrust direction, for DIRECTION_IN_FRAME type. Set to null otherwise. */
private final Frame thrustDirectionFrame;
/** Local Orbital Frame type, for DIRECTION_IN_LOF type. Set to null otherwise. */
private final LOFType thrustDirectionLofType;
private static void checkParameterNotNull(Object parameter, String name,
ThrustDirectionProviderType type) {
if (parameter == null) {
throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET,
name, "ThrustDirectionProvider-" + type.toString());
}
}
private ThrustDirectionProvider (final ThrustDirectionProviderType type,
final AttitudeProvider attitudeProvider,
final VariableThrustDirectionVector variableDirectionInFrame, Vector3D fixedDirection,
final Frame frame, final LOFType thrustDirectionLofType) {
this.type = type;
this.attitudeProvider = attitudeProvider;
this.variableDirectionInFrame = variableDirectionInFrame;
this.thrustDirectionFrame = frame;
this.thrustDirectionLofType = thrustDirectionLofType;
this.fixedDirection = fixedDirection;
}
/**
* Build a ThrustDirectionProvider from a fixed direction in the satellite frame
* the satellite attitude won't be managed by this object
* @param direction constant direction in the satellite frame
* @return a new instance
*/
public static ThrustDirectionProvider buildFromFixedDirectionInSatelliteFrame(
final Vector3D direction) {
ThrustDirectionProvider obj = new ThrustDirectionProvider(
ThrustDirectionProviderType.SATELLITE_ATTITUDE, null, null, direction, null, null);
checkParameterNotNull(direction, "fixedDirection", obj.type);
return obj;
}
/**
* Build a ThrustDirectionProvider where the attitude is provided by an external object
* the direction of thrust will be constant
* @param attitudeProvider the object that provide the satellite attitude
* @param direction thruster axis in satellite frame
* @return a new instance
*/
public static ThrustDirectionProvider buildFromCustomAttitude(
final AttitudeProvider attitudeProvider, final Vector3D direction) {
ThrustDirectionProvider obj = new ThrustDirectionProvider(
ThrustDirectionProviderType.CUSTOM_ATTITUDE, attitudeProvider, null, direction,
null, null);
checkParameterNotNull(attitudeProvider, "attitudeProvider", obj.type);
checkParameterNotNull(direction, "direction", obj.type);
return obj;
}
/**
* Build a ThrustDirectionProvider by a variable direction in a custom frame
* @param thrustDirectionFrame reference frame for thrust direction
* @param variableDirectionInFrame the object providing the thrust direction
* @param thrusterAxisInSatelliteFrame thruster axis in satellite frame
* @return a new instance
*/
public static ThrustDirectionProvider buildFromDirectionInFrame(
final Frame thrustDirectionFrame,
final VariableThrustDirectionVector variableDirectionInFrame,
final Vector3D thrusterAxisInSatelliteFrame) {
ThrustDirectionProvider obj = new ThrustDirectionProvider(
ThrustDirectionProviderType.DIRECTION_IN_FRAME, null, variableDirectionInFrame,
thrusterAxisInSatelliteFrame, thrustDirectionFrame, null);
checkParameterNotNull(variableDirectionInFrame, "variableDirectionInFrame", obj.type);
checkParameterNotNull(thrustDirectionFrame, "thrustDirectionFrame", obj.type);
return obj;
}
/**
* Build a ThrustDirectionProvider by a variable direction in a Local Orbital Frame
* @param thrustDirectionLofType Local Orbital Frame type
* @param variableDirectionInFrame the object providing the thrust direction
* @param thrusterAxisInSatelliteFrame thruster axis in satellite frame
* @return a new instance
*/
public static ThrustDirectionProvider buildFromDirectionInLOF(
final LOFType thrustDirectionLofType,
final VariableThrustDirectionVector variableDirectionInFrame,
final Vector3D thrusterAxisInSatelliteFrame) {
ThrustDirectionProvider obj = new ThrustDirectionProvider(
ThrustDirectionProviderType.DIRECTION_IN_LOF, null, variableDirectionInFrame,
thrusterAxisInSatelliteFrame, null, thrustDirectionLofType);
checkParameterNotNull(variableDirectionInFrame, "variableDirectionInFrame", obj.type);
checkParameterNotNull(thrustDirectionLofType, "thrustDirectionLofType", obj.type);
return obj;
}
public Vector3D getFixedDirection() {
return fixedDirection;
}
@Override
public Attitude getAttitude(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) {
switch (type) {
case CUSTOM_ATTITUDE:
return attitudeProvider.getAttitude(pvProv, date, frame);
case DIRECTION_IN_FRAME:
case DIRECTION_IN_LOF:
return getAttitudeFromFrame(pvProv, date, frame);
default:
throw new OrekitException(OrekitMessages.INVALID_TYPE_FOR_FUNCTION, "ThrustDirectionProvider.getAttitude", "type", type.toString());
}
}
@Override
public <T extends RealFieldElement<T>> FieldAttitude<T> getAttitude(
FieldPVCoordinatesProvider<T> pvProv, FieldAbsoluteDate<T> date, Frame frame) {
throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED, "ThrustDirectionProvider with RealFieldElement");
}
public Attitude getAttitudeFromFrame(PVCoordinatesProvider pvProv, AbsoluteDate date,
Frame frame) {
Rotation inertial2htrusterFrame;
if (type.equals(ThrustDirectionProviderType.DIRECTION_IN_FRAME)) {
inertial2htrusterFrame = thrustDirectionFrame.getTransformTo(frame, date).getRotation();
} else { // LOF
inertial2htrusterFrame = thrustDirectionLofType
.rotationFromInertial(pvProv.getPVCoordinates(date, frame));
}
Vector3D thrustDirection = variableDirectionInFrame.computeThrustDirection(pvProv, date,
frame);
Vector3D thrustDirectionInertial = inertial2htrusterFrame.applyInverseTo(thrustDirection);
Rotation attitude = new Rotation(getFixedDirection(), thrustDirectionInertial);
Attitude att = new Attitude(date, frame, attitude.revert(), Vector3D.ZERO, Vector3D.ZERO);
return att;
}
public AttitudeProvider getManeuverAttitudeProvider() {
AttitudeProvider attitudeProvider = null;
if (type != ThrustDirectionProviderType.SATELLITE_ATTITUDE) {
attitudeProvider = this;
} // else default behaviour
return attitudeProvider;
}
}
// ///////////////////////////////////////////////////
// Copyright Exotrail (c), 2019
// ///////////////////////////////////////////////////
package org.orekit.forces.maneuvers;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.frames.Frame;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.PVCoordinatesProvider;
/** Interface to compute the thrust direction of a maneuver
* @author Mikael Fillastre
* @author Andrea Fiorentino
*/
public interface VariableThrustDirectionVector {
/** Compute the thrust direction corresponding to an orbital state.
* @param pvProv local position-velocity provider around current date
* @param date current date
* @param frame reference frame from which attitude is computed
* @return direction thrust direction at the specified date and position-velocity state
*/
public Vector3D computeThrustDirection(PVCoordinatesProvider pvProv, AbsoluteDate date,
Frame frame);
}
/* Copyright 2002-2020 CS Group
* Licensed to CS Group (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.trigger;
import java.util.stream.Stream;
import org.hipparchus.Field;
import org.hipparchus.RealFieldElement;
import org.hipparchus.ode.events.Action;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.AbstractDetector;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.FieldEventDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
/** Maneuver triggers based on start and stop detectors.
* This allow a succession of burn interval.
* The thruster starts firing when the start detector becomes positive.
* The thruster stops firing when the stop detector becomes positive.
* The 2 detectors should not be positive at the same time.
* They can be both negative
* @author Mikael Fillastre
* @author Andrea Fiorentino
*/
public class EventBasedManeuverTriggers implements ManeuverTriggers, EventHandler<EventDetector> {
/** detectors to start firing, only detect increasing sign change */
private final AbstractDetector<? extends EventDetector> startFiringDetector;
/** detectors to stop firing, only detect increasing sign change.
* e.g. it can be a negate detector of the start detector*/
private final AbstractDetector<? extends EventDetector> stopFiringDetector;
/** flag for init method, called several times : force models + each detector */
private boolean initialized;
/** Triggered date of engine start. */
private AbsoluteDate triggeredStart;
/** Triggered date of engine stop. */
private AbsoluteDate triggeredEnd;
/** Propagation direction. */
private boolean forward;
public EventBasedManeuverTriggers (
final AbstractDetector<? extends EventDetector> startFiringDetector,
final AbstractDetector<? extends EventDetector> stopFiringDetector) {
this.startFiringDetector = startFiringDetector.withHandler(this);
this.stopFiringDetector = stopFiringDetector.withHandler(this);
this.triggeredStart = null;
this.triggeredEnd = null;
initialized = false;
forward = true; // because init not called from DSST
}
public AbstractDetector<? extends EventDetector> getStartFiringDetector() {
return startFiringDetector;
}
public AbstractDetector<? extends EventDetector> getStopFiringDetector() {
return stopFiringDetector;
}
/** {@inheritDoc} */
@Override
public void init(final SpacecraftState initialState, final AbsoluteDate target) {
if (!initialized) {
initialized = true;
if (stopFiringDetector == null) {
throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "stopFiringDetector", "EventBasedManeuverTriggers");
}
if (startFiringDetector == null) {
throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "startFiringDetector", "EventBasedManeuverTriggers");
}
final AbsoluteDate sDate = initialState.getDate();
this.forward = sDate.compareTo(target) < 0;
checkInitialFiringState(initialState);
} // multiples calls to init : because it is a force model and by each detector
}
/**
* can be overloaded by sub classes
*
* @param initialState
* @param target
*/
protected void checkInitialFiringState(final SpacecraftState initialState) {
if (isFiringOnInitialState(initialState)) {
setFiring(true, initialState.getDate());
}
}
/**
* can be called by sub classes
*
* @param initialState
* @param target
*/
protected boolean isFiringOnInitialState(final SpacecraftState initialState) {
// set the initial value of firing
double insideThrustArcG = startFiringDetector.g(initialState);
boolean isInsideThrustArc = false;
if (insideThrustArcG == 0) {
// bound of arc
// check state for the next second (which can be forward or backward)
double nextSecond = isForward() ? 1 : -1;
double nextValue = startFiringDetector.g(initialState.shiftedBy(nextSecond));
isInsideThrustArc = nextValue > 0;
} else {
isInsideThrustArc = insideThrustArcG > 0;
}
return isInsideThrustArc;
}
/** {@inheritDoc} */
@Override
public Stream<EventDetector> getEventsDetectors() {
return Stream.of(startFiringDetector, stopFiringDetector);
}
/** {@inheritDoc} */
@Override
public <T extends RealFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(
final Field<T> field) {
// not implemented, it depends on the input detectors
throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED, "EventBasedManeuverTriggers.getFieldEventsDetectors");
}
public void setFiring(boolean firing, AbsoluteDate date) {
if (firing != isFiring(date)) {
if (isForward()) {
if (firing) {
if (!date.equals(triggeredEnd)) {
triggeredStart = date;
} // else no gap between stop and start, can not handle correctly : skip it
} else {
triggeredEnd = date;
}
} else { // backward propagation
if (firing) { // start firing by end date
if (!date.equals(triggeredStart)) {
triggeredEnd = date;
} // else no gap between stop and start, can not handle correctly : skip it
} else {
triggeredStart = date;
}