Skip to content

Implementation guide

kolaf edited this page Jan 5, 2024 · 4 revisions

Implementing a new task type

Implement a new task type can be quite simple or quite complex depending on is how different it is from the existing task types in ASLT. In general it consists of the following steps.

Define the new navigation task type

In navigation_task_type_definitions.py define a new task type and add it to the NAVIGATION_TASK_TYPES list.

Create a scorecard

The scorecard defines the navigation task type. After creating a new navigation task type definition, create a new scorecard similar to the ones defined in default_scorecards and add its creation function to create_scorecards in create_scorecards.py to ensure that it is created whenever the application is started. As mentioned in the architecture overview, there is this a scorecard for each navigation task type that defines all the parameters and default values, and when creating a navigation task a copy of this scorecard is created and the user is permitted to change certain elements of the scorecard. There are two entries in the scorecard that are important to update, task_type and calculator. The "calculator" field determines which gatekeeper is created, while task_type controls how the task is rendered in the navigation maps and the live tracking map.

Create the route

The routes used by navigation tasks are constructed from EditableRoute objects. An EditableRoute is created and modified through the route editor and currently supports the following elements:

  • 0 or more takeoff gates
  • 0 or more landing gates
  • Exactly one track. The track has to have a starting point, a finish point, and 0 or more waypoints in between. There is some experimental support for unknown legs in the track, but this is not well documented and will probably be changed.
  • 0 or more polygons of types information, penalty, prohibited, or gate.
    • Information polygons show with a blue shade and displays the polygon name as information on the map. Flying through these has no effect.
    • Penalty zones show as orange on the map and flying through these incurs a penalty per second inside the polygon.
    • Prohibited zones show as red on the map and flow through these incurs a fixed penalty per entry.
    • Gate polygons have to wrap a waypoint in the track. The purpose of these is to have a wider definition of a gate than the straight-line that is typically employed. Currently this is only utilised in the poker run task type to relax the requirements for hitting the gate.

The Route object is generated from the EditableRoute, and its construction depends on the navigation task type selected.

There is a basic assumption in the system that any route has a track. The route editor is therefore only able to draw a single track with waypoints in order, and there is an implicit order between waypoints in the resulting route. The main elements of the Route objects are:

  • A list of waypoints that make up the track.
  • A list of take off gates
  • A list of landing gates
  • Other information that is relevant for precision or ANR style routes.

The waypoints contain information about distance and bearing from the previous and to the next waypoint, as well as the width of the gates, the type of the gate, et cetera. All this is defined by the Waypoint object. The type of route to be constructed is selected in the create_route function of the EditableRoute. Expand this function to create the correct Route object for the navigation task type. You can either use one of the existing recreation mechanisms (e.g. precision or ANR), or create a new one modelled on one of the others.

Adding new features to EditableRoute

Certain task types do not care about waypoint order in a track. An example of this is a waypoint hunt where the goal is to reach as many waypoints as possible within a certain time limit or fuel limit. There is no problem emulating this with the current track support in EditableRoute, but creating a continuous track is not intuitive for this kind of task. We should therefore consider adding individual waypoints to the route editor that can be used instead of a continuous track. however, note that removing the requirement that there must be a track, we must be careful with what kind of navigation tasks we can create based on the different EditableRoutes. While it is possible to create a waypoint hunt task by ignoring the enforced order on a track, it is not possible to create a precision task based on an unordered list of waypoints.

Adding a scoring engine for the new task type

To add a scoring engine for a new task type implement a subclass of Gatekeeper. The abstract super class contains much of the boilerplate required to create the takeoff and landing gates if they are included in the Route, and also creates a list of gates based on the waypoint list in the Route object. The gatekeeper takes an optional list of calculators which I used to score a common elements such as penalty zones. So, if you're task type has support for penalty zones, simply include the penalties on calculator when instantiating the gatekeeper. Creating the gatekeeper is performed by the calculator_factory which needs to be updated to return the correct gatekeeper subclass for your new navigation task.

Scoring is implemented in the check_gates function which must be overridden. GatekeeperRoute is a complex gatekeeper that keeps track of gates that must be crossed in sequence, while GatekeeperLanding is an example of a very simple gatekeeper which simply assigns one points every time the landing gate is crossed.

To implement something like a waypoint hunt, for instance, instead of checking for the contestants crossing the gate lines in order the gatekeeper can calculate the distance to the centre position of each gate every time a new position is received. If this distance is below a certain threshold (e.g. 0.5NM) the gate can be considered crossed and points/penalties can be assigned accordingly.

Updating the contestants core is an asynchronous process to keep away any noticeable lag from the front end caused by all the database updates that are triggered. The Gatekeeper provides a helpful function update_score which accepts a UpdateScoreMessage where you enter the relevant scoring details. This will push everything to the database and the front end.

Rendering a new navigation task

There are two situations where a navigation task needs to be rendered. The first is the navigation map that can be generated for each contestant to use during the task. The second is the live tracking map.

Rendering the navigation task navigation map

Creating the navigation task map mishandled by the plot_route method. It uses several helper functions such as plot_precission_track, plot_anr_corridor_track and so on that handle plotting the exact representation of the route for the navigation task. For the new navigation task type either choose one of the existing plotters, or create a new one. For instance, when creating a navigation task type for a waypoint hunt, you probably want to plot each waypoint as a circle with the correct radius that corresponds to the detection limit used by the gatekeeper.

Rendering the navigation task live tracking map

Rendering the live tracking map is handled by a React module. The ConnectedNavigationTask component render components based on the task_type defined in the scorecard. The existing track renderers are implemented in the trackRenderers folder. Depending on the requirements of the navigation task it is possible to reuse existing track renderers, or maybe it is necessary to create a new one. For instance, for the waypoint hunt example it is necessary to create a new track renderer which is similar to the navigation task renderer and displays the waypoints with range circles instead of a track with lines between the waypoints.

Closing notes

When designing a task as it is important to decide whether points are earned by achieving something, or if bad behaviour is penalised. For instance, crossing a gate in a precision task incurs a penalty depending on how far off in time the crossing is. However, a waypoint hunt will probably want to award points when a waypoint is crossed. This is also the case for the poker run task. Both types are supported and the sorting direction ( i.e. is a high-scoring good or bad) is set when the user creates the navigation task. This should at some point be modified to be controlled by the scorecard so that the user does not need to care.

Translations

Translations are currently not implemented, so adding translations requires some extra work. Both the back-end (Django)and the front end (Django,Javascript,React) support translations. In Django this is done by wrapping the strings to be translated in explicit calls to gettext() as per the documentation. These marked strings will be exported to translatable files using the matemessages Django management command.

Similarly, React supports translation through react-i18next. this has to go through the same process of finding the text to be translated, wrapping in appropriate function calls, and adding the is required translation files.

This is a bit cumbersome to begin with, but once the work has started, adding additional strings to the translation, and additional languages should be quite straightforward.