Click or drag to resize

Evaluator Parameterization

The Evaluator pattern works on the assumption that all information required for analysis is known up front. That is, a developer creates definitional objects from known data sets and then performs the desired analysis. This enables highly-optimized evaluators to be created for specific computations. However, in some situations, not all inputs into your computations are known up-front. In these cases, it can be useful to design a system using placeholder objects, which are specially designed to allow the actual input values to be provided later when the evaluators are actually being evaluated.

Normal evaluators, as described in the Evaluators And Evaluator Groups topic, represent functions of time. Parameterized evaluators extend this concept to support functions that vary with respect to additional parameters.

Typical Evaluators

The following code sample shows a typical analysis situation that uses a normal evaluator to determine whether access exists between a UAV and its ground station. Two platforms are constructed, a route for the UAV is created using waypoints, and the position of the ground station is defined. An AccessEvaluator is created and evaluated in order to compute access for the link. The evaluator is optimized for this particular scenario, and the evaluator can be used from multiple threads simultaneously, as long as the IThreadAware interface is inspected. The Multithreading topic describes this interface in more detail.

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

JulianDate uavStart = new JulianDate(new GregorianDate(1980, 8, 1, 0, 0, 0));
JulianDate uavStop = new JulianDate(new GregorianDate(1980, 8, 1, 1, 0, 0));

Waypoint[] waypoints = new Waypoint[2];
waypoints[0] = new Waypoint(uavStart, new Cartographic(0, 0, 1000), 100, 10);
waypoints[1] = new Waypoint(waypoints[0], earth.getShape(), new Cartographic(1, 1, 1000), 100);

WaypointPropagator uavPropagator = new WaypointPropagator(earth, Arrays.asList(waypoints));

Platform uav = new Platform();
uav.setLocationPoint(uavPropagator.createPoint());
uav.setOrientationAxes(Axes.getRoot());

Platform groundStation = new Platform();
groundStation.setLocationPoint(new PointCartographic(earth, new Cartographic(0.0, 0.0, 0.0)));
groundStation.setOrientationAxes(Axes.getRoot());

LinkInstantaneous link = new LinkInstantaneous(uav, groundStation);
CentralBodyObstructionConstraint constraint = new CentralBodyObstructionConstraint(link, earth);

AccessEvaluator evaluator = constraint.getEvaluator();

if (evaluator.evaluate(uavStart) != AccessClassification.ACCESS_EXISTS) {
    System.out.println("No access found!");
}

This approach assumes that you know the route up front, in order to create the waypoints using the waypoint propagator. However, suppose instead you are tracking a live UAV operation, using the Tracking Library, and are receiving continual updates as to its position.

To use the standard evaluator pattern, each time a new position is received, new waypoints would be added to the propagator and a new evaluator needs to be created. It can become very expensive to recreate evaluators repeatedly like this due to the time-consuming optimization that is done under the hood. Evaluators are designed to facilitate efficient calculations where only time is changing. Furthermore, it is not thread-safe to change properties of a definitional object, which in this example is the WaypointPropagator, from multiple threads simultaneously. As a result, each evaluator must have its own copy of the data that defines the route. Basically, the evaluator pattern is not designed for this situation.

Parameterized Evaluators

Parameterized evaluators deal with this type of scenario by allowing you to specify all of your known information up front, but insert placeholder definitional objects for data that will not be known until the time of evaluation. The following code sample demonstrates how to design the same access calculation as above, except in this case, the location and orientation of the UAV is defined using placeholders to retrieve the information from an entity, uavEntity, which is being continually updated using Tracking Library as the mission progresses. The Entities topic has more information about entities.

Note Note

This example situation uses Tracking Library, described in more detail in the Tracking topic, but parameterized evaluators are a general concept in STK Components, and do not inherently require Tracking Library.

Java
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth();
EntityParameter<ExampleEntity> entityParameter = new EntityParameter<>();

Platform uav = new Platform();
uav.setLocationPoint(new EntityPoint<>(new TypeLiteral<ExampleEntity>() {}, entityParameter));
uav.setOrientationAxes(new EntityAxes<>(new TypeLiteral<ExampleEntity>() {}, entityParameter));

Platform groundStation = new Platform();
groundStation.setLocationPoint(new PointCartographic(earth, new Cartographic(0.0, 0.0, 0.0)));
groundStation.setOrientationAxes(Axes.getRoot());

LinkInstantaneous link = new LinkInstantaneous(uav, groundStation);
CentralBodyObstructionConstraint constraint = new CentralBodyObstructionConstraint(link, earth);

EvaluatorGroup group = new EvaluatorGroup();

AccessEvaluator evaluator = constraint.getEvaluator(null, group);

ParameterizedEvaluatorWithIntervals2<Transaction, ExampleEntity, AccessClassification, AccessQueryResult> parameterizedEvaluator = 
        group.parameterize(evaluator, TransactionParameter.getInstance(), entityParameter);

context.doTransactionally(Action1.of(transaction -> {
    // Note that it is assumed that uavEntity is an entity
    // object being managed by a different thread responsible
    // for processing the live data as it is received.
    JulianDate date = uavEntity.getLastUpdate().getValue(transaction);
    AccessClassification result = parameterizedEvaluator.evaluate(date, transaction, uavEntity);

    if (result != AccessClassification.ACCESS_EXISTS) {
        System.out.println("No access found!");
    }
}));

The LocationPoint (get / set) and OrientationAxes (get / set) properties of the uav platform are defined to be EntityPoint<ExampleEntity> and EntityAxes<ExampleEntity> instances, respectively. Both of these placeholder definitional objects are constructed using the same EntityParameter<ExampleEntity> instance, indicating that both are parameterized on the same entity parameter. Then, the parameterize method is called on the EvaluatorGroup in order to create the parameterized evaluator, which will be parameterized on the Transaction and the entity. See the Transactions topic for more information on transactions. Finally, we evaluate the parameterized evaluator, passing in the time, transaction, and UAV entity to evaluate with. Internally, these additional parameters are used to fill in the placeholder definitional objects that we used to define our problem, in this case the position and orientation, then performs the access calculation.

The above example shows one dynamically moving object accessing a typical ground position whose position can be defined up front. This same pattern can be extended to model a link between two entities, where both are being updated continually by a data feed. For example, suppose the ground station was actually a communications truck, moving independently from the UAV. The following code sample shows how this can be modeled:

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

EntityParameter<ExampleEntity> uavEntityParameter = new EntityParameter<>();
EntityParameter<ExampleEntity> commsTruckEntityParameter = new EntityParameter<>();

Platform uav = new Platform();
uav.setLocationPoint(new EntityPoint<>(new TypeLiteral<ExampleEntity>() {}, uavEntityParameter));
uav.setOrientationAxes(new EntityAxes<>(new TypeLiteral<ExampleEntity>() {}, uavEntityParameter));

Platform commsTruck = new Platform();
commsTruck.setLocationPoint(new EntityPoint<>(new TypeLiteral<ExampleEntity>() {}, commsTruckEntityParameter));
commsTruck.setOrientationAxes(new EntityAxes<>(new TypeLiteral<ExampleEntity>() {}, commsTruckEntityParameter));

LinkInstantaneous link = new LinkInstantaneous(uav, commsTruck);
CentralBodyObstructionConstraint constraint = new CentralBodyObstructionConstraint(link, earth);

EvaluatorGroup group = new EvaluatorGroup();

AccessEvaluator evaluator = constraint.getEvaluator(null, group);

ParameterizedEvaluatorWithIntervals3<Transaction, ExampleEntity, ExampleEntity, AccessClassification, AccessQueryResult> parameterizedEvaluator =
        group.parameterize(evaluator, TransactionParameter.getInstance(), uavEntityParameter, commsTruckEntityParameter);

context.doTransactionally(Action1.of(transaction -> {
    // Note that it is assumed that uavEntity and truckEntity
    // are entity objects being managed by a different thread
    // responsible for processing the live data as it is received.
    JulianDate date = JulianDate.getNow();
    AccessClassification result = parameterizedEvaluator.evaluate(date, transaction, uavEntity, truckEntity);

    if (result != AccessClassification.ACCESS_EXISTS) {
        System.out.println("No access found!");
    }
}));

Note that we create two EntityParameter<ExampleEntity> instances: one to represent the UAV and one to represent the truck. Each parameter is then used to construct the corresponding platform, and build the access query as before. This can be extended to include as many platforms and entities as needed.

Evaluator parameterization works with any evaluator available in STK Components, and can be useful in any situation where performing a calculation needs to vary with respect to additional inputs beyond time.