Howto:Methods to replace the NASAL code with JSBSim code

From FlightGear wiki
Jump to navigation Jump to search

Often may be convenient not mix NASAL functions with JSBSim functions, both for having a code easier to maintain, but also for greater efficiency in terms of execution. JSBSim is a data driven language ( Data-driven programming is similar to event-driven programming, in which both are structured as pattern matching and processing, and are usually implemented by a loop, though they are typically applied to different domains from: Wikipedia Data-driven programming). Having noticed that in Flightgear NASAL is often used instead of JSBSim because often do not know the potential of JSBSim implemented in Flightgear, I think it is useful to put notes on the various methods that can facilitate this work.

Note: This how to requires from the reader a knowledge of basic programming elements in JSBSim. For those not familiar with JSBSim it is recommended to study the publication: JSBSim Reference Manual

Performance of JSBSim (or data-driven languages) in a real-time (RT) context

For many programmers accustomed to the procedural language classes with event-driven extensions, they think that the "data driven" approach can be more expensive because for each step-time all the variables are read and the related transformation processes are performed. At 120 Hz (recalculation rate typical of JSBSim in Flightgear) it can actually seem a very expensive process, but without using the particular optimization parameters (anyway present in JSBSim), the machine cycles necessary to perform a complete recalculation of all the variables presently present an aircraft of Flightgear are few because it is not necessary, in these languages, to perform an interpreter and to activate methods of management of interruptions, timers etc ... But everything remains confined within a vector of variables that is read and transformed according to the implemented algortms. Operatively, it is observed that the CPU load, due to the JSBSim applications, is still quite low because the main-loop mechanism is efficient (a mechanism typically used for real-time programming).

CPU load reduction by execrate option

A classic criticism that is made of an extensive use of JSBSim in terms of a useful replacement of NASAL code is that JSBSim executes its modules at a frequency of 120 Hz, whereas NASAL executes its modules according to the frame-rate, which on average it is 20-30 Hz. This difference may seem very high and therefore make extensive use of JSBSim too heavy.
In fact, as explained above, JSBSim is very light in execution and its percentage of CPU occupancy is very low (on average only some percentage value ...). But it is possible to reduce the CPU load even more on the individual groups of modules (called Channel) by use a declarative option called execrate. The method used by execrate to do this is to execute a module only after a certain number of cycles, the number of wait cycle less one is defined as parameter in the execrate declaration.

Let's take an example that should clarify the concept:

    <channel name="jato electric" execrate="8">
        <switch name="systems/jato/electric-active">
            <default value="0"/>
            <test logic="AND" value="1">
                systems/electric/bus[1]/V GT 18
                systems/electric/bus[1]/JATO-ignition/fuse == 1
            </test>
        </switch>
    </channel>

The declaration: <channel name="jato electric" execrate="8"> defines that every eight cycles the modules inside the channel will be executed.
In this way it is possible to considerably reduce the CPU load, but we must remember that:

The modules executed with active execrate are evaluated with a lower frequency of the modules without execrate and this fact could create collateral problems.

A typical case is when an output variable present in the module that do not belong to the same channel. In this case is it possible generate problems of incorrect synchronization that can lead to mysterious malfunctions. Another case is when the output variable writes on a variable that is also updated by the Flightgear properties tree (variables starting with "/", for example the variable: /controls/engines/engine/throttle) in this case it is probable that the variable can randomly updated by Flightgear and not by JSBSim, this generating a substantial uncertainty in its value that could lead to unpredictable behavior. However, this is a typical problem of concurrent processes that must be managed with appropriate measures.

Using the variables defined in the Flightgear properties tree, by JSBSim

The implementation of JSBSim for Flightgear contains an easy way to assign or read values to the variables in the Flightgear properties tree:

All variables starting with / are with absolute references, referring to the Flightgear properties tree where JSBSim is a subset. For example, the variable: /controls/engines/engine[1]/throttle define the throttle value for engine 1. If the variables not start with the character / is a variable with relative references, for example : fcs/throttle-cmd-norm[1] which is the equivalent in JSBSim of the variable normally used by Flightgear (and NASAL) /controls/engines/engine[1]/throttle. Vice versa, in XML scripts it is possible to refer to a JSBSim variable simply by inserting the suffix: fdm/jsbsim/...

Summarise:

In NASAL or in Flihtgear XML is equivalent:

/controls/engines/engine[1]/throttle -> fdm/jsbsim/fcs/throttle-cmd-norm[1]

In JSBSim is equivalent:

fcs/throttle-cmd-norm[1] -> /controls/engines/engine[1]/throttle

A complete example of changing the throttle value from JSBSim

The problem is due to the fact that there is a binding between the Flightgear application and the JSBSim implementation. This means that the variable fcs/throttle-cmd-norm[n] (where n is the engine number) is continuously updated by Flightgear and therefore can not be changed directly. Unfortunately, the implementation of JSBSim for Flightgear does not currently have the ability to silence the fcs/throttle-cmd-norm[n] variable synchronization function with the corresponding Flightgear variable /controls/engines/engine[n]/throttle So it's better to play cunning and edit the Flightgear assign /controls/engines/engine[n]/throttle directly by assigning the value that we would like to assume fcs/throttle-cmd-norm[n] at this point everything works perfectly.

For example, to activate a solid fuel rocket engine it is sufficient to set (even for a moment) the variable: /controls/engines/engine[n]/throttle to 1 via a switch method or a function fcs_function this is the code I used:

<switch name="throttle cmd norm[1]">
    <default value="0"/>
    <test logic = "AND" value = "1">
        systems/jato/ignition-on == 1
        systems/jato/rocket_number_1 == 1
    </test>
    <output>/controls/engines/engine[1]/throttle</output>
</switch>

This switch function reminds us of two important things to know: If in JSBSim for Flightgear a variable starts with the character / it means that it is a variable that refers to the properties tree of Flightgear, this means that the variable /controls/engines/engine[1]/throttle can be so assigned or read directly by JSBSim. The Switch function that I put in the example can not be defined with the name or tied properties (which in the example has become only a generic reference), but the assignment must be done only through the <output> ... </output> method. If instead a reference to a variable does not start with the / character, then that variable has a reference relative to the JSBSim properties tree only (a sort of local variable).

Switches with animation

The switches with a gentle rotation movement are not managed directly by the animation <rotate> , but by a property that refers to a variable that changes its value over time, thus generating the animation effect. This may seem like a complication, but in the end it allows you to manage the effect very precisely. For example, an on-off switch (Togle) is realized and a switching time of 1 second is desired, the switch will actually be in active state when it has reached 90% of its final positioning. In this case, having a fine code management is essential and JSBSim allows you to achieve all this with simplicity and efficiency.

Define the switch object in XML property list to obtain a togle-animated switch

The animation of the switch in the <PropertyList> is made of two parts:

  1. Animation type pick to detect the switching action
  2. Animation type rotate to perform the switching action

The animation type pick is the trigger to activate the action and the property fdm/jsbsim/systems/switches/sw-test is the memory status that is placed directly inside the property list in the fdm/jsbsim area in order to have a location that is easy to find. It is possible to note that it is convenient to set up sub-folders (../switches) so that these properties are grouped together so that the code can be read more easily. The tags: <repeatable>false</repeatable> and <interval-sec>0.5</interval-sec> are essential because they avoid the rebound effect, the reason is due to the fact that the JSBSim code is executed with a different clock than the XML that defines the switch in the ProperyList, so it is possible that the state of the switch changes when the JSBSim code has not yet been executed, generating a sort of virtual rebound that would make the change in status aleatory.

<animation>
    <type>pick</type>
    <visible>true</visible>
    <object-name>switch_sensitive_area</object-name>
    <action>
        <button>0</button>
        <repeatable>false</repeatable>
        <interval-sec>0.5</interval-sec>
        <binding>
            <command>property-toggle</command>
            <property>fdm/jsbsim/systems/switches/sw-test</property>
        </binding>
    </action>
</animation>

After is define the animation type rotate, this animation binds a property that varies from 0 to 1 calculated by the JSBSim application. For convenience of reading the name of this variable is defined with the postfix -pos (position).

A small note: in the example I have separated the part: mobile switch_lever from the sensitive area: switch_sensitive_area because our desire is to generalize the code, often the switch_lever can also be part of the sensitive area. The programmer will then choose the solution he finds most suitable.
<animation>
    <type>rotate</type>
    <object-name>switch_lever</object-name>
    <property>fdm/jsbsim/systems/switches/sw-test-pos</property>
    <factor>-25</factor>
    <!-- other tags are optional -->
</animation>

JSBSim code to obtain a togle-animated switch

At this time we do not give particular advice on how to organize the JSBSim code, but it is assumed to use a specialized channel for the switches placed in a System area, this means that the code can be organized in this way:

<system name="Manual switches"> 

    <property value="0">systems/switches/sw-test-togle</property>
    <!-- another memory status properties -->

    <channel name="switchs" execrate="xy">
        <!-- JSBSim action code -->
    </channel>

</system>

The tag <system name="Manual switches"> defines the module called manual switch which contains the channels that manage manual switches. This is just one of the possible ways to organize the code. However, since the JSBSim code is very wordy (a typical fact in programming codes with XML syntax), it is convenient to subdivide the code into sub-folders and/or into separate files. From the point of efficiency in the execution of the code does not change much, but it makes the code much easier to maintain. Every JSBSim property and object is visible to the whole JSBSim code, this is certainly convenient, but it is better to always write a very structured code in order to be able to effectively maintain the program in the future!

The tag: <property value="0">fdm/jsbsim/systems/switches/sw-test-togle</property> defines the memory of the state of the circuit breaker that is written, through a binding, execute by a specializzate Flightgear program, that combines the XML of the PropertyList with the JSBSim code.

The tag: <channel name="switchs" execrate="xy"> where xy is a numerical value, for example 8, 10 ... 16, which we have explained above. This part of the JSBSim code can be executed with a lower frequency, such as 8 (120/8 = 15 fps) which allows to obtain a fairly fluid animation, without affecting the system's performance too much. If the value is higher, it will be lower the refresh frequency and therefore could make rough the effect of switch movement.

Finally now we enter the innermost part of the JSBSim code, the one that performs the actions:

<system name="Manual switches"> 
    <property value="0">systems/switches/sw-test-togle</property>
    
    <channel name="switchs" execrate="8">

        <lag_filter name="systems/switches/sw-test-pos">
            <input>systems/systems/switches/sw-test-togle</input>
            <c1>1.0</c1>
        </lag_filter>

        <switch name="systems/switches/sw-test-on">
            <default value="0"/>
            <test logic="AND" value="1">
                systems/switches/sw-test-pos GE 0.9
            </test>
        </switch>
    
    </channel>
</system>

Analyze the tag: <lag_filter name="systems/switches/sw-test-pos">:
This tag defines the JSBSim lag_filter method which has the effect of varying the output status (filter name, e.g "systems/switches/sw-test-pos") from a certain initial value to a final value (defined in the tag <input> ... </input>) with an acceleration/deceleration defined by the parameter defined in the tag <c1> .. </ c1>. It may seem strange that we define the movement through a concept of acceleration/deceleration instead speed, but in reality it is! In fact, if we move a switch, we apply a constant force which, from the kinematic point of view, is like applying constant acceleration! This is why the movement of the JSBSim drive button looking so natural. It starts at a certain speed and then slow down towards the end of the movement. This means that the property systems/switches/sw-test-pos will never assume a final fixed value, but will continue to converge towards its final value. This fact introduces the need to insert a subsequent <switch> .. </ switch> method that acts as a filter.

Analyze the tag: <switch name="systems/switches/sw-test-on">:
This is the switch, one of the most important methods of a data-driven language like JSBSim, the switch has many functions, but also an essentially simple structure. In this case its function is very simple, it must set its value to 1 when the position of the switch lever is at 90% (switch on area). The tag: <default value="0"/> define the systems/switches/sw-test-on if the test switch failed. The tag: systems/switches/sw-test-pos GE 0.9 is the test condition GT (Greater Than) compared to a target value 0.9 If the test was successful, then the function returns the value 1 (<test logic="AND" value="1">)

This also means that it is possible to lengthen the action time of the switch in two ways:

  1. By decreasing the deceleration parameter C1, in this case we will see the switch proceed more slowly.
  2. Increasing the test value of the switch function from 0.9 to 0.99 or higher in order to simulate the reaction time, for example, of a relay or an actuator mechanism. In this case we will see the switch in its final position, but we will see a delay in the execution of an action.

In the end, although it may seem complicated to perform the animation of a switch via JSBSim, the example allowed us to see the potential of the method in order to obtain a very realistic effect, with an executive cost, with the same visual quality, much lower than what would be required in NASAL. The written code is very simple to read and to maintain as it does not contain timers or interrupt states.