Howto:Write simple scripts in Nasal

From FlightGear wiki
Jump to navigation Jump to search

Nasal is a scripting language available in FlightGear which opens up a world of possibilities for aircraft modellers - much more detailed information can be found elsewhere.

If you have any Nasal specific questions, you will want to check out the Nasal FAQ and our Nasal forum. Feel free to ask new questions or help answer or refine existing ones.

Here, though, as a simple example to get you started, we will create a very basic script to convert engine temperatures from degF to degC, as required by the Fokker 50 instruments.

Such a script should ideally be very generic so that it can be reused easily; but the script here is deliberately kept as simple as possible.

Creating the nasal file

As with many other things in FG, nasal functions can live in many different places. Here, however, we're going to create a "Systems" directory under the Main fokker50 directory. Later, other files might be added to this directory; an electrical system, functions to control other cockpit indicators, etc.

Within that new fokker50/Systems directory then, create an empty plain text file, we'll call it "degftodegc.nas". Note that many different functions can live in the same .nas file; this could be renamed to something more generic later if other similar functions are added to it in time.

It's always nice to start with a helpful comment at the top of the file;

# Converts degF to degC

Now, open our function;

var convertTemp = func{

What we need to do first is to read in the engine temperature (in degF) from the relevant place in the property tree. We'll create a local variable for that;

  var degF = getprop("engines/engine[0]/egt_degf");

Next, we need to do the actual conversion process.

  var degC = (degF - 32) * 5/9;

That seems pretty self-explanatory (but note the trailing semicolon at the end of each line of the function - easy to forget!)

Now that we have a variable in our script containing the temperature in degreesC, we will want to write it into a suitable place in the property tree to allow our instruments to make use of it;

  setprop("engines/engine[0]/egt-degc", degC);

This property didn't exist before, but will be created and the value held in our "degC" variable will be written there. Now that our script seems quite complete we can close the function with a closing curly brace

}

Finally, your code should look like this:

# create a new variable named "convertTemp", assign a function body to it
var convertTemp = func {
 # create a new variable named "degF" and assign the value from the getprop() call to it
 var degF = getprop("engines/engine[0]/egt_degf");
 # create a new variable named degc and compute the value using the degF variable
 var degC = (degF - 32) * 5/9;
 # finally, write the result to the property tree using the setprop() call
 setprop("engines/engine[0]/egt-degc", degC);
}

Including the nasal file in the -set file

To make sure our aircraft "knows" about this nasal file, we will need to add a section to the main aircraft -set.xml (in this case, fokker50-set.xml of course).

Here's the relevant XML, which I put at the bottom immediately above the closing </propertylist>:

 <nasal>
  <fokker50>
   <file>Aircraft/fokker50/Systems/degftodegc.nas</file>
  </fokker50>
 </nasal>

Crunch Time, or, testing our function

Now, we can start FlightGear to test our new function. Start FG in the usual manner with the appropriate aircraft.

Now, call up the "nasal console" from the debug menu (only available in recent versions of FG.) In a blank tab, type

fokker50.convertTemp();

and press execute. (The "fokker50" part comes from the entry we made in the -set xml file)

Nasal console.jpg

You can see in the property browser (file/browse internal properties) that we now have a new egt property for the first engine (/engines/engine), with the value in degC - success!

However, you will also see that it doesn't change, unless you execute the function again. We need to add some more to our script in order to have it run continuously; add this to the end of your function (before the closing curly brace though!)

  settimer(convertTemp, 0.1);

Settimer will call the convertTemp function, and the second argument determines the frequency with which it does so. If your function requires as rapid a response as possible, use 0.

Now, our function, once called, will constantly update the degC property - but we still have to call it manually. In order to start the function, we'll use a "listener"; this "watches" a given property and calls the desired function whenever that property is written to. This is an ideal, efficient method to use for things like switches which don't change state very often.

Place this AFTER the close of the convertTemp function:

setlistener("sim/signals/fdm-initialized", convertTemp);

This listens for the fdm-initialized property to be set (this will happen when the sim has started up), and starts our convertTemp function when that happens.

Finishing up

As an "exercise for the reader" (I always hate those!) you should make the script also convert the temperature values for the second engine.

Now you should be able to take advantage of the more detailed information available about nasal here on the wiki, at www.plausible.org/nasal, and also other pre-existing nasal scripts which can be useful to borrow from.