Click or drag to resize

Code Sample

The following code samples illustrate the various pieces required to set up, configure, and numerically propagate a state.

Note Note

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

Configuring Context

First, we configure the context in STK Components to include EOP data, using EarthOrientationParameters, and loading JPL ephemerides using JplDE421. The EOP and JPL data are not required to run, but they will affect the accuracy of the force models during propagation.

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();
SunCentralBody sun = CentralBodiesFacet.getFromContext().getSun();
MoonCentralBody moon = CentralBodiesFacet.getFromContext().getMoon();

// Load EOP table into the library
try (BufferedReader reader = Files.newBufferedReader(Paths.get(eopFilePath))) {
    earth.setOrientationParameters(EarthOrientationParametersFile.readData(reader));
}

// Load JPL Ephemerides into the library
JplDE421 ephem = new JplDE421(deFilePath);
// The following updates the "CenterOfMassPoint" on each of the CentralBodies
ephem.useForCentralBodyPositions(CentralBodiesFacet.getFromContext());
// The following is needed to accurately model the precise orientation of the Moon
moon.setFixedFrame(ephem.getMoonTopographicFixedFrame());
Defining State Elements

Next, we decide which elements need to be integrated in the state and which should be evaluated as auxiliary data during propagation. A position which depends on complex force models needs to be integrated to predict its values. In contrast, a density is simply a function of position and has no differential equation associated with it. The differential equations will be filled in next.

Java
NumericalPropagatorDefinition state = new NumericalPropagatorDefinition();

final String positionID = "MyVehiclePosition";
PropagationNewtonianPoint position = new PropagationNewtonianPoint(positionID, earth.getInertialFrame(), initialPosition, initialVelocity);

state.setEpoch(epoch);
state.getIntegrationElements().add(position);
// state.getIntegrationElements().add(angleOfAttack);
// etc.

// Add auxiliary output
AuxiliaryStateScalar densityOutput = new AuxiliaryStateScalar();
String densityID = "MyAtmosphericDensity";
densityOutput.setIdentification(densityID);
state.getAuxiliaryElements().add(densityOutput);
Configuring Force Models

In general, each PropagationStateElement will have some way of defining its derivative for the integrator. In the case of a PropagationVector, the derivative is simply a Vector whose value represents the derivative of the highest order of the represented vector state. However, the most common element will likely be the PropagationNewtonianPoint which models the position and velocity of a body subject to a list of ForceModels.

Java
SphericalHarmonicGravity gravity = new SphericalHarmonicGravity();
gravity.setTargetPoint(position.getIntegrationPoint());
int degree = 41;
int order = 41;
gravity.setGravityField(new SphericalHarmonicGravityField(
        SphericalHarmonicGravityModel.readFrom(gravityEGM96FilePath),
        degree, order, true, SphericalHarmonicsTideType.NONE));

ThirdBodyGravity thirdBody = new ThirdBodyGravity();
thirdBody.setCentralBody(earth);
final double sunGm = 1.327122e20; // m^3 / sec^2
final double moonGm = 4.902801076e12; // m^3 / sec^2
thirdBody.addThirdBody(sun.getName(), sun.getCenterOfMassPoint(), sunGm);
thirdBody.addThirdBody(moon.getName(), moon.getCenterOfMassPoint(), moonGm);
thirdBody.setTargetPoint(position.getIntegrationPoint());

SimpleSolarRadiationForce srp = new SimpleSolarRadiationForce();
ScalarOccultation occultationFactor = new ScalarOccultationDualCone();
occultationFactor.setIlluminatingBody(sun);
occultationFactor.getOccludingBodies().add(earth);
occultationFactor.getOccludingBodies().add(moon);
occultationFactor.setTargetPoint(position.getIntegrationPoint());
occultationFactor.setVectorType(RadiationVectorType.TRUE_POSITION);
srp.setOccultationFactor(occultationFactor);
srp.setCoefficientOfReflectivity(Scalar.toScalar(1.0));
srp.setReferenceArea(Scalar.toScalar(20.0));

AtmosphericDragForce drag = new AtmosphericDragForce();
ConstantSolarGeophysicalData geoData = new ConstantSolarGeophysicalData(150.0, 3.00);
ScalarDensityJacchiaRoberts density = new ScalarDensityJacchiaRoberts(position.getIntegrationPoint(), geoData);
drag.setDensity(density);
drag.setCoefficientOfDrag(Scalar.toScalar(2.2));
drag.setReferenceArea(Scalar.toScalar(20.0));

position.getAppliedForces().add(gravity);
position.getAppliedForces().add(thirdBody);
position.getAppliedForces().add(srp);
position.getAppliedForces().add(drag);
position.setMass(mass);

densityOutput.setAuxiliaryScalar(density);
Configuring Integrator

The last step before creating the propagator is to choose which integration scheme to use in order to propagate the state and to configure its settings.

Java
RungeKuttaFehlberg78Integrator integrator = new RungeKuttaFehlberg78Integrator();
integrator.setInitialStepSize(stepSize);
integrator.setStepSizeBehavior(KindOfStepSize.RELATIVE);
integrator.setMaximumStepSize(Double.MAX_VALUE);
integrator.setMinimumStepSize(0.001);
// This is STK's default and truncates each step to 3 decimal places
integrator.setStepTruncationOrder(-3);

state.setIntegrator(integrator);
Propagating

After creating a NumericalPropagator, we use it to generate ephemeris over a given span of time, then create a PointInterpolator to represent the propagated ephemeris for further analysis in STK Components. Note that this point should be used to define the LocationPoint (get / set) of a platform, rather than the IntegrationPoint (get) which only represents the position during the integration process.

Java
int outputSparsity = 1; // 1 indicates to output after every integration step, 2 every other step, etc, etc
Duration propagationTime = Duration.fromSeconds(totalTime);
NumericalPropagationStateHistory rawData = propagator.propagate(propagationTime, outputSparsity);

// Recover the position, velocity, (and acceleration if specified on the position before creating the propagator)
DateMotionCollection1<Cartesian> ephemeris = rawData.getDateMotionCollection(positionID);

DateMotionCollection1<Double> densityData = rawData.getDateMotionCollection(densityID);

Point platformPosition = new PointInterpolator(position.getIntegrationFrame(), InterpolationAlgorithmType.HERMITE, 6, ephemeris);

// Perform analysis using other libraries...

The above code snippet illustrates a simple propagation over time, however, it is also possible to have the duration of propagation determined not by an overall time but by some other stopping condition which is a function of time and the state. This can be accomplished by attaching an event handler to the StepTaken (add / remove) event, which will fire every time the propagator takes a step, as shown below:

Java
// The following allows us to propagate all at once or step the state manually
final NumericalPropagator propagator = state.createPropagator(group);

// Note: the "IntegrationPoint" from the PropagationNewtonianPoint cannot be evaluated
// outside of the context of the NumericalPropagator.  However, the StepTaken event
// occurs inside the scope of propagation and the point is created in the same EvaluatorGroup.
// So, the evaluation of the point will represent the current position and velocity in the state.
// Similarly, any evaluator which was built off of the point will evaluate using the current
// position and velocity.

final PointEvaluator activePointEvaluator = GeometryTransformer.observePoint(position.getIntegrationPoint(), position.getIntegrationFrame(), group);

propagator.addStepTaken(EventHandler.of((sender, e) -> {
    JulianDate t = e.getCurrentTime();
    double[] x = e.getCurrentState();

    StepSizeInformation info = e.getStepSizeInformation();

    // The following two methods of retrieving the position are equally valid...
    Motion1<Cartesian> posVel = propagator.getConverter().convertState(positionID, x);
    // ...but this one is faster and eliminates the need to perform a transformation manually
    // if the position is needed in a different frame
    posVel = activePointEvaluator.evaluate(e.getCurrentTime(), 1);

    // Check for a "stopping condition" and if reached stop propagation
    if (posVel.getValue().getMagnitude() > 1e8) {
        e.setIndication(PropagationEventIndication.STOP_PROPAGATION_AFTER_STEP);
    } else {
        e.setIndication(PropagationEventIndication.CONTINUE_PROPAGATION);
    }
}));

It is also possible to use the propagator in situations where surrounding code is iterating (or "shooting") to find some desired final state based on an initial guess. It's always possible to create a new propagator, but if the only things that change are the initial epoch and initial state, it's also possible to reset the propagator to a new set of initial conditions and try again. To do this, use the PropagationStateConverter in reverse to specify new initial conditions and update the initial state accordingly, then call the reset method on the propagator.

Without calling reset, each call to propagate will start where the previous call left off. This is designed to allow systems which propagate for extended periods of time to stop intermittently to verify the ephemeris, write or commit the data, or provide a means of specifying segments of propagation. However, note that if any of the differential equations defined on the state elements need to change, a new propagator will need to be created based on the new equations. However, to start the new propagator where the last one left off, simply set the new propagator's InitialState (get) to a copy of the old propagator's CurrentState (get).

Java
// If conducting an iterative analysis with the same configuration (same state, force models, correctors, etc)...
// Reinitialize the state and repropagate

double[] newInitialState = propagator.getInitialState(); // Gets a copy

Motion1<Cartesian> newInitialConditions = new Motion1<>(nextInitialPosition, nextInitialVelocity);
propagator.getConverter().adjustState(positionID, newInitialState, newInitialConditions);

propagator.reset(epoch, newInitialState);
rawData = propagator.propagate(Duration.fromSeconds(totalTime), 1);

// To use a different state or different force models, create a new NumericalPropagatorDefinition with new StateElements and ForceModels