Click or drag to resize

Segments

SegmentDefinitions are the fundamental type in the Segment Propagation Library. This topic explains what segments are, how they work, and other basic concepts. It also includes descriptions and examples for each type of segment provided by the library.

Note Note

The functionality described in this topic requires a license for the Segment Propagation Library.

Segments

Traditionally, propagation in STK Desktop usually involved setting up an object's initial conditions and then propagating until some time limit is hit. Although such tools and libraries are very useful, they may be too simple to model a vehicle's whole trajectory. There was no way to handle cases when the way the object is propagated needs to change.

For example, consider what it would take to propagate a satellite through an impulsive maneuver. There is a lot of plumbing and busy work you would need to do. First, you would propagate starting from your initial conditions, then determine the time of maneuver, which is the time at which you stop your initial propagation. Then you would take that final state, apply the maneuver, then propagate forward from that new state. Finally, you would have to manually stitch together the ephemeris, being careful about the discontinuity at the time of the maneuver.

Automating that kind of trajectory design is exactly what the Segment Propagation Library is for. You would first define a NumericalPropagatorSegment with a NumericalPropagatorDefinition and a StoppingCondition, then an ImpulsiveManeuverSegment with your delta-v and other maneuver information, and finally a second NumericalPropagatorSegment stopping at your desired event. Finally, you put them all into a SegmentList, propagate, and you get your final ephemeris out.

Note Note

Although the examples given in the documentation are for spacecraft, most of the segments can be used for any kind of propagation. See the Aircraft Propagation topic for information on propagating aircraft using the same concepts.

Types of Segments

The segment types included range from general base types to controlling the flow of segments to ones specific for satellite propagation forward or backward in time. If some part of your trajectory cannot be represented as one of these segments then you can extend the library to add to the capability yourself, or contact AGI to request the additional capability.

Each of these segment types are described in more detail in the Individual Segments section below.

The Segment Life Cycle

Segments follow the Create-Set-Call pattern like most of the rest of DME Component Libraries. After determining how to split your desired trajectory into segments, instantiate and configure each segment. Generally you will construct an overall SegmentList by adding each segment to the Segments (get) list. Then, call getSegmentListPropagator on the overall SegmentList, which returns a SegmentPropagator. When you call propagate, all individual segments added to the list will be propagated in order. With a few exceptions, the final state of each segment will be used as the initial state of the next segment (except for the NumericalInitialStateSegment which defines its own initial state. FollowSegment can also be configured to ignore the previous segment's final state). Once propagation is finished, a SegmentResults is returned. When propagating a SegmentList, a SegmentListResults will be returned. The ephemeris for each segment can be accessed by calling getDateMotionCollectionOfOverallTrajectory, and other information can be interrogated as well. In addition, the SegmentResults created by some segments (SegmentList, TargetedSegmentList, and NumericalPropagatorSegment) can be cast to a more specific type deriving from SegmentResults, as shown in later examples.

In order to change the configuration of your segments during propagation, such as to solve for a constrained trajectory or to enable or disable some elements, use a TargetedSegmentList instead of a SegmentList. TargetedSegmentLists, in addition to a list of segments, have a list of TargetedSegmentListOperators that will alter the configuration of the segments in a defined way.

One of the most common TargetedSegmentListOperators is the TargetedSegmentListDifferentialCorrector. This operator works by treating the computed trajectory as a multivariable function. Each segment that can be varied by the function solver has methods to aid in the creation of a SegmentPropagatorVariable to feed to the targeted segment list's differential corrector. Or, you can use a ParameterizedScalarVariable to control wrapped geometry types in the nested segments. Once the corrector is added to the TargetedSegmentList and everything is configured, propagation can commence just like the normal SegmentList. Function solvers will compute the trajectory, vary the variables, and recompute, iterating over and over again towards a goal.

Segment Adapters

Most segments require that you specify the identifications of the elements getting propagated and what ReferenceFrame, Axes, or object that the propagated values will be defined in, and a StateElementAdapterDefinition. When the SegmentPropagators are configured, the segment will take the previous segment's defined-in object (ReferenceFrame, Axes...) for each of that segment's elements. Having retrieved those values, the current segment will create an adapter that will perform the transformation of the final value of the previous segment at propagation time. These adapters are preserved in the SegmentResults as well.

All in all, this means that your segments do not all need to propagate in the same frame. If you are modeling a trajectory from the Earth to the Moon, you can break it up into two segments; the first in the Earth's inertial frame, and the second in the Moon's inertial frame. When you get the results for these two segments, the ephemeris for each individual segment will be in whatever frame you defined it in, but when you call getDateMotionCollectionOfOverallTrajectory, you can specify the defined-in object to get the entire ephemeris in the desired frame or axes.

Note that many segments will handle the adapter configuration automatically. NumericalPropagatorSegment, for example, knows how to create adapters for all built in PropagationStateElements and AuxiliaryStateElements. SegmentList leverages its nested segments to create the adapters. Also, some segments that are used for control flow (such as ReturnSegment and StopSegment) are designed to use the previous segment's defined-in objects, since they themselves do not modify or propagate a state.

Individual Segments

InitialStateSegment

InitialStateSegment<T> is a segment that will have in its results a single state initialized from the InitialState (get / set) property. You will need to configure the adapters on this segment by calling setElementAndAdapter. It should only be used as the first segment to get propagated that isn't a SegmentList. Otherwise there will be a discontinuity, as the final state of the previous segment will be ignored by this segment.

Any InitialStateSegment<T> should be the first segment to get propagated (that is not a SegmentList or TargetedSegmentList). However, this segment is not required as long as one of the following is true:

This segment is the only segment that allows for the values in the initial state to be modified by a TargetedSegmentListOperator. See the Multivariable Function Solvers topic for more information.

The following code sample demonstrates how to configure a simple InitialStateSegment<T>:

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();

SegmentList overallList = new SegmentList();

// create our initial state segment
InitialStateSegment<BasicState> initialStateSegment = new InitialStateSegment<BasicState>();

// define the state
BasicState initialState = new BasicState();
initialState.addStateElementMotion(SatelliteMotionIdentification,
                                   new Motion1<Cartesian>(new Cartesian(8000000.0, 0.0, 0.0), 
                                                          new Cartesian(0.0, 5500.0, 5500.0)));
initialState.setCurrentDate(TimeConstants.J2000);

initialStateSegment.setInitialState(initialState);

// set the adapter and ReferenceFrame for this segment
initialStateSegment.setElementAndAdapter(new ReferenceFrameAdapter(SatelliteMotionIdentification, earth.getInertialFrame()));

// create some propagate segment
PropagateSegment propagateSegment = createAndConfigurePropagateSegment();

// compose the segments together
overallList.getSegments().add(initialStateSegment);
overallList.getSegments().add(propagateSegment);

// propagate
SegmentListPropagator propagator = overallList.getSegmentListPropagator(new EvaluatorGroup(), null);

SegmentListResults results = propagator.propagateSegmentList();
BasicState firstPropagatedState = (BasicState) results.getEntireComputedEphemeris().get(0);
// firstPropagateState will be the same as initialState

BasicLaunchSegment

BasicLaunchSegment is a segment that produces ephemeris in the configured CentralBody's (get / set) fixed frame. The underlying physical model of this segment uses an ellipse approximating the trajectory in the central body's fixed frame from the launch position to the burnout position. This trajectory is then sampled for output via simple interpolation. The segment begins at the configured launch position and epoch. The launch position may be augmented with an optional initial acceleration. The segment ends at a configured burnout epoch (launch epoch plus time of flight) with the burnout position specified in the same frame as the launch position. The burnout velocity may be specified in either the fixed frame with a specified velocity magnitude, or in the inertial frame with a specified velocity magnitude, flight path angle, and azimuthal angle. In the fixed frame mode the flight path angle is zero and the inclination of the final state is determined by the arc between the launch and burnout states.

See BasicLaunchSegment for the complete set of options.

Some important remarks regarding this segment:

  • The state element adapter must be set.

  • The positions for both launch and burnout by default assume the CentralBody (get / set) fixed frame. Both positions must be set in the same frame.

  • It is important to note that the burnout velocity can be specified in either fixed or inertial frames, irrespective of the frame used for the positions.

  • If the input states have information for orders greater than 0, this information is ignored.

  • This segment may produce warnings indicating potential issues when attempting to create an ellipse for interpolation. See the documentation for BasicLaunchSegmentResults for the types of warnings and their meanings.

The following code sample demonstrates how to configure a BasicLaunchSegment:

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();

// Launch and burnout points. Higher order terms in the motion are ignored, and may be optionally computed by the propagator.
Cartesian launchLocation = earth.getShape().cartographicToCartesian(new Cartographic(Trig.degreesToRadians(1.5), Trig.degreesToRadians(7.0), 0.0));
Motion1<Cartesian> launchMotion = new Motion1<Cartesian>(launchLocation);
Cartesian burnoutLocation = earth.getShape().cartographicToCartesian(new Cartographic(Trig.degreesToRadians(89.0), Trig.degreesToRadians(47.0), 3000000.0));
Motion1<Cartesian> burnoutMotion = new Motion1<Cartesian>(burnoutLocation);
// Motion ID
String motionElementID = "LaunchMotion";

// Segment definition.
BasicLaunchSegment basicLaunchSegment = new BasicLaunchSegment(Duration.fromSeconds(60.0)); // 60 second step size.
basicLaunchSegment.setElementAndAdapter(new ReferenceFrameAdapter(motionElementID, earth.getFixedFrame()));  // The input positions are in the Earth fixed frame.
basicLaunchSegment.setCentralBody(earth);

// Launch state.
BasicState launchState = new BasicState();
launchState.addStateElementMotion(motionElementID, launchMotion);
launchState.setCurrentDate(new JulianDate(new GregorianDate(2017, 10, 3, 0, 0, 0.0)));
basicLaunchSegment.setLaunchState(launchState);
basicLaunchSegment.setMotionID(motionElementID);

// Burnout state.
BasicState burnoutState = new BasicState();
burnoutState.addStateElementMotion(motionElementID, burnoutMotion);
burnoutState.setCurrentDate(launchState.getCurrentDate().addMinutes(10.0)); // 600 seconds is the time of flight.
basicLaunchSegment.setBurnoutState(burnoutState);
basicLaunchSegment.setBurnoutVelocityFrame(SimpleAscentPropagatorBurnoutVelocityFrame.FIXED_FRAME);
basicLaunchSegment.setBurnoutVelocityMagnitude(ValueDefinition.toValueDefinition(7299.76)); // In SI units in the Earth's fixed frame.

SegmentPropagator launchPropagator = basicLaunchSegment.getSegmentPropagator();
SegmentResults results = launchPropagator.propagate();

// Cast the results to check the warnings property.
BasicLaunchSegmentResults resultsWithWarningsProperty = (results instanceof BasicLaunchSegmentResults) ? (BasicLaunchSegmentResults) results : null;

NumericalInitialStateSegment

NumericalInitialStateSegment is an InitialStateSegment<T>: that will initialize its InitialState (get / set) property from a NumericalPropagatorDefinition. Its adapters will be configured automatically for this segment.

The following code sample demonstrates how to configure a simple NumericalInitialStateSegment:

Java
SegmentList overallList = new SegmentList();

// create the segment
NumericalInitialStateSegment initialStateSegment = new NumericalInitialStateSegment();
initialStateSegment.setName("Initial_State_Segment");

// for the NumericalInitialStateSegment, the initial state will be configured 
// with the initial values stored in a NumericalPropagatorDefinition
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(SatelliteMotionIdentification);
NumericalPropagatorDefinition numericalPropagator = createNumericalPropagator(propagationPoint);
initialStateSegment.setPropagatorDefinition(numericalPropagator);
// adapters are configured automatically

// use the same NumericalPropagatorDefinition when configuring the propagate segment
NumericalPropagatorSegment propagationSegment = configureNumericalPropagatorSegment(numericalPropagator, propagationPoint);

overallList.getSegments().add(initialStateSegment);
overallList.getSegments().add(propagationSegment);

// get the propagator and propagate
EvaluatorGroup group = new EvaluatorGroup();
SegmentPropagator segmentPropagator = overallList.getSegmentPropagator(group);

SegmentResults propagationResults = segmentPropagator.propagate();

PropagateSegment

This segment will take a StoppablePropagator, and propagate it forward or backward in time, stopping when one of its StoppingConditions (get) is satisfied. The SegmentResults returned will be an instance of PropagateSegmentResults, and will include information regarding how propagation was stopped. You will need to configure the adapters on this segment by calling setElementAndAdapter, however, some derived types will configure the elements and adapters automatically.

Normally this segment will propagate from the previous segment's final state. However, when there is no such segment before this one providing a final state, it will start from the initial state returned from getInitialState. However, if that initial state is null, and no initial state passed into the propagate method, an exception will be thrown.

The thresholds of the StoppingConditions can be modified by a TargetedSegmentListOperator.

The following code sample demonstrates how to create a PropagateSegment with a StoppingCondition:

Java
// general settings
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();
Motion1<Cartesian> initialConditions = new Motion1<Cartesian>(new Cartesian(8000000.0, 0.0, 0.0), 
                                                              new Cartesian(1500.0, 8500.0, 0.0));
JulianDate initialDate = TimeConstants.J2000;

// configure the wrapped stoppable propagator
TwoBodyStoppablePropagator twoBodyPropagator = new TwoBodyStoppablePropagator();
twoBodyPropagator.setGravitationalParameter(WorldGeodeticSystem1984.GravitationalParameter);
twoBodyPropagator.setPropagationFrame(earth.getInertialFrame());
twoBodyPropagator.setStep(Duration.fromMinutes(5.0));
twoBodyPropagator.setPropagationPointIdentification(SatelliteMotionIdentification);
twoBodyPropagator.setInitialDate(initialDate);
twoBodyPropagator.setInitialMotion(initialConditions);

// make the segment
PropagateSegment propSegment = new PropagateSegment(twoBodyPropagator);

// add a stopping condition
propSegment.getStoppingConditions().add(new DurationStoppingCondition(Duration.fromHours(1.0)));

// configure the element and adapter on the propagator
propSegment.setElementAndAdapter(new ReferenceFrameAdapter(SatelliteMotionIdentification, 
                                                           earth.getInertialFrame()));

// get our propagator and propagate
SegmentPropagator propagator = propSegment.getSegmentPropagator();
PropagateSegmentResults results = (PropagateSegmentResults) propagator.propagate();

NumericalPropagatorSegment

This segment will take a NumericalPropagatorDefinition, and propagate it forward or backward in time, stopping when one of its StoppingConditions (get) is satisfied. The adapters on this segment will be configured automatically.

Normally this segment will propagate from the previous segment's final state. However, when there is no such segment before this one providing a final state, it will start from the initial condition of its configured PropagatorDefinition (get / set).

The following code sample demonstrates how to create a NumericalPropagatorSegment with two StoppingConditions:

Java
// create the segment
NumericalPropagatorSegment propagatorSegment = new NumericalPropagatorSegment();
propagatorSegment.setName("Propagator_Segment");

// create and add the NumericalPropagatorDefinition
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(SatelliteMotionIdentification);
NumericalPropagatorDefinition numericalPropagator = createNumericalPropagator(propagationPoint);
propagatorSegment.setPropagatorDefinition(numericalPropagator);

// create and add a stopping condition
DurationStoppingCondition durationStop = new DurationStoppingCondition(Duration.fromDays(10.0));
durationStop.setName("Duration_of_10days_Stopping_Condition");
propagatorSegment.getStoppingConditions().add(durationStop);

// create and add a second stopping condition
ScalarStoppingCondition altitudeStoppingCondition = 
        new ScalarStoppingCondition(new ScalarCartographicElement(earth,
                                                                  propagationPoint.getIntegrationPoint(), 
                                                                  CartographicElement.HEIGHT),
                                    500000.0, // 500 km desired value
                                    0.01, // 1 cm tolerance 
                                    StopType.ANY_THRESHOLD);
altitudeStoppingCondition.setName("Altitude_Of_500km_Stopping_Condition");
propagatorSegment.getStoppingConditions().add(altitudeStoppingCondition);

// get the propagator and propagate
SegmentPropagator numericalSegmentPropagator = propagatorSegment.getSegmentPropagator();
PropagateSegmentResults propagationResults = (PropagateSegmentResults) numericalSegmentPropagator.propagate();

HoldSegment

This segment will create a ConstantStateStoppablePropagator, and propagate it forward or backward in time, stopping when one of its StoppingConditions (get) is satisfied. You can configure the adapters on this segment by calling setElementAndAdapter, to change the ReferenceFrame, Axes, or defined-in object of an element from what it was in the previous segment. However, if the state elements from the previous segment are already in the desired frame or axes, no adapter is needed.

This segment does not provide an initial state. It will throw an exception if no state is passed to the propagate method.

The following code sample demonstrates how to create a HoldSegment:

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();
PropagateSegment firstSegment = createAndConfigurePropagateSegment();

// initially propagating in the inertial frame
firstSegment.setElementAndAdapter(new ReferenceFrameAdapter(SatelliteMotionIdentification, earth.getInertialFrame()));

// stop when our range to our target is 0
ScalarStoppingCondition rangeOfZeroStoppingCondition = createRangeOfZeroStoppingCondition();
firstSegment.getStoppingConditions().add(rangeOfZeroStoppingCondition);

// hold our fixed position for 2 days
HoldSegment holdSegment = new HoldSegment();
holdSegment.setStep(Duration.fromMinutes(3.0));
holdSegment.getStoppingConditions().add(new DurationStoppingCondition(Duration.fromDays(2.0)));
holdSegment.setElementAndAdapter(new ReferenceFrameAdapter(SatelliteMotionIdentification, earth.getFixedFrame()));

// configure the list and propagate
SegmentList list = new SegmentList();
list.getSegments().add(firstSegment);
list.getSegments().add(holdSegment);

SegmentListPropagator propagator = list.getSegmentListPropagator();

FollowSegment

With this segment, the initial and final state of propagation can be configured beyond the default behavior of the segments. This segment will take a StoppablePropagator, and then evaluate the segment's StartOfFollowSegment (get / set) and EndOfFollowSegment (get / set), returning the propagated results in between. You will need to configure the adapters on this segment by calling setElementAndAdapter. This segment can be useful if you have an ephemeris extended into the future and you want to perform a maneuver, but you don't know when.

Since the initial state of the segment may not use the final state of the previous segment, there can be a discontinuity in the results if this is not the first ephemeris producing segment.

The following code sample demonstrates how to create a FollowSegment:

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();
String covarianceIdentification = "Covariance";

StkEphemerisFile ephemerisFile = getEphemerisFile(); // get our ephemeris file

// configure our state with the ephemeris file data
GeometryDynamicState dynamicState = new GeometryDynamicState();
Point ephemerisLocationPoint = ephemerisFile.createPoint();
Covariance3By3DynamicMatrix covariance = new Covariance3By3DynamicMatrix(ephemerisFile.createCovarianceInterpolator());

dynamicState.setElement(SatelliteMotionIdentification, ephemerisLocationPoint);
dynamicState.setElement(covarianceIdentification, covariance);

// create the wrapped stoppable propagator
DynamicStateStoppablePropagator stoppablePropagator = new DynamicStateStoppablePropagator();
stoppablePropagator.setDynamicState(dynamicState);

// create our segment
FollowSegment follow = new FollowSegment(stoppablePropagator);
// set our adapters
follow.setElementAndAdapter(new ReferenceFrameAdapter(SatelliteMotionIdentification, earth.getInertialFrame()));
follow.setElementAndAdapter(new SimpleAdapter<Matrix>(covarianceIdentification));

// create our stopping condition
DurationStoppingCondition stoppingCondition = new DurationStoppingCondition();
// we are going to run in a differential corrector where we control our stopping condition threshold
ParameterizedDurationVariable durationTripVariable = 
        new ParameterizedDurationVariable(Duration.fromMinutes(30), 60.0, 1.0, follow);
stoppingCondition.setThreshold(durationTripVariable.getValue());
follow.getStoppingConditions().add(stoppingCondition);

// we know that we need to spend at least an hour in the parking orbit
follow.setStartOfFollowSegment(
        new FollowSegmentStartsWithStoppingConditions(new DurationStoppingCondition(Duration.fromHours(1.0))));

// let the configured conditions end the propagation
follow.setEndOfFollowSegment(new FollowSegmentEndsAsWithDefaultConditions());

// and after we stop the previous segment, perform a maneuver
ImpulsiveManeuverSegment impulsiveManeuver = new ImpulsiveManeuverSegment();
ImpulsiveManeuverInformation leaveParkingOrbit = 
        new ImpulsiveManeuverInformation(SatelliteMotionIdentification, 
                                         new Cartesian(1000.0, 0.0, 0.0), 
                                         earth.getInertialFrame().getAxes());
impulsiveManeuver.setManeuver(leaveParkingOrbit);

// configure our overall list
TargetedSegmentList segmentList = new TargetedSegmentList();
segmentList.getSegments().add(follow);
segmentList.getSegments().add(impulsiveManeuver);
// add more segments

// configure our differential corrector
TargetedSegmentListDifferentialCorrector differentialCorrector = new TargetedSegmentListDifferentialCorrector();

// the constraint
ScalarAtEndOfSegmentConstraint trueAnomalyConstraint = new ScalarAtEndOfSegmentConstraint();

// we need to use the parameter in the constraint to create our point
ParameterizedOnStatePoint constraintPoint = 
        new ParameterizedOnStatePoint(trueAnomalyConstraint.getParameter(), 
                                      earth.getInertialFrame(), 
                                      SatelliteMotionIdentification);

// with the point, we can create our true anomaly
Scalar trueAnomalyScalarForConstraint = 
        new ScalarModifiedKeplerianElement(WorldGeodeticSystem1984.GravitationalParameter, 
                                           constraintPoint, 
                                           KeplerianElement.TRUE_ANOMALY, 
                                           earth.getInertialFrame());
trueAnomalyConstraint.setScalar(trueAnomalyScalarForConstraint);
trueAnomalyConstraint.setDesiredValue(ValueDefinition.toValueDefinition(Trig.degreesToRadians(180.0)));
trueAnomalyConstraint.setSegment(follow);
trueAnomalyConstraint.getSettings().setTolerance(0.0001);

// finish configuring our differential corrector
differentialCorrector.getConstraints().add(trueAnomalyConstraint);
differentialCorrector.getVariables().add(durationTripVariable);

// add the corrector to our targeted segment list
segmentList.getOperators().add(differentialCorrector);

// propagate
SegmentListPropagator propagator = segmentList.getSegmentListPropagator();
SegmentListResults results = propagator.propagateSegmentList();

ImpulsiveManeuverSegment

ImpulsiveManeuverSegment applies a change to the velocity of one or more Cartesian propagation elements in the state. This will result in a discontinuity in the velocity. Two ephemeris points are returned in its SegmentResults, one before the maneuver and one after the maneuver. The adapters on this segment will be configured automatically.

One concern about satellites performing maneuvers is that they may run out of fuel. By changing the InvalidFuelUseBehavior (get / set) property on the particular maneuver information, you can control what will happen if the vehicle would run out of fuel during the maneuver. You can set it to throw an exception, to stop propagating, to perform only the amount of the maneuver possible, or to do the entire requested maneuver regardless of the fuel state. Regardless of which option is selected, the RanOutOfFuel (get / set) property in the returned results will be set to true.

ImpulsiveManeuverInformation takes in the rocket's exhaust velocity in order to perform the fuel usage calculation. If instead you have the specific impulse of your rocket engine, you can generally calculate the exhaust velocity by multiplying the specific impulse by EarthSurfaceGravity

Every property except for the Orientation (get / set) can be directly modified by a TargetedSegmentListOperator. Note that the Orientation (get / set) can depend on the position and velocity of the spacecraft, but to do so you have to use the PropagationPoint (get) included in the ImpulsiveManeuverInformation.

The following code sample demonstrates how to configure an ImpulsiveManeuverSegment by adding an ImpulsiveManeuverInformation:

Java
// create the segment
ImpulsiveManeuverSegment impulsiveManeuverSegment = new ImpulsiveManeuverSegment();
impulsiveManeuverSegment.setName("Impulsive_Maneuver");


// create the maneuver information for the particular point element getting propagated
ImpulsiveManeuverInformation maneuverInformation = 
        new ImpulsiveManeuverInformation(SatelliteMotionIdentification, 
                                         new Cartesian(103.2, 0.002, 1.02), // delta-v, meters/second
                                         null);
maneuverInformation.setOrientation(new AxesVelocityOrbitNormal(maneuverInformation.getPropagationPoint(), earth));
impulsiveManeuverSegment.setManeuver(maneuverInformation);

// add the impulsive maneuver segment to a segment list (since the ImpulsiveManeuverSegment 
// can't be the first or only segment propagated)
someSegmentList.getSegments().add(impulsiveManeuverSegment);

// get the propagator and propagate
SegmentListPropagator propagator = someSegmentList.getSegmentListPropagator();
SegmentListResults overallResults = propagator.propagateSegmentList();

// get the specific results
SegmentResults temporaryResults = overallResults.getResultsOfSegment(impulsiveManeuverSegment);
ImpulsiveManeuverSegmentResults impulsiveManeuverResults = (ImpulsiveManeuverSegmentResults) temporaryResults;

FiniteManeuverSegment

There are many times when an impulsive maneuver is not realistic enough. If you need an accurate simulation of a burn, or you want to model a low-thrust satellite, you will need to model them as finite maneuvers.

In order to accomplish this higher fidelity modeling, the Segment Propagation Library includes ContinuousThrustForceModel which can be used in conjunction with PropagationNewtonianPoint to model a thrust. This force model takes the thrust vector and adds it to the list of forces applied to the propagation point. You can configure it as follows:

Java
// CreatePropagatedNewtonianPoint also creates fuel and dry mass elements
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(SatelliteMotionIdentification);
NumericalPropagatorDefinition basePropagator = createNumericalPropagator(propagationPoint);
PropagationScalar fuelMassScalar = new PropagationScalar(500.0);
fuelMassScalar.setIdentification("Fuel Mass");
AuxiliaryStateScalar dryMass = new AuxiliaryStateScalar(Scalar.toScalar(500.0));
dryMass.setIdentification("Dry Mass");
propagationPoint.setMass(Scalar.add(fuelMassScalar.getIntegrationValue(), dryMass.getAuxiliaryScalar()));
basePropagator.getIntegrationElements().add(fuelMassScalar);
basePropagator.getAuxiliaryElements().add(dryMass);

double nominalFuelFlow = -0.02; // kg/sec, negative because fuel will be consumed
ScalarFixed fuelFlowRateScalar = new ScalarFixed(nominalFuelFlow);
fuelMassScalar.setScalarDerivative(fuelFlowRateScalar);

double isp = 250.0; // seconds
Cartesian thrustDirection = new Cartesian(0.0, 0.0, 1.0);   // unit vector
Vector thrustVector = ContinuousThrustForceModel.createIspThrustVector(
        Scalar.toScalar(isp), 
        fuelFlowRateScalar, 
        new AxesVelocityOrbitNormal(propagationPoint.getIntegrationPoint()),
        thrustDirection);
ContinuousThrustForceModel thrustForce = new ContinuousThrustForceModel(thrustVector, propagationPoint.getIntegrationFrame().getAxes());
propagationPoint.getAppliedForces().add(thrustForce);

Let's break this code down a little bit. First, we define our propagator and our PropagationNewtonianPoint. With those created, we configure our dry mass and fuel mass. After configuring the preceding items we create our thrust Vector using a helper method on ContinuousThrustForceModel. This helper method constructs a thrust vector based on the Tsiolkovsky rocket equation (it is easy to use any vector you desire). Finally, we create our force model, assign the vector, and add it to our point.

That is most of the work needed for successful propagation with a FiniteManeuverSegment. Now let's go over a more complete example of usage. In order for us to get a working example of this segment, we will first instantiate the setup segment, then create and apply stopping conditions, and finally execute propagation. Presented is a basic example; it is simple to add other stopping conditions, auto-sequences for the stopping conditions, and other propagation elements.

Java
FiniteManeuverSegment segment = new FiniteManeuverSegment();
segment.setName("Finite Maneuver");
segment.setPropagatorDefinition(basePropagator);

DurationStoppingCondition tenMinuteBurn = new DurationStoppingCondition(Duration.fromMinutes(10.0));
segment.getStoppingConditions().add(tenMinuteBurn);

// add a stopping condition to run out of fuel
StoppingCondition outOfFuelStoppingCondition = 
        FiniteManeuverSegment.createOutOfFuelStoppingCondition(fuelMassScalar.getIntegrationValue(), "Out of Fuel", true);
segment.getStoppingConditions().add(outOfFuelStoppingCondition);

// propagate
SegmentPropagator propagator = segment.getSegmentPropagator();
SegmentResults results = propagator.propagate();

If you are modeling a high energy maneuver that doesn't take too long, often you would like to start the burn before the end of the previous segment. For example, an impulsive maneuver at perigee makes sense to raise your orbit, but you often want the finite version of that maneuver to have its time centered on the perigee. The FiniteManeuverSegment lets you start the maneuver earlier in propagation by setting the BurnCenteringDuration (get / set) as shown. The intent of this construct is that it will be set to the threshold of a DurationStoppingCondition. The value of BurnCenteringDuration (get / set) will be evaluated at propagation time. That Duration will be halved and assigned the opposite sign of the direction of propagation (so if we are propagating backwards, a positive time will be used). Then a state will be generated by the most recent segment that propagated through that time, and that new state will be the initial state of the maneuver segment. The overall results of the previous segments will also be truncated to end at the evaluated time.

Java
InitialStateSegment<BasicState> initialState = configureInitialStateSegment();
PropagateSegment initialPropagation = createAndConfigurePropagateSegment();

FiniteManeuverSegment finiteManeuverSegment = basicFiniteManeuver();

DurationStoppingCondition durationCondition = new DurationStoppingCondition(Duration.fromMinutes(5.0));
finiteManeuverSegment.getStoppingConditions().add(durationCondition);
finiteManeuverSegment.setBurnCenteringDuration(durationCondition.getThreshold());

PropagateSegment finalPropagation = createAndConfigurePropagateSegment();

SegmentList mcs = new SegmentList();
mcs.getSegments().add(initialState);
mcs.getSegments().add(initialPropagation);
mcs.getSegments().add(finiteManeuverSegment);
mcs.getSegments().add(finalPropagation);

// propagate
SegmentListResults results = mcs.getSegmentListPropagator().propagateSegmentList();
PropagateSegmentResults firstPropagateSegmentResults = (PropagateSegmentResults) results.getResultsOfSegment(initialPropagation);
FiniteManeuverSegmentResults maneuverResults = (FiniteManeuverSegmentResults) results.getResultsOfSegment(finiteManeuverSegment);

Occasionally, it is useful to fix your thrust vector to what it was at a specific time. For example, maybe your spacecraft has guidance systems that hold its orientation constant during a burn as opposed to adjusting attitude. In a situation such as this, the AxesFixedAtJulianDate should be used for the burn axes. These axes will hold an orientation at a specific time. This time of fixation does not have to be known until the propagator is run. To further illustrate this example, let's complicate the scenario to imagine using burn centering. The fixation time in this more complicated case can be set to a parameter that gets evaluated at propagation time.

To choose when the segment that was previous to the finite maneuver ends and when the engine is actually ignited, you can construct a parameter from the IgnitionState (get) or PropagationStartState (get) state parameters on the FiniteManeuverSegment.

Java
FiniteManeuverSegment segment = new FiniteManeuverSegment();
segment.setName("Finite Maneuver");

// createPropagatedNewtonianPoint also creates fuel and dry mass elements
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(SatelliteMotionIdentification);
NumericalPropagatorDefinition basePropagator = createNumericalPropagator(propagationPoint);
segment.setPropagatorDefinition(basePropagator);
PropagationScalar fuelMassElement = configureDefaultMassesAndReturnFuelElement(propagationPoint, basePropagator, 500.0, 500.0);

double nominalFuelFlow = -0.02; // kg/sec, negative because fuel will be consumed
ScalarFixed fuelFlowRateScalar = new ScalarFixed(nominalFuelFlow);
fuelMassElement.setScalarDerivative(fuelFlowRateScalar);
double isp = 250.0; // seconds
Cartesian thrustDirection = new Cartesian(0.0, 0.0, 1.0); // unit vector

AxesVelocityOrbitNormal axesThatWillGetFixed = new AxesVelocityOrbitNormal(propagationPoint.getIntegrationPoint());
AxesFixedAtJulianDate axesFixedAtEngineIgnition = new AxesFixedAtJulianDate(axesThatWillGetFixed, new TimeFromStateValueDefinition(segment.getIgnitionState()));
Vector thrustVector = ContinuousThrustForceModel.createIspThrustVector(Scalar.toScalar(isp), fuelFlowRateScalar, axesFixedAtEngineIgnition, thrustDirection);
ContinuousThrustForceModel thrustForce = new ContinuousThrustForceModel(thrustVector, propagationPoint.getIntegrationFrame().getAxes());
propagationPoint.getAppliedForces().add(thrustForce);

StoppingCondition tenMinuteBurn = new DurationStoppingCondition(Duration.fromMinutes(10.0));
segment.getStoppingConditions().add(tenMinuteBurn);

// add a stopping condition to run out of fuel
StoppingCondition outOfFuelStoppingCondition = FiniteManeuverSegment.createOutOfFuelStoppingCondition(fuelMassElement.getIntegrationValue(), "Out of Fuel", true);
segment.getStoppingConditions().add(outOfFuelStoppingCondition);

SegmentPropagator propagator = segment.getSegmentPropagator();
SegmentResults results = propagator.propagate();

You can construct your ThrustVector (get / set) with any of DME Component Libraries geometry types. To demonstrate how flexible the system is, we are going to create a Scalar that will allow you to change the thrust direction, and then use this to zero out the inclination of an orbit with a low thrust engine.

First, we create our Scalar. This scalar will return 1 or negative 1 based on the Z component of the passed in vector. This scalar will also scale down a little as the Z component of the vector becomes small.

Java
public static class ScalarSwitchingOnVelocityVector extends Scalar {
    public ScalarSwitchingOnVelocityVector() {}

    public ScalarSwitchingOnVelocityVector(Vector vector) {
        m_vector = vector;
    }

    protected ScalarSwitchingOnVelocityVector(ScalarSwitchingOnVelocityVector existingInstance, CopyContext context) {
        super(existingInstance, context);
        m_vector = context.updateReference(existingInstance.m_vector);
    }

    @Override
    public Object clone(CopyContext context) {
        return new ScalarSwitchingOnVelocityVector(this, context);
    }

    @Override
    protected boolean checkForSameDefinition(Scalar other) {
        ScalarSwitchingOnVelocityVector o = other instanceof ScalarSwitchingOnVelocityVector ? (ScalarSwitchingOnVelocityVector) other : null;
        return o != null && areSameDefinition(m_vector, o.m_vector) && checkForSameDefinition(o);
    }

    protected boolean checkForSameDefinition(ScalarSwitchingOnVelocityVector other) {
        return other != null && other.getClass() == ScalarSwitchingOnVelocityVector.class;
    }

    @Override
    protected int computeCurrentDefinitionHashCode() {
        return HashCode.combine(ScalarSwitchingOnVelocityVector.class.hashCode(),
                                super.computeCurrentDefinitionHashCode(),
                                getDefinitionHashCode(m_vector));
    }

    @Override
    public void enumerateDependencies(DependencyEnumerator enumerator) {
        super.enumerateDependencies(enumerator);
        enumerator.enumerate(m_vector);
    }

    @Override
    public ScalarEvaluator getEvaluator(EvaluatorGroup group) {
        if (group == null) 
            throw new ArgumentNullException("group");

        PropertyInvalidException.validateNonNull(m_vector, "Vector");
        return group.createEvaluator(createEvaluatorCallback);
    }

    private final EvaluatorGroup.Callback0<ScalarEvaluator> createEvaluatorCallback = EvaluatorGroup.Callback0.of(this::createEvaluator);

    private final ScalarEvaluator createEvaluator(EvaluatorGroup group) {
        return new Evaluator(group, m_vector.getEvaluator(group));
    }

    public final Vector getVector() {
        return m_vector;
    }

    public final void setVector(Vector value) {
        throwIfFrozen();
        m_vector = value;
    }

    private Vector m_vector;

    private static final class Evaluator extends ScalarEvaluator {
        public Evaluator(EvaluatorGroup group, VectorEvaluator evaluator) {
            super(group);
            m_evaluator = evaluator;
        }

        private Evaluator(Evaluator existingInstance, CopyContext context) {
            super(existingInstance, context);
            m_evaluator = existingInstance.m_evaluator;
            updateEvaluatorReferences(context);
        }

        @Override
        public Object clone(CopyContext context) {
            return new Evaluator(this, context);
        }

        @Override
        protected void dispose(boolean disposing) {
            if (m_isDisposed || !disposing) {
                return;
            }
            m_isDisposed = true;
            m_evaluator.dispose();
        }

        @Override
        public boolean getIsThreadSafe() {
            return false;
        }

        @Override
        public void updateEvaluatorReferences(CopyContext context) {
            m_evaluator = context.updateReference(m_evaluator);
        }

        @Override
        public TimeIntervalCollection getAvailabilityIntervals(TimeIntervalCollection consideredIntervals) {
            return m_evaluator.getAvailabilityIntervals(consideredIntervals);
        }

        @Override
        public boolean isAvailable(JulianDate date) {
            return m_evaluator.isAvailable(date);
        }

        @Override
        public boolean getIsTimeVarying() {
            return m_evaluator.getIsTimeVarying();
        }

        @Override
        public Motion1<Double> evaluate(JulianDate date, int order) {
            Motion1<Cartesian> motion = m_evaluator.evaluate(date, order + 1);

            double value = motion.getValue().getZ();
            if (value == 0) {
                return new Motion1<Double>(0.0);
            }

            // throttle down as our Z speed gets low
            double multiple = -1.0;
            if (Math.abs(value) < 10.0) {
                multiple = multiple * value * 0.1;
            }

            return new Motion1<Double>(multiple * Math.signum(value));
        }

        private boolean m_isDisposed;
        private VectorEvaluator m_evaluator;
    }
}

We begin by defining the initial conditions and setting up the basic types that we will use.

Java
double earthGm = WorldGeodeticSystem1984.GravitationalParameter;
Motion1<Cartesian> initialConditions = new ModifiedKeplerianElements(17000000.0, 1.0 / 17200000.0, Trig.degreesToRadians(23.0), 
        0.0, 0.0, Trig.degreesToRadians(5.0), earthGm).toCartesian();

PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(SatelliteMotionIdentification, initialConditions);
NumericalPropagatorDefinition basePropagator = createNumericalPropagator(propagationPoint);

double initialActualFuelMass = 5000.0;
double initialDryMass = 1000.0;
PropagationScalar fuelMassElement = configureDefaultMassesAndReturnFuelElement(propagationPoint, basePropagator, initialActualFuelMass, initialDryMass);

ScalarStoppingCondition outOfFuelStoppingCondition = new ScalarStoppingCondition(fuelMassElement.getIntegrationValue(), 0.00000001, 0.000000001,
        StopType.THRESHOLD_DECREASING);
outOfFuelStoppingCondition.setEnabled(StoppingConditionEnabled.ENABLED);
ScalarFixed fuelFlow = new ScalarFixed(-0.005);
fuelMassElement.setScalarDerivative(fuelFlow);

The fuel flow code is similar to the previous example.

Java
PropagationScalar fuelMassScalar = new PropagationScalar(fuelMass);
fuelMassScalar.setIdentification("Fuel Mass");

AuxiliaryStateScalar dryMassScalar = new AuxiliaryStateScalar(Scalar.toScalar(dryMass));
dryMassScalar.setIdentification("Dry Mass");

propagationPoint.setMass(Scalar.add(fuelMassScalar.getIntegrationValue(), dryMassScalar.getAuxiliaryScalar()));

basePropagator.getIntegrationElements().add(fuelMassScalar);
basePropagator.getAuxiliaryElements().add(dryMassScalar);

Now we make our thrust force model. It is very similar to the previous examples; the only interesting difference is that we are multiplying our thrust vector by an instance of the switching scalar.

Java
// create the continuous thrust force model
ContinuousThrustForceModel thrust = new ContinuousThrustForceModel();
thrust.setIntegrationAxes(propagationPoint.getIntegrationFrame().getAxes());

// use the constant thrust/isp thrust vector
double isp = 1000.0;
Vector thrustVector = ContinuousThrustForceModel.createIspThrustVector(Scalar.toScalar(isp), 
        fuelFlow,
        propagationPoint.getIntegrationFrame().getAxes(),
        new Cartesian(0.0, 0.0, 1.0)); // thrust along the negative Z
thrust.setThrustVector(thrustVector);

Vector velocityVector = new VectorVelocity(propagationPoint.getIntegrationPoint(), propagationPoint.getIntegrationFrame());

ScalarSwitchingOnVelocityVector switchingScalar = new ScalarSwitchingOnVelocityVector(velocityVector);
thrust.setThrustVector(Vector.multiply(thrustVector, switchingScalar)); // switch the thrust back and forth

propagationPoint.getAppliedForces().add(thrust);

Finally we create our segment, stopping conditions, and propagate.

Java
FiniteManeuverSegment segment = new FiniteManeuverSegment();
segment.setPropagatorDefinition(basePropagator);

// a time condition just in case
segment.getStoppingConditions().add(new DurationStoppingCondition(Duration.fromDays(10.0)));

// don't forget the out of fuel stopping condition we made
segment.getStoppingConditions().add(outOfFuelStoppingCondition);

// don't keep going once we've hit our flat orbit
ScalarStoppingCondition inclinationStoppingCondition = new ScalarStoppingCondition(
        new ScalarModifiedKeplerianElement(
            earthGm, 
            propagationPoint.getIntegrationPoint(),
            KeplerianElement.INCLINATION, 
            propagationPoint.getIntegrationFrame()), 
        0.0, Trig.degreesToRadians(0.05), StopType.ANY_THRESHOLD);
segment.getStoppingConditions().add(inclinationStoppingCondition);

SegmentList list = new SegmentList();
list.getSegments().add(segment);

Note that because we are not trying to line up when our inclination is 0 with when the satellite crosses the equator, we won't get a final inclination that is exactly zero, but it will be very close.

Zero Inclination Example

SwitchableSegment

There will be times when you might want to change the segment you are propagating in a TargetedSegmentList>. For example, maybe you want to swap out a low fidelity propagate segment with a SegmentList containing a higher fidelity propagator. The SwitchableSegment construct coupled with a SwapSegmentOperator lets you perform this operation.

Java
TargetedSegmentList mcs = new TargetedSegmentList();

PropagateSegment propagateWithoutManeuver = createAndConfigurePropagateSegment();
FiniteManeuverSegment propagateWithManeuver = basicFiniteManeuver();

SwitchableSegment overallManeuverSegment = new SwitchableSegment(propagateWithManeuver, propagateWithoutManeuver);
PropagateSegment propagateSomeMore = createAndConfigurePropagateSegment();

mcs.getSegments().add(overallManeuverSegment);
mcs.getSegments().add(propagateSomeMore);

TargetedSegmentListDifferentialCorrector firstDifferentialCorrector = createDifferentialCorrector1(propagateWithoutManeuver, propagateWithoutManeuver);

SwapSegmentOperator swapSegmentOperator = new SwapSegmentOperator(overallManeuverSegment);

TargetedSegmentListDifferentialCorrector secondDifferentialCorrector = createDifferentialCorrector2(propagateWithManeuver, propagateWithManeuver);

mcs.getOperators().add(firstDifferentialCorrector);
mcs.getOperators().add(swapSegmentOperator);
mcs.getOperators().add(secondDifferentialCorrector);

You will need to specify one of the segments as the primary segment. The primary segment determines how the final ephemeris for the SwitchableSegment will be reported, by determining the segment's ReferenceFrame and Axes used for the report. If you inspect the OriginalResults (get) property, you will get the ephemeris in the frames and axes as defined in that segment. In a typical configuration, the PrimarySegment (get / set) is the segment that the SwitchableSegment will get switched to, and the AlternateSegment (get / set) is the segment that is initially propagated.

A more concrete example of the use of a SwitchableSegment is with the SeedFiniteManeuverOperator. This operator will configure several settings on a FiniteManeuverSegment based on an ImpulsiveManeuverSegment that was initially run.

Java
TargetedSegmentList mcs = new TargetedSegmentList();
PropagateSegment propToSomePoint = createAndConfigurePropagateSegment();

ImpulsiveManeuverSegment impulsiveManeuver = createFirstManeuver();

ImpulsiveManeuverInformation impulsiveManeuverInformation = 
        new ImpulsiveManeuverInformation(SatelliteMotionIdentification, new Cartesian(0.0, 0.0, 100.0));
impulsiveManeuverInformation.setOrientation(earth.getInertialFrame().getAxes());
impulsiveManeuver.setManeuver(impulsiveManeuverInformation);

durationStoppingCondition.setThreshold(new ParameterizedValueDefinition<Duration>());

SwitchableSegment overallManeuverSegment = new SwitchableSegment(finiteManeuver, impulsiveManeuver);
PropagateSegment propagateSomeMore = createAndConfigurePropagateSegment();

mcs.getSegments().add(propToSomePoint);
mcs.getSegments().add(overallManeuverSegment);
mcs.getSegments().add(propagateSomeMore);

TargetedSegmentListDifferentialCorrector firstDifferentialCorrector = createDifferentialCorrector1(impulsiveManeuver, impulsiveManeuver);

SeedFiniteManeuverOperator seedManeuverOperator = new SeedFiniteManeuverOperator(overallManeuverSegment, SatelliteMotionIdentification);
seedManeuverOperator.setFuelFlowRateScalar(fuelFlowRate);
seedManeuverOperator.setStoppingConditionToUpdate(durationStoppingCondition);

TargetedSegmentListDifferentialCorrector secondDifferentialCorrector = createDifferentialCorrector2(finiteManeuver, finiteManeuver);

mcs.getOperators().add(firstDifferentialCorrector);
mcs.getOperators().add(seedManeuverOperator);
mcs.getOperators().add(secondDifferentialCorrector);

SegmentList

SegmentList is a container of multiple segments. It is a segment itself, so one SegmentList can contain another SegmentList. When propagated, it returns a SegmentResults that you may cast to a SegmentListResults in order to get additional information, such as the time interval of each segment and results of each individual segment. The adapters do not need to be set; they will be initialized from the nested segments. Remember that the order of the segments in Segments (get) is important.

The following code sample demonstrates how to populate the Segments (get) list and propagate the overall SegmentList:

Java
// make up a bunch of segments
NumericalInitialStateSegment initialSegment = configureNumericalInitialStateSegment();
NumericalPropagatorSegment propagateSegment = configureNumericalPropagatorSegment();
ImpulsiveManeuverSegment maneuverSegment = configureImpulsiveManeuver();
NumericalPropagatorSegment finalPropagateSegment = configureNumericalPropagatorSegment();
SegmentList someOtherSegmentList = new SegmentList();
someOtherSegmentList.setName("Some_Other_Segment_List");

// create the SegmentList you will propagate
SegmentList mainSegmentList = new SegmentList();
mainSegmentList.setName("Main_Segment_List");

// add the segments in order
mainSegmentList.getSegments().add(initialSegment);
mainSegmentList.getSegments().add(propagateSegment);
someOtherSegmentList.getSegments().add(maneuverSegment);

// nest the maneuver in an inner SegmentList
mainSegmentList.getSegments().add(someOtherSegmentList);

// go ahead and put one SegmentList in another
mainSegmentList.getSegments().add(finalPropagateSegment);

// get the propagator and propagate
SegmentListPropagator segmentListPropagator = mainSegmentList.getSegmentListPropagator();
SegmentListResults segmentListResults = segmentListPropagator.propagateSegmentList();

// SegmentListResults will recursively search for a specific segments results
SegmentResults maneuverResults = segmentListResults.getResultsOfSegment(maneuverSegment);

TargetedSegmentList

TargetedSegmentList is a SegmentList that also has one or more TargetedSegmentListOperators. The segments included in the TargetedSegmentList will have some part of its segments modified by the operators. These modifications can change values of settings in the segments to solve for a particular trajectory, or turn some option on or off. See the Targeted Segment List Operators topic for more information.

Configuring a TargetedSegmentList is the same as configuring a normal SegmentList, but also requires that you configure at least one TargetedSegmentListOperator. See the Targeted Segment List Operators topic for examples to how to configure and use TargetedSegmentListOperators and TargetedSegmentList.

UpdateSegment

UpdateSegment allows you to directly edit the state from the previous segment. The adapters do not need to be set. Often editing the state is done with a callback that will modify a value in the state. For example, if you wanted to decrease the fuel mass, the following code sample demonstrates how to do so:

Java
UpdateSegment updateSegment = new UpdateSegment();

// we will vary the fuel mass to target a final overall mass
ParameterizedScalarVariable variable = new ParameterizedScalarVariable(100.0, 1.0, 0.1, updateSegment);

// configure the updater
SingleValueStateUpdater fuelMassUpdater = 
        new SingleValueStateUpdater(
                variable.getValue(), 
                SingleValueUpdaterCallback.of((state, updateValue) -> {
                    // update the state by subtracting the value of the update value from the fuel mass
                    state.modifyValue(fuelMassID, state.<Double> getValue(fuelMassID) - updateValue);
                    return state;
                }));

// you can have as many updaters as you would like
updateSegment.getUpdates().add(fuelMassUpdater);

// configure our overall segment list
TargetedSegmentList targetedSegmentList = new TargetedSegmentList();
targetedSegmentList.getSegments().add(initialStateSegment); // made earlier
targetedSegmentList.getSegments().add(updateSegment);
TargetedSegmentListDifferentialCorrector differentialCorrector = new TargetedSegmentListDifferentialCorrector();

// we want our overall mass to be 850 kg
DelegateBasedConstraint overallMassConstraint = 
        new DelegateBasedConstraint(
                DelegateBasedConstraintCallback.of(s -> 
                        s.getStateForNextSegment().<Double> getValue(fuelMassID) +
                        s.getStateForNextSegment().<Double> getValue(dryMassID)), 
                updateSegment, 
                850.0, 
                0.1);

// finish configuring our differential corrector
differentialCorrector.getConstraints().add(overallMassConstraint);
differentialCorrector.getVariables().add(variable);

// add the operator
targetedSegmentList.getOperators().add(differentialCorrector);

// propagate
SegmentListPropagator propagator = targetedSegmentList.getSegmentListPropagator();
SegmentListResults results = propagator.propagateSegmentList();
SegmentResults updateSegmentResults = results.getSegmentResults().get(1);
double finalMass = updateSegmentResults.getStateForNextSegment().<Double> getValue(fuelMassID) + 
                   updateSegmentResults.getStateForNextSegment().<Double> getValue(dryMassID);
// will be 850

ReturnSegment

ReturnSegment is a segment that will end propagation of a specified SegmentList if enabled. This segment can also distinguish between running as part of normal propagation, or if it is being run as part of a TargetedSegmentListOperator and be enabled or disabled depending on which content it is being propagated.

The related ChangeReturnSegmentOperator type is a TargetedSegmentListOperator that can change the Behavior (get / set) property of a ReturnSegment.

Generally, a ReturnSegment can end any SegmentList that it is in, however, there is one notable exception. If a ReturnSegment is in a TargetedSegmentList, it can not return out to a SegmentList that is outside of the TargetedSegmentList while the TargetedSegmentList is running a ChangeReturnSegmentOperator.

The following code sample demonstrates using a ReturnSegment:

Java
// define constants
String satelliteName = "Satellite";
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();

// make the segment list (create the differential corrector too)
TargetedSegmentList segmentList = createTargetedSegmentList();

// initial state
NumericalInitialStateSegment initialStateSegment = configureNumericalInitialStateSegment();
ImpulsiveManeuverSegment firstManeuver = createFirstManeuver();

// make the propagation segment
// we will need the integration point to make the altitude stopping condition
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(satelliteName);
NumericalPropagatorSegment propagationSegment = new NumericalPropagatorSegment();
propagationSegment.setPropagatorDefinition(createNumericalPropagator(propagationPoint));

// make a stopping condition
ScalarStoppingCondition altitudeStoppingCondition = new ScalarStoppingCondition(
        new ScalarCartographicElement(
                earth,
                propagationPoint.getIntegrationPoint(),
                CartographicElement.HEIGHT),
        420000000.0, // 42,000 kilometers
        1.0,         // stop within a meter
        StopType.ANY_THRESHOLD);
propagationSegment.getStoppingConditions().add(altitudeStoppingCondition);

// if the satellite goes above 42 000 km, then we don't want to do the second maneuver
ReturnSegment stopIfAltitudeIsTooLow = new ReturnSegment(segmentList, ReturnSegmentBehavior.ENABLED);
propagationSegment.setStoppingConditionAutoSegment(altitudeStoppingCondition, stopIfAltitudeIsTooLow, 1);

// or our orbit might still be too low in which case we want to do another maneuver
StoppingCondition anotherStoppingCondition = createSomeOtherStoppingCondition();
propagationSegment.getStoppingConditions().add(anotherStoppingCondition);
ImpulsiveManeuverSegment impulsiveManeuver = createSecondManeuver();

// configure the segment list
segmentList.getSegments().add(initialStateSegment);
segmentList.getSegments().add(firstManeuver);
segmentList.getSegments().add(propagationSegment);
segmentList.getSegments().add(impulsiveManeuver);

StopSegment

StopSegment is a segment that will stop all propagation, if enabled. This can be very useful to use with an auto-sequence, configured by calling setStoppingConditionAutoSegment, where you know that propagation should not continue if a particular StoppingCondition is satisfied.

If the StopSegment is enabled, propagation will stop the very first time the segment is propagated. This includes when being run as part of a TargetedSegmentListOperator.

The related ChangeStopSegmentOperator type is a TargetedSegmentListOperator that can change the StopEnabled (get / set) property of a StopSegment.

The following code sample demonstrates using a StopSegment:

Java
// define constants
String satelliteName = "Satellite";
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();

// make the segment list
SegmentList segmentList = new SegmentList();

// initial state
NumericalInitialStateSegment initialStateSegment = configureNumericalInitialStateSegment();

// make the propagation segment
// we will need the integration point to make the altitude stopping condition
PropagationNewtonianPoint propagationPoint = createPropagatedNewtonianPoint(satelliteName);
NumericalPropagatorSegment propagationSegment = new NumericalPropagatorSegment();
propagationSegment.setPropagatorDefinition(createNumericalPropagator(propagationPoint));

// make a stopping condition
ScalarStoppingCondition altitudeStoppingCondition = new ScalarStoppingCondition(
        new ScalarCartographicElement(
                earth,
                propagationPoint.getIntegrationPoint(),
                CartographicElement.HEIGHT),
        100000.0, // 100 kilometers
        1.0,      // stop within a meter
        StopType.THRESHOLD_DECREASING); // the satellite won't come back up

// the satellite won't come back up
propagationSegment.getStoppingConditions().add(altitudeStoppingCondition);

// if the satellite goes below 100 km, then stop propagation
StopSegment stopIfAltitudeIsTooLow = new StopSegment(true);
propagationSegment.setStoppingConditionAutoSegment(altitudeStoppingCondition, stopIfAltitudeIsTooLow, 1);

// or our orbit might not reenter in which case we want to continue propagating another segment.
StoppingCondition anotherStoppingCondition = createSomeOtherStoppingCondition();
propagationSegment.getStoppingConditions().add(anotherStoppingCondition);

// if we don't stop, this segment will propagate after the previous propagation segment finishes
NumericalPropagatorSegment someOtherPropagationSegment = configureNumericalPropagatorSegment();

// configure the segment list
segmentList.getSegments().add(initialStateSegment);
segmentList.getSegments().add(propagationSegment);
segmentList.getSegments().add(someOtherPropagationSegment);