Click or drag to resize

Code Sample

The following is a series of code samples that create a simple route and illustrate use of the Route Design Library.

Note Note

The functionality described in this topic requires a license for the Route Design Library.

Setting Up Dynamics and Defaults for the Route

The following example sets up the ProfileSpeed and ProfileDynamics for the route as well as the DefaultConnectionBehavior. The heights, speeds, and dynamics can be specified independently for each profile. A different profile can be used for different procedures, but for simplicity one set of values is used here to represent a constant height and constant speed cruise. If different heights and speeds are specified for the profiles, the connection segments set on the RoutePropagator (or added specifically by the user) will transition smoothly between them.

Java
ProfileSpeed cruiseSpeed = new ProfileSpeed(30.0, SpeedReference.TOTAL); // ~67 miles per hour

ProfileDynamics profileDynamics = new ProfileDynamics();
double inclineAngle = Trig.degreesToRadians(10.0);
profileDynamics.setAscentSpeed(cruiseSpeed.getTargetSpeed() * Math.sin(inclineAngle));
profileDynamics.setDescentSpeed(1.5 * profileDynamics.getAscentSpeed());
profileDynamics.setPitchUpAcceleration(0.4 * Constants.EarthSurfaceGravity);
profileDynamics.setPushOverAcceleration(0.6 * Constants.EarthSurfaceGravity);
profileDynamics.setThrustAcceleration(0.4 * Constants.EarthSurfaceGravity);
profileDynamics.setThrustDeceleration(0.6 * Constants.EarthSurfaceGravity);

RoutePropagator propagator = new RoutePropagator();
propagator.setCentralBody(earth);

propagator.getDefaultConnectionBehavior().setSurfaceShape(localTerrain.getShape());
propagator.getDefaultConnectionBehavior().setKindOfSurfaceConnection(KindOfSurfaceConnection.RHUMB_LINE);
propagator.getDefaultConnectionBehavior().setDynamics(profileDynamics);
propagator.getDefaultConnectionBehavior().setSpeed(cruiseSpeed);

if (useTerrainForProfiles) {
    propagator.getDefaultConnectionBehavior().setKindOfProfile(DefaultProfileBehavior.AVOID_TERRAIN);
    propagator.getDefaultConnectionBehavior().setTerrainSurface(localTerrain);
    propagator.getDefaultConnectionBehavior().setTerrainSamplingDistance(100.0); // ~328 feet
    propagator.getDefaultConnectionBehavior().setMinimumHeightAboveTerrain(400.0); // ~1312 feet
} else {
    propagator.getDefaultConnectionBehavior().setKindOfProfile(DefaultProfileBehavior.STANDARD_CONNECTION);
    propagator.getDefaultConnectionBehavior().setTerrainSurface(new EllipsoidTerrainProvider(earth.getShape(), earth.getFixedFrame()));
}
Setting up the Takeoff Procedure

The following example sets up a TakeoffProcedure. The 'profile' for the takeoff is included in the procedure and no additional profile is needed. Instead, the user specifies the behavior of the takeoff directly on the procedure and the procedure produces the corresponding profile. Note that the angle specified for the takeoff will override the values for the ascent velocity in the dynamics specified above but will still use the pitch accelerations.

Java
TakeoffProcedure takeoff = new TakeoffProcedure();
takeoff.setRunwayReferenceSurface(localTerrain);
takeoff.setDynamics(profileDynamics);
takeoff.setTurningRadiusBeforeTakeoff(10.0); // radius for taxiing onto the runway
takeoff.setTurningRadiusAfterTakeoff(400.0); // ~1312 feet
takeoff.setRunwayStartPosition(runway.getInitialPoint());
takeoff.setRunwayHeading(runway.getHeading());
takeoff.setInitialSpeed(1.0); // ~2.2 mph
takeoff.setTakeoffSpeed(20.0); // ~44.7 mph
takeoff.setRollDistance(runway.getSurfaceDistance() * 0.75);
takeoff.setClimbAngle(inclineAngle);
takeoff.setInitialHeightAboveRunway(groundHeight);
takeoff.setFinalHeightAboveRunway(300.0); // ~1000 feet
propagator.getSegments().add(takeoff);
Setting up the Route Profiles

The following example sets up the Profiles for the route. Depending on whether the user cares about avoiding terrain, the profile used for the procedures is either one that maintains a minimum height above the terrain surface or simply transitions smoothly. Note how the StandardTransitionProfile allows the user to specify whether its boundary conditions are fixed or whether they can be modified by the propagator. If the values for these flags are set to true, the connection profiles will be able to update the heights in the event that the route cannot reach the desired height without violating the ProfileDynamics specified above. Otherwise, if the geometry is infeasible, the system may produce a configuration error. See the section on configuration below for more information.

Java
RouteProfile procedureProfile;
cruiseHeight = waypoints.get(0).getHeight();
if (useTerrainForProfiles) {
    double minimumHeightAboveTerrain = 200.0; // ~656 feet
    double samplingDistance = 100.0; // ~328 feet
    TerrainAvoidanceProfile profile = new TerrainAvoidanceProfile();
    profile.setTerrain(localTerrain);
    profile.setMinimumHeightAboveTerrain(minimumHeightAboveTerrain);
    profile.setSamplingDistance(samplingDistance);
    profile.setInitialHeightAboveTerrain(cruiseHeight - terrainBaseHeight);
    profile.setFinalHeightAboveTerrain(cruiseHeight - terrainBaseHeight);
    profile.setDynamics(profileDynamics);
    profile.setSpeed(cruiseSpeed);
    procedureProfile = profile;
} else {
    StandardTransitionProfile profile = new StandardTransitionProfile();
    profile.setDynamics(profileDynamics);
    profile.setInitialHeight(cruiseHeight);
    profile.setFinalHeight(cruiseHeight);
    profile.setCruiseSpeed(cruiseSpeed);
    profile.setHeightReferenceSurface(new EllipsoidTerrainProvider(earth.getShape(), earth.getFixedFrame()));
    profile.setAllowModificationOfInitialHeight(false);
    profile.setAllowModificationOfFinalHeight(false);
    procedureProfile = profile;
}
Setting up the Turn Procedures

The following example configures the various turn procedures. Primarily, these provide a way of specifying waypoints for a route while differentiating how to handle the turn that occurs at the waypoint.

Java
TurnAfterWaypointProcedure turnAfter = new TurnAfterWaypointProcedure();
turnAfter.setProfile(procedureProfile);
turnAfter.setSurfaceShape(earth.getShape());
turnAfter.setTurningRadius(turningRadius);
turnAfter.setWaypoint(waypoints.get(wpIndex++));
propagator.getSegments().add(turnAfter);

TurnBeforeWaypointProcedure turnBefore = new TurnBeforeWaypointProcedure();
turnBefore.setProfile(procedureProfile);
turnBefore.setSurfaceShape(earth.getShape());
turnBefore.setTurningRadius(turningRadius);
turnBefore.setWaypoint(waypoints.get(wpIndex++));
propagator.getSegments().add(turnBefore);

InscribedTurnProcedure turnShort = new InscribedTurnProcedure();
turnShort.setProfile(procedureProfile);
turnShort.setSurfaceShape(earth.getShape());
turnShort.setTurningRadius(turningRadius);
turnShort.setWaypoint(waypoints.get(wpIndex++));
propagator.getSegments().add(turnShort);
Setting up the Holds and Search Patterns

The following example configures the various holding and search patterns. These represent procedures that occur over a particular area of interest. The entry points and exit points will depend on the previous and next procedures.

Java
ConstantHeightProfile levelProfile = new ConstantHeightProfile();
levelProfile.setHeight(cruiseHeight);
levelProfile.setHeightReferenceSurface(localTerrain);
levelProfile.setThrustAcceleration(profileDynamics.getThrustAcceleration());
levelProfile.setThrustDeceleration(profileDynamics.getThrustDeceleration());
levelProfile.setSpeed(cruiseSpeed.getTargetSpeed());
levelProfile.setAllowVariationInHeight(false);
levelProfile.setAllowVariationInSpeed(false);

CircularHoldProcedure circularHoldProcedure = new CircularHoldProcedure();
circularHoldProcedure.setProfile(levelProfile);
circularHoldProcedure.setSurfaceShape(earth.getShape());
circularHoldProcedure.setTurningRadius(turningRadius);
circularHoldProcedure.setCenter(waypoints.get(wpIndex++));
circularHoldProcedure.setTurnDirection(ClosedTurnDirection.RIGHT);

// For a hold, either specify minimum revolutions, minimum time, or both
// The maximum of the two will determine the time in the hold
circularHoldProcedure.setMinimumTime(Duration.fromSeconds(300.0)); // 5 minutes
circularHoldProcedure.setMinimumRevolutions(1.0);
propagator.getSegments().add(circularHoldProcedure);

Cartographic corner1 = waypoints.get(wpIndex++);
Cartographic corner2 = waypoints.get(wpIndex++);
Cartographic side = waypoints.get(wpIndex++);
EllipsoidRhumbLine line = new EllipsoidRhumbLine(earth.getShape(), corner1, corner2);
Cartographic centerSide = line.interpolateUsingFraction(0.5);
double heading = line.getHeading();
EllipsoidRhumbLine line2 = new EllipsoidRhumbLine(earth.getShape(), corner2, side);
double width = line2.getSurfaceDistance();
double heading2side = line2.getHeading();
if (Trig.getSmallSpan(heading, heading2side) > 0) {
    heading2side = heading + Constants.HalfPi;
} else {
    heading2side = heading - Constants.HalfPi;
}
Cartographic centerOne = new EllipsoidRhumbLine(earth.getShape(), corner1, heading2side, width / 2.0).getFinalPoint();
Cartographic centerTwo = new EllipsoidRhumbLine(earth.getShape(), corner2, heading2side, width / 2.0).getFinalPoint();

RacetrackHoldProcedure racetrack = new RacetrackHoldProcedure();
racetrack.setProfile(levelProfile);
racetrack.setSurfaceShape(earth.getShape());
racetrack.setCenterOfTurnOne(centerOne);
racetrack.setCenterOfTurnTwo(centerTwo);
racetrack.setTurningRadiusForHold(turningRadius * 2.0);
racetrack.setTurningRadiusForEntryAndExit(turningRadius);
racetrack.setTurnDirection(ClosedTurnDirection.LEFT);
// For a hold, either specify minimum revolutions, minimum time, or both
// The maximum of the two will determine the time in the hold
racetrack.setMinimumTime(Duration.fromSeconds(300.0)); // 5 minutes
racetrack.setMinimumRevolutions(1.0);
racetrack.setConstrainEntryAndExitToTrackArcs(false);
propagator.getSegments().add(racetrack);

corner1 = waypoints.get(wpIndex++);
corner2 = waypoints.get(wpIndex++);
side = waypoints.get(wpIndex++);
line = new EllipsoidRhumbLine(earth.getShape(), corner1, corner2);
centerSide = line.interpolateUsingFraction(0.5);
heading = line.getHeading();
double length = line.getSurfaceDistance();
line2 = new EllipsoidRhumbLine(earth.getShape(), corner2, side);
width = line2.getSurfaceDistance();
heading2side = line2.getHeading();
if (Trig.getSmallSpan(heading, heading2side) > 0) {
    heading2side = heading + Constants.HalfPi;
} else {
    heading2side = heading - Constants.HalfPi;
}
Cartographic center = new EllipsoidRhumbLine(earth.getShape(), centerSide, heading2side, width / 2.0).getFinalPoint();

ConstantHeightProfile higherCruiseHeight = new ConstantHeightProfile(cruiseSpeed.getTargetSpeed(),
        Constants.EarthSurfaceGravity, Constants.EarthSurfaceGravity, cruiseHeight + 500.0, RouteHeightReference.SURFACE_SHAPE);

RasterSearchProcedure raster = new RasterSearchProcedure();
raster.setSurfaceShape(earth.getShape());
raster.setCenter(center);
raster.setHeadingAlongLength(heading);
raster.setWidth(width);
raster.setLength(length);
raster.setTurningRadius(turningRadius);
raster.setProfile(higherCruiseHeight);
propagator.getSegments().add(raster);

double finalHeight = heightOnFinalApproach + terrainBaseHeight -
        localTerrain.getHeightRelativeToShape(waypoints.get(wpIndex).getLongitude(), waypoints.get(wpIndex).getLatitude());
Setting up the Landing Procedure

The following example configures the LandingProcedure. The profile for the landing is included in the procedure. So no additional profile is specified. Instead, the user specifies the behavior of the landing directly on the procedure and the procedure produces the corresponding profile. Note that the angle specified for the landing will override the values for the descent velocity in the dynamics specified above but will still use the pitch accelerations.

Java
LandingProcedure landing = new LandingProcedure();
landing.setRunwayReferenceSurface(localTerrain);
landing.setRunwayStartPosition(runway.getInitialPoint());
landing.setRunwayHeading(runway.getHeading());
landing.setRollDistance(runway.getSurfaceDistance() * 0.75);
landing.setInitialHeightAboveRunway(heightOnFinalApproach);
landing.setSpeedOnApproach(cruiseSpeed.getTargetSpeed());
landing.setTurningRadiusOnApproach(turningRadius * 2.0);
landing.setDynamics(profileDynamics);
landing.setFinalHeightAboveRunway(groundHeight);
landing.setFinalSpeed(1.0);
landing.setDescentAngle(Trig.degreesToRadians(-10.0));
landing.setTurningRadiusAfterLanding(10.0); // radius used for taxiing off the runway
propagator.getSegments().add(landing);
Handling a "Taxi" Maneuver after Landing

The following example shows how it is possible to mix aircraft routes with ground vehicle routes. Here, the aircraft will taxi along the ground after landing. In order to do this, a custom connection is required to force the route to behave like a ground vehicle between the landing and the taxi procedure.

Java
ConstantHeightProfile taxiProfile = new ConstantHeightProfile();
taxiProfile.setHeight(groundHeight); // Account for the center of mass above the ground
taxiProfile.setSpeed(10.0); // ~20 miles per hour
taxiProfile.setHeightReferenceSurface(localTerrain);
taxiProfile.setAllowVariationInHeight(false);
taxiProfile.setAllowVariationInSpeed(false);

// Create a custom connection to override the aircraft behavior
// in order to act like a ground vehicle
ProfileRouteConnection taxiConnection = new ProfileRouteConnection();
taxiConnection.setKindOfSurfaceConnection(KindOfSurfaceConnection.RHUMB_LINE);
taxiConnection.setRouteProfile(taxiProfile);
taxiConnection.setSurfaceShape(earth.getShape());
propagator.getSegments().add(taxiConnection);

TurnBeforeWaypointProcedure finalWaypoint = new TurnBeforeWaypointProcedure();
finalWaypoint.setWaypoint(new Cartographic(Trig.degreesToRadians(69.2621550808261), Trig.degreesToRadians(34.9428441053572), 0.0));
finalWaypoint.setTurningRadius(10.0);
finalWaypoint.setProfile(taxiProfile);
finalWaypoint.setSurfaceShape(earth.getShape());
propagator.getSegments().add(finalWaypoint);
{
    
Propagating and Configuration Results

The following example shows how to propagate the route and check that it was configured correctly. If for some reason one or more of the procedures (or profiles) is unable to be configured such that it creates valid geometry within the user specified parameters, the propagator will produce an error in the ProcedureConfigurationResults (get) and ConnectionConfigurationResults (get) properties on the PropagatedRoute. Such cases include not being able to transition between one profile and another because their heights are too far apart for the specified ascent or descent velocity or also because the speeds specified require accelerations that exceed the thrust acceleration. The surface geometry can also cause problems, such as when the user mistakenly positions a circular hold such that the next waypoint lies inside of its radius.

Java
PropagatedRoute route = propagator.propagateFromTime(start);
if (route.getHasConfigurationErrors()) {
    for (ProcedureConfigurationResult error : route.getProcedureConfigurationResults()) {
        RouteProcedure problemProcedure = error.getProcedure();
        SurfaceConnectionStatus entryError = error.getSurfaceEntryResult().getConnectionStatus();
        SurfaceConnectionStatus exitError = error.getSurfaceExitResult().getConnectionStatus();
        HeightConfigurationStatus heightError = error.getProfileResult().getHeightConfigurationResult().getConnectionStatus();
        SpeedConfigurationStatus speedError = error.getProfileResult().getSpeedConfigurationResult().getConnectionStatus();
    }
}

Point trajectoryPoint = route.createPointFromRoute();
JulianDate routeStart = route.getStart();
JulianDate routeStop = route.getStop();
Route Geometry

The following example demonstrates some of the geometry types available with Route Design Library.

Java
PropagatedRoute route = propagator.propagateFromTime(start);

// Define geometry
double bankRate = Trig.degreesToRadians(5.0); // 5 deg/sec
AxesFromBankAngle aircraftAxes = new AxesFromBankAngle(route, Constants.EarthSurfaceGravity, bankRate);
Scalar routeHeight = new ScalarRouteHeight(route, localTerrain);
Scalar routeHeading = new ScalarRouteHeading(route);
Scalar routeTotalSpeed = new ScalarRouteTotalSpeed(route, localTerrain);
Scalar routeSurfaceSpeed = new ScalarRouteSurfaceSpeed(route);

// Create evaluators
EvaluatorGroup group = new EvaluatorGroup();
AxesEvaluator axesEvaluator = GeometryTransformer.getAxesTransformation(earth.getFixedFrame().getAxes(), aircraftAxes, group);
ScalarEvaluator heightEvaluator = routeHeight.getEvaluator(group);
ScalarEvaluator headingEvaluator = routeHeading.getEvaluator(group);
ScalarEvaluator totalSpeedEvaluator = routeTotalSpeed.getEvaluator(group);
ScalarEvaluator surfaceSpeedEvaluator = routeSurfaceSpeed.getEvaluator(group);

// Optimize for performance
group.optimizeEvaluators();
heightEvaluator = group.updateReference(heightEvaluator);
headingEvaluator = group.updateReference(headingEvaluator);
totalSpeedEvaluator = group.updateReference(totalSpeedEvaluator);
surfaceSpeedEvaluator = group.updateReference(surfaceSpeedEvaluator);

// Perform analysis here