Nasal library

From FlightGear wiki
Jump to: navigation, search


This page documents the global library functions of FlightGear's built-in scripting language, Nasal. This includes core library functions, which were included in Nasal before its integration into FlightGear, and extension functions, which have been subsequently added, and are specifically designed for FlightGear. The main relevant folders in Git are:

Tip  Copy & paste the examples into your Nasal Console and execute them to see what they do.

Core library functions

This is the list of the basic core library functions. Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time. See also:

append()

append(vector, element[, element[, ...]]);

Source

This function appends, or adds, the given element(s) to the end of the vector given in the first argument. Returns the vector operated on.
vector
The vector to which the arguments will be appended.
element
An element to be added to the vector.

Examples

var vector = [1, 2, 3]; # Initialize the vector
append(vector, 4); # Append the number 4 to the end of the vector
debug.dump(vector); # Print the contents of the vector
var vector = [1, 2, 3]; # Initialize the vector
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector
debug.dump(vector); # Print the contents of the vector

bind()

bind(function, locals[, outer_scope]);

Source

This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.
function
Function to evaluate.
locals
Hash containing values that will become the namespace (first closure) for the function.
outer_scope
Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.

Example

# This is a namespace/hash with a single member, named "key," which is initialized to 12
var Namespace = {
    key: 12
};

# This is different namespace/hash containing a function
# dividing a variable "key" (which is unavailable/nil in this namespace) by 2
var AnotherNamespace = {
    ret: func {
        key /= 2;
    }
};

# To see that key is not available, try to call AnotherNamespace.ret() first
call(AnotherNamespace.ret, [], nil, nil, var errors = []);
if(size(errors)){
    print("Key could not be divided/resolved!");
    debug.printerror(errors);
}

# Associate the AnotherNamespace.ret() function with the first namespace
# so that "key" is now available
var function = bind(AnotherNamespace.ret, Namespace);

# Invoke the new function
function();

# Print out the value of Namespace.key
# It was changed to 12 from 6 by AnotherNamespace.ret()
print(Namespace.key);

call()

call(func[, args[, me[, locals[, error]]]);

Source

Calls the given function with the given arguments and returns the result. This function is very useful as it allows much more control over function calls and catches any errors or die() calls that would normally trigger run-time errors cancelling execution of the script otherwise.
func
Function to execute.
args
Vector containing arguments to give to the called function.
me
me reference for the function call (i.e., for method calls). If given, this will override any me value existing in the namespace (locals argument).
locals
A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.
error
A vector to append errors to. If the called function generates an error, the error, place, and line will be written to this. These errors can be printed using debug.printerror().

Examples

# prints "Called from call()"
call(func {
    print("Called from call()");
});
# prints "a = 1 : b = 2
call(func(a, b){
        print("a = ", a, " : b = ", b);
    },
    [1, 2]
);
var Hash = {
    new: func {
        var m = { parents: [Hash] };

        m.el1 = "string1";
        m.el2 = "string2";

        return m;
    }
};

# prints "me.el1 = string1", then "me.el2 = string2" on the next line
call(func(a, b){        
        print("me.el", a, " = ", me["el" ~ a]);      
        print("me.el", b, " = ", me["el" ~ b]);
    },
    [1, 2],
    Hash.new()
);
# prints the value of math.pi
call(func {
        print(pi);
    }, nil, nil,
    math
);
call(func {
        print(math.ip); # math.ip doesn't exist
    }, nil, nil, nil,
    var errs = []
);
debug.printerror(errs); # The error is caught and printed using debug.printerror()

caller()

caller([level]);

Source

Returns a vector containing a record from the current call stack. The level numbering starts from the currently executing function (level 0). Level 1 (the default) is the caller of the current function, and so on. The result is a four-element vector containing a hash of local variables, the function object, the source, and the line number.
level
Optional integer specifying the stack level to return a result from. Defaults to 1 (the caller of the currently executing function.

Examples

var myFunction = func(a, b){
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b
    return 2 * 2;
};

print("2 x 2 = ", myFunction(2, 2));
var get_arg_value = func(){
    print("Argument to myFunc = ", caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()
};

var myFunc = func(a){
    get_arg_value();
};

myFunc(3);
This is a real example taken from fgdata/Nasal/canvas/MapStructure.nas. Function r() (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: { name: "<name>", vis: 1, zindex: nil }.
var MapStructure_selfTest = func() {
        var temp = {};
        temp.dlg = canvas.Window.new([600,400],"dialog");
        temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);
        temp.root = temp.canvas.createGroup();
        var TestMap = temp.root.createChild("map");
        TestMap.setController("Aircraft position");
        TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/
        TestMap.setTranslation(
                temp.canvas.get("view[0]")/2,
                temp.canvas.get("view[1]")/2
        );
        var r = func(name,vis=1,zindex=nil) return caller(0)[0];
        # TODO: we'll need some z-indexing here, right now it's just random
        # TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?
        # maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?
        foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] )
                TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
                                        visible: type.vis, priority: type.zindex,
                );
}; # MapStructure_selfTest

chr()

chr(code);

Source

Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems. For a list of the most commonly used characters, see the ASCII printable code chart This is a link to a Wikipedia article (Dec column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.
Note  In Nasal, only strings enclosed with double-quotes ("string") supports control chracters. Strings in single quotes ('string') do not.
Code Name Equivalent to
10 Newline This is a link to a Wikipedia article \n
9 Horizontal tab This is a link to a Wikipedia article \t
13 Carriage return This is a link to a Wikipedia article \r
code
Integer character code for the desired glyph.

Examples

print("Code 65 = ", chr(65)); # prints "Code 65 = A"
This example displays all of the characters in a list, in the format Code n = >char<, n being the index, and char being the character.
for(var i = 0; i <= 255; i += 1){
    print("Code ", i, " = >", chr(i), "<");
}

closure()

closure(func[, level]);

Source

Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of func.
func
Function to evaluate.
level
Optional integer specifying the scope level. Defaults to 0 (the namespace of func).

Example

var get_math_e = func {
    return e; # return the value of math.e
}

var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e
debug.dump(closure(myFunction)); # print the namespace of get_math_e

print(myFunction());

cmp()

cmp(a, b);

Source

Compares two strings, returning -1 if a is less than b, 0 if they are identical and 1 if a is greater than b.
a
First string argument for comparison.
b
Second string argument for comparison.

Examples

print(cmp("1", "two")); # prints -1
print(cmp("string", "string")); # prints 0
print(cmp("one", "2")); # prints 1
print(cmp("string1", "string2")); # prints -1

compile()

compile(code[, filename]);

Source

Compiles the specified code string and returns a function object bound to the current lexical context. If there is an error, the function dies, with the argument to die() being filename.
code
String containing Nasal code to be compiled.
filename
Optional string used for error messages/logging. Defaults to <compile>

Examples

var myCode = 'print("hello");';
var helloFunc = compile(myCode, "myCode");
helloFunc();

compile is very convenient to support Nasal loaded from other files. For instance, PropertyList XML files (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled. For an example of how to do this, save the below XML code as $FG_ROOT/gui/dialogs/test.xml.

<?xml version="1.0"?>
 
<PropertyList>
 
<nasal><![CDATA[
print("You have FlightGear v", getprop("/sim/version/flightgear"));
]]></nasal>
 
</PropertyList>
Now, start FlightGear and execute this code in the Nasal Console.
# Build the path
var FGRoot = getprop("/sim/fg-root");
var filename = "/gui/dialogs/test.xml";
var path = FGRoot ~ filename;

var blob = io.read_properties(path);
var script = blob.getValues().nasal; # Get the nasal string

# Compile the script.  We're passing the filename here for better runtime diagnostics
var code = call(func {
    compile(script, filename);
}, nil, nil, var compilation_errors = []);

if(size(compilation_errors)){
    die("Error compiling code in: " ~ filename);
}

# Invoke the compiled script, equivalent to code();
# We're using call() here to detect errors:
call(code, [], nil, nil, var runtime_errors = []);

if(size(runtime_errors)){
    die("Error calling code compiled loaded from: " ~ filename);
}

contains()

contains(hash, key);

Source

Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.
hash
The hash to search in.
key
The scalar to be searched for, contained as a key in the hash.

Examples

# Initialize a hash
var hash = {
    element: "value"
};
print(contains(hash, "element") ? "Yes" : "No"); # This will print "Yes"
# Initialize a hash
var hash = {
    element: "value"
};
print(contains(hash, "element2") ? "Yes" : "No"); # This will print "No"

delete()

delete(hash, key);

Source

Deletes the key from the hash if it exists. Operationally, this is identical to setting the hash value specified by the key to nil, but this variant potentially frees storage by deleting the reference to the key and by shrinking the hash. Returns the hash that has been operated on.
hash
The hash from which to delete the key.
key
The scalar to be deleted, contained as a key in the hash.

Example

# Initialize the hash
var hash = {
    element1: "value1",
    element2: "value2"
};
delete(hash, "element1"); # Delete element1
debug.dump(hash); # prints the hash, which is now minus element1

die()

die(error);

Source

Terminates execution and unwinds the stack. The place and the line will be added to the error. This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with call().
error
String describing the error.
Note This parameter is technically optional, but it is highly recommended to use it.

Example

print("Will print");
die("Don't go any further!");
print("Won't print"); # Will not be printed because die() stops the process

find()

find(needle, haystack);

Source

Finds and returns the index of the first occurrence of the string needle in the string haystack, or -1 if no such occurrence was found.
needle
String to search for.
haystack
String to search in.

Examples

print(find("c", "abcdef")); # prints 2
print(find("x", "abcdef")); # prints -1
print(find("cd", "abcdef")); # prints 2

ghosttype()

ghosttype(ghost);

Source

Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ naGhostType instance) if no name has been set. Ghost is an acronym that stands for Garbage-collected Handle to OutSide Thingy.
ghost
Ghost to return a description for.

Example

print(ghosttype(airportinfo())); # prints "airport"

id()

id(object);

Source

Returns a string containing information on the type and ID of the object provided in the single argument. The information is returned in the form of <type>:<id>, where <type> is the type of object, and <id> is the ID.
object
Can be either of a string, a vector, a hash, a code, a function, or a ghost.

Example

print(id("A")); # prints "str:000000001624A590"

int()

int(number);

Source

Returns the integer part of the numeric value of the single argument, or nil if none exists.
number
Number or string with just a number in it to return an integer from.

Examples

print(int(23)); # prints "23"
print(int(23.123)); # prints "23"
debug.dump(int("string")); # prints "nil"

keys()

keys(hash);

Source

Returns a vector containing the list of keys found in the single hash argument.
hash
The hash to return the keys from.

Example

# Initialize a hash
var hash = {
    element1: "value",
    element2: "value"
};
debug.dump(keys(hash)); # print the vector

left()

left(string, length);

Source — Version added: FG 2.12

Returns a substring of string, starting from the left.
string
String to return part of.
length
Integer specifying the length of the substring to return.

Example

print(left("string", 2)); # prints "st"

num()

num(number);

Source

Returns the numerical value of the single string argument, or nil if none exists.
number
String with just a number in it to return a number from.

Examples

print(num("23")); # prints "23"
print(num("23.123")); # prints "23.123"
debug.dump(num("string")); # prints "nil"

pop()

pop(vector);

Source

Removes and returns the last element of the single vector argument, or nil if the vector is empty.
vector
Vector to remove an element from.

Examples

var vector = [1, 2, 3];
pop(vector);
debug.dump(vector); # prints "[1, 2]"
var vector = [1, 2, 3];
debug.dump(pop(vector)); # prints "3"
var vector = [];
debug.dump(pop(vector)); # prints "nil"

right()

right(string, length);

Source — Version added: FG 2.12

Returns a substring of string, starting from the right.
string
String to return part of.
length
Integer specifying the length of the substring to return.

Example

print(right("string", 2)); # prints "ng"

setsize()

setsize(vector, size);

Source

Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with nil entries. Returns the vector operated upon.
vector
The vector to be operated on.
size
The desired size of the vector in number of entries.

Examples

var vector = [1, 2, 3]; # Initialize a vector
setsize(vector, 4);
debug.dump(vector); # print the vector
var vector = [1, 2, 3]; # Initialize a vector
setsize(vector, 2);
debug.dump(vector); # print the vector

size()

size(object);

Source

Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is nil or a number, this error will be thrown: object has no size().
object
Object to find the size of. Must be a string, a vector or a hash.

Examples

var string = "string";
print(size(string)); # prints "6"
var vector = [1, 2, 3];
print(size(vector)); # prints "3"
var hash = {
    element1: "value1",
    element2: "value2",
    element3: "value3"
};
print(size(hash)); # prints "3"

sort()

sort(vector, function);

Source

Returns a vector containing the elements in the input vector sorted in according to the rule given by function. Implemented with the ANSI C qsort(), sort() is stable. This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input. It is run in a loop, so function is run several times.
vector
Input vector to sort.
function
Function according to which the elements will be sorted by. It should take two arguments and should return one of 1, 0, or -1.
Return value Meaning
less than 0 first argument should go before second argument
0 first argument equals second argument
greater than 0 first argument should go after second argument

Examples

This example sorts elements from greatest to smallest.
var sort_rules = func(a, b){
    if(a < b){
        return -1; # A should before b in the returned vector
    }elsif(a == b){
        return 0; # A is equivalent to b
    }else{
        return 1; # A should after b in the returned vector
    }
}
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints "[1, 2, 3, 4, 5, 6]"
# Outputs the elements in reverse order (greatest to smallest)
var sort_rules = func(a, b){
    if(a < b){
        return 1; # -1 in the above example
    }elsif(a == b){
        return 0;
    }else{
        return -1; # 1 in the above example
    }
}
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints "[6, 5, 4, 3, 2, 1]"

split()

split(delimiter, string);

Source

Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.
delimiter
String that will split the substrings in the returned vector.
string
String to split up.

Examples

debug.dump(split("cd", "abcdef")); # prints "['ab', 'ef']"
debug.dump(split(".", "3.2.0")); # prints "[3, 2, 0]"
debug.dump(split("/", "path/to/file")); # prints "['path', 'to', 'file']"

sprintf()

sprintf(format[, arg[, arg, [...]]]);

Source

Creates and returns a string formatted using ANSI C vsnprintf() [1]. Below is a table of supported format specifiers.
 %[flags][width][.precision]specifier
Flags
Flag Output
+ Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers. By default, only negative numbers are preceded with a - sign.
space Prefixes non-signed numbers with a space.
- Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.
0 Use 0 instead of spaces to pad a field when the width option is specified.
# Used with o, x or X specifiers the value is preceded with 0, 0x or 0X respectively for values different than zero. Used with e, E and f, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with g or G the result is the same as with e or E but trailing zeros are not removed.
Width
Integer specifying the minimum number of characters to be returned.
Precision
Integer preceded by a dot specifying the number of decimal places to be written.
Specifiers
Specifier Output
d, i Signed decimal number.
% Percent (%) character.
c A single character assigned to a character code, the code given in an integer argument. See http://www.asciitable.com/ for a list of supported characters and their codes.
o Unsigned integer as an octal number.
u Unsigned decimal integer.
x, X Unsigned integer as a hexadecimal number. If x is used, any letters in the number are lowercase, while X gives uppercase.
e, E Double value in scientific notation (i.e., [-]ddd.ddde[+/-]ddd), with an exponent being denoted by e or E depending on whether an upper or lowercase is used respectively.
f Floating-point number, in fixed decimal notation, by default with 6 decimal places.
F Appears to be available[2], but doesn't work.
g, G Double in either normal or exponential notation, whichever is more appropriate for its magnitude. g uses lower-case letters, G uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included. Also, the decimal point is not included on whole numbers.
format
String specifying the format. Can be used with or without a format specifiers. See below for examples.
arg
Argument specifying a value to replace a format placeholder (such as %d) in the format string. Not required if there are no format specifiers.

Examples

print(sprintf("%i", 54)); # prints "54"
print(sprintf("Pi = %+.10f", math.pi)); # prints "Pi = +3.1415926536"
print(sprintf("%6d", 23)); # prints "    23"
print(sprintf("%06d", 23)); # prints "000023"
var FGVer = getprop("/sim/version/flightgear");
print(sprintf("You have FlightGear v%s", FGVer)); # prints "You have FlightGear v<your version>"
print(sprintf("Hexadecimal 100000 = %X", 100000)); # prints "Hexadecimal 100000 = 186A0"
print(sprintf("Hexadecimal 100000 = %x", 100000)); # prints "Hexadecimal 100000 = 186a0"
print(sprintf("Code 65 is %c", 65)); # prints "Code 65 is A"
print(sprintf("%e", 54)); # prints "5.400000e+001"
print(sprintf("%E", 54)); # prints "5.400000E+001"
print(sprintf("%o", 54)); # prints "66"
print(sprintf("50%% of 100 is %i", 100 / 2)); # prints "50% of 100 is 50"

streq()

streq(a, b);

Source

Tests the string values of the two arguments for equality. This function is needed because the == operator (see Nasal Operators) tests for numeric equality first. If either or both the arguments are not strings, 0 (False) will be returned. Returns either 0 (False) or 1 (True).
Note  This function is rarely required in typical code.
a
First argument for testing equality.
b
Second argument for testing equality.

Examples

print(streq("0", "0")); # prints "1" (True)
print(0 == 0.0); # prints "1" (True)
print(streq("0", "0.0")); # prints "0" (False)

substr()

substr(string, start [, length]);

Source

Similar the subvec(), but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).
string
String to return a substring from.
start
Integer specifying the start of a substring.
length
Optional argument specifying the length of the substring. Defaults to the end of the string.

Examples

print(substr("abcde", 1, 3)); # prints "bcd"
print(substr("abcde", 1)); # prints "bcde"
print(substr("abcde", 2, 1)); # prints "c"

subvec()

subvec(vector, start[, length]);

Source

Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector).
vector
The vector to take the sub-vector from.
start
The starting point of the sub-vector within the given vector.
length
Optional argument specifying the length of the sub-vector, from the starting point.

Notes:

  • Omitting the vector and start arguments is not an error (possibly it should be) but the return value is nil.
  • A negative start argument is an error. This seems wrong. Perhaps the language designer could comment.
  • A value of start greater than size(vector) causes an error. A value equal to size(vector) returns an empty vector.
  • If the value of length is greater than size(vector) - start then it is ignored. That is, all elements from start to the end of vector are returned. If length is zero then an empty vector is returned. A negative value of length causes an error.

Examples

var vector = [1, 2, 3];
debug.dump(subvec(vector, 0)); # prints "[1, 2, 3]"
var vector = [1, 2, 3];
debug.dump(subvec(vector, 1)); # prints "[2, 3]"
var vector = [1, 2, 3];
debug.dump(subvec(vector, 1, 1)); # prints "[2]"

typeof()

typeof(object);

Source

Returns a string indicating the whether the object is nil, a scalar (number or string), a vector, a hash, a function, or a ghost.
object
Object to return the type of.

Examples

var object = nil;
print(typeof(object)); # prints "nil"
var object = "Hello world!";
print(typeof(object)); # prints "scalar"
var object = math.pi;
print(typeof(object)); # prints "scalar"
var object = [1, 2, 3];
print(typeof(object)); # prints "vector"
var object = {};
print(typeof(object)); # prints "hash"
var object = func {};
print(typeof(object)); # prints "func"
var object = airportinfo();
print(typeof(object)); # prints "ghost"

Extension functions

The extension functions are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:

abort()

abort();

Source

This function is a wrapper for the C++ abort() function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the "exit" fgcommand, which will exit FlightGear more gracefully (see example below).

Examples

This example will immediately stop FlightGear with an error, such as "FlightGear has stopped working."
abort();
For exiting FlightGear in a better way, please use the following code:
fgcommand("exit");

abs()

abs(number);

Source

This simple function returns the absolute value of the provided number.
number
This argument is required and should be a number.

Examples

print(abs(1)); # prints "1"
print(abs(-1)); # prints "1"

addcommand()

addcommand(name, code);

Source — Version added: FG 2.12

This function enables the addition of a new custom fgcommand to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.

name
This will become the name of the new fgcommand. Must be a string.
code
The code that will be executed when the fgcommand is run. Must be a function.

Examples

This example adds a new fgcommand and then runs it. Although it executes a simple print() statement, any valid Nasal code can be used.
addcommand("myFGCmd", func {
    print("fgcommand 'myFGCmd' has been run.");
});
fgcommand("myFGCmd");
This example demonstrates how parameters are defined in a new fgcommand.
addcommand("myFGCmd", func(node){
    print(node.getNode("number").getValue()); # prints the value of "number," which is 12
});
fgcommand("myFGCmd", props.Node.new({"number": 12}));

airportinfo()

airportinfo();
airportinfo(type);
airportinfo(id);
airportinfo(lat, lon[, type]);

Source

Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:
  • lon: Longitude of the location.
  • lat: Latitude of the location.
  • has_metar: True or false depending whether the airport has a METAR code defined for it.
  • elevation: Elevation of the location in metres.
  • id: ICAO code of the airport (or ID of the seaplane base/heliport).
  • name: Name of the airport/heliport/seaplane base.
  • runways
    • <runway name>
      • stopway: Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.
      • threshold: Length of the runway's displaced threshold This is a link to a Wikipedia article in metres. Will return 0 if there is none.
      • lat: Latitude of the runway.
      • lon: Longitude of the runway.
      • width: Width of the runway in metres.
      • heading: Heading of the runway.
      • length: Length of the runway in metres.

Information is extracted in the same way as accessing members of a Nasal hash. For example:

# prints to lengths of the runways of the nearest airport in feet and metres
var info = airportinfo();
print("-- Lengths of the runways at ", info.name, " (", info.id, ") --");
foreach(var rwy; keys(info.runways)){
    print(rwy, ": ", math.round(info.runways[rwy].length * M2FT), " ft (", info.runways[rwy].length, " m)");
}

Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.

id
The ICAO code of an airport to retrieve information about.
type
When this argument is used, the function will return the closest airport of a certain type. Can be one of "heliport," "seaport," or "airport" (default).
Note Running this function without any parameters is equivalent to this:
airportinfo("airport");

lat and lon
When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the type parameter) to those coordinates.

Examples

var info = airportinfo();
print("Nearest airport: ", info.name, " (", info.id, ")"); # prints the name and ICAO code of the nearest airport
var info = airportinfo("heliport");
print("Elevation of the nearest heliport: ", math.round(info.elevation * M2FT), " ft"); # prints the elevation and name of the nearest heliport
var info = airportinfo("KSQL");
print("-- Runways of ", info.name, " (", info.id, "): --");
foreach(var rwy; keys(info.runways)) {
    print(rwy); # prints the runways of KSQL
}
var info = airportinfo(37.81909385, -122.4722484);
print("Coordinates of the nearest airport: ", info.lat, ", ", info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge
var info = airportinfo(37.81909385, -122.4722484, "seaport");
print("Nearest seaplane base: ", info.name, " (", info.id, ")"); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge
This example prints the all information from an airportinfo() call.
var info = airportinfo("KSFO");
print(info.name);
print(info.id);
print(info.lat);
print(info.lon);
print(info.has_metar);
print(info.elevation);
foreach(var rwy; keys(info.runways)){
    print("-- ", rwy, " --");
    print(info.runways[rwy].lat);
    print(info.runways[rwy].lon);
    print(info.runways[rwy].length);
    print(info.runways[rwy].width);
    print(info.runways[rwy].heading);
    print(info.runways[rwy].stopway);
    print(info.runways[rwy].threshold);
}

airwaysRoute()

airwaysRoute(start, end[, type]);

Source

This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See Nasal Flightplan for more information.

start
Start waypoint, in the form of a waypoint ghost, such as that provided by flightplan().
end
Same as above.
type
Instructs the function to compute a high level route (when set to "highlevel"), or a low level route (when set to "lowlevel"). Defaults to "highlevel."

Examples

In the route manager dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the Map for this). Then run this code in your Nasal Console.
var fp = flightplan();
var start = fp.getWP(0);
var end = fp.getWP(fp.getPlanSize() - 1);
var rt = airwaysRoute(start, end);
foreach(var wp; rt){
    print(wp.wp_name); # print the waypoints in the computed route
}
Exactly the same as above, but computes a low level path.
var fp = flightplan();
var start = fp.getWP(0);
var end = fp.getWP(fp.getPlanSize() - 1);
var rt = airwaysRoute(start, end, "lowlevel");
foreach(var wp; rt){
    print(wp.wp_name); # print the waypoints in the computed route
}

assert()

assert(condition[, message]);

Source — Version added: FG 3.2

Returns either true if the condition evaluates as true, or aborts with a die() call, which can be customised.
condition
Condition to evaluate.
message
Optional message that will be used in any die() call. Defaults to "assertion failed!"

Examples

var a = 1;
var b = 2;
print(assert(a < b)); # prints "1" (true)
var a = 1;
var b = 2;
assert(a > b, 'a is not greater than b'); # aborts with a custom error message

carttogeod()

carttogeod(x, y, z);

Source

Converts Earth-centered, Earth-fixed This is a link to a Wikipedia article coordinates (x, y and z) to geodetic coordinates This is a link to a Wikipedia article (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the WGS 84 This is a link to a Wikipedia article (6,378,137 metres).[3]
x
Mandatory x-axis value, in metres.
y
Mandatory y-axis value, in metres.
z
Mandatory z-axis value, in metres.

Example

var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.
print("Latitude: ", lat); # prints lat, lon and alt, which are all zero, see above
print("Longitude: ", lon);
print("Altitude: ", alt);

cmdarg()

_cmdarg()
cmdarg();

Part 1 | Part 2

cmdarg() returns the property root of certain types of XML files. These could be nodes in the Property Tree, or temporary and/or non-public nodes outside the Property tree.

It is used by Nasal scripts embedded in XML files. It returns a props.Node object (see fgdata/Nasal/props.nas), and you can use all of its methods on the returned value. cmdarg() should only be used in four types/places of XML files:

  • Bindings: This is needed so that the value of a joystick's axis can be accessed internally.
  • Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog).
  • Embedded Canvases: The Nasal code behind Canvas windows embedded in PUI dialogs can use it to accessing the root directory of their Canvas.
  • Animation XML files: If the animation XML file is used in an AI/MP model, cmdarg() will return the root of the AI model in the /ai/models/ directory. Examples: /ai/models/aircraft[3]/, /ai/models/multiplayer[1]/

You should not use cmdarg() in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate cmdarg() call.

Also, you should not delay cmdarg() using maketimer(), settimer() or setlistener(), because it will return an unrelated property.

Example
This example demonstrates the usage of cmdarg() in a binding. Save the below XML snippet as $FG_ROOT/gui/dialogs/cmdarg-demo.xml. Then run the Nasal snippet below in your Nasal Console. Upon clicking Close, a message will be printed sowing the root of the binding in the Property Tree.

<?xml version="1.0" encoding="UTF-8"?>
 
<PropertyList>
 
<name>cmdarg-demo</name>
<layout>vbox</layout>
 
<text>
  <label>Click "Close" to activate the demonstration (a message in the console).</label>
</text>
 
<button>
  <legend>Close</legend>
  <binding>
    <command>nasal</command>
    <script>print("Button binding root: '" ~ cmdarg().getPath() ~ "'");</script>
  </binding>
  <binding>
    <command>dialog-close</command>
  </binding>
</button>
 
</PropertyList>
fgcommand("dialog-show", {"dialog-name": "cmdarg-demo"});

This example demonstrates the usage of cmdarg() in Nasal code within dialogs. Open $FG_ROOT/gui/dialogs/cmdarg-demo.xml from the previous example, copy & paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click Click me!, the button's label will change to "I've been changed!"

<?xml version="1.0" encoding="UTF-8"?>
 
<PropertyList>
 
<name>cmdarg-demo</name>
<layout>vbox</layout>
 
<text>
  <label>Click "Click me!" to activate the demonstration (the button's label will change).</label>
</text>
 
<button>
  <legend>Click me!</legend>
  <binding>
    <command>nasal</command>
    <script>change_label();</script>
  </binding>
</button>
 
<button>
  <legend>Close</legend>
  <binding>
    <command>dialog-close</command>
  </binding>
</button>
 
<nasal>
  <open><![CDATA[
    var dlg_root = cmdarg();
    var dlg_name = {"dialog-name": "cmdarg-demo"};
    var change_label = func {
        dlg_root.getNode("button[0]/legend").setValue("I've been changed!");
        fgcommand("dialog-close", dlg_name);
        fgcommand("dialog-show", dlg_name);
    }
  ]]></open>
</nasal>
 
</PropertyList>

For an example of cmdarg() used with Canvas, please see Howto:Adding a canvas to a GUI dialog.

courseAndDistance()

courseAndDistance(to);
courseAndDistance(from, to);

Source

Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see here), and is in the range 0–360. Both arguments can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
from
Optional parameter defining the from where the function should calculate its results. If the function is given one argument (to), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.
to
Like the first parameter, but defines the second point.

Examples

This example demonstrates the usage of the function with the airport ghost type.
var from = airportinfo("KSFO");
var to = airportinfo("KSQL");
var (course, dist) = courseAndDistance(from, to);
print(course); # prints course from KSFO to KSQL
print(dist); # prints distance in nm from KSFO to KSQL
This example demonstrates the usage of the function with hashes containing lat and lon.
var from = {lat: 0, lon: 0};
var to = {lat: 1, lon: 1};
var (course, dist) = courseAndDistance(from, to);
print(course);
print(dist);
This example demonstrates usage of a geo.Coord object.
var from = geo.Coord.new().set_latlon(0, 0);
var to = geo.Coord.new().set_latlon(1, 1);
var (course, dist) = courseAndDistance(from, to);
print(course);
print(dist);
This example demonstrates usage of differing parameter types.
var from = airportinfo("KSFO");
var to = geo.Coord.new().set_latlon(0, 0);
var (course, dist) = courseAndDistance(from, to);
print(course);
print(dist);
The same as above, but the other way round.
var to = {lat: 1, lon: 1};
var (course, dist) = courseAndDistance(0, 0, to);
print(course);
print(dist);
Usage of just one parameter.
var dest = airportinfo("KSQL");
var (course, dist) = courseAndDistance(dest);
print("Turn to heading ", math.round(course), ". You have ", sprintf("%.2f", dist), " nm to go");

createWP()

createWP(pos, name[, flag]);

Source

Creates a new waypoint ghost object.
pos
Dictates the position of the new waypoint. It can be one of the following:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.
name
String that will become the name of the new waypoint.
flag
Optional string that will tell FlightGear what type of waypoint it is. Must be one of "sid," "star," "approach," "missed," or "pseudo."

Examples

Creates a waypoint directly in front and 1 km away and appends it to the flight plan.
var pos = geo.aircraft_position().apply_course_distance(getprop("/orientation/heading-deg"), 1000);
var wp = createWP(pos, "NEWWP");
var fp = flightplan();
fp.appendWP(wp);
var pos = geo.aircraft_position().apply_course_distance(getprop("/orientation/heading-deg"), 1000);
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, "NEWWP");
var fp = flightplan();
fp.appendWP(wp);
var apt = airportinfo();
var wp = createWP(apt, "NEWWP");
var fp = flightplan();
fp.appendWP(wp);
var pos = geo.aircraft_position().apply_course_distance(getprop("/orientation/heading-deg"), 1000);
var wp = createWP(pos.lat(), pos.lon(), "NEWWP");
var fp = flightplan();
fp.appendWP(wp);
Creates a new waypoint and adds it to the flight plan. Waypoints of the type "pseudo" are then removed from the flight plan, including the new waypoint. The print() statements show this.
var pos = geo.aircraft_position().apply_course_distance(getprop("/orientation/heading-deg"), 1000);
var wp = createWP(pos, "NEWWP", "pseudo");
var fp = flightplan();
fp.appendWP(wp);
print(fp.getPlanSize());
fp.clearWPType("pseudo");
print(fp.getPlanSize());

createWPFrom()

createWPFrom(object[, flag]);

Source

Creates a new waypoint object from another object.
object
A ghost object. Must be a ghost type that is one of "airport," "navaid," "runway," or "fix."
flag
Optional string that will tell FlightGear what type of waypoint it is. Must be one of "sid," "star," "approach," "missed," or "pseudo."

Examples

Creates a new waypoint and appends it to the flight plan.
var apt = airportinfo("KSFO");
var wp = createWPFrom(apt);
var fp = flightplan();
fp.appendWP(wp);
Creates a new waypoint and appends it to the flight plan. This way point is then removed; the print() statements prove this.
var apt = airportinfo("KSFO");
var wp = createWPFrom(apt, "pseudo");
print(wp.wp_name);
var fp = flightplan();
fp.appendWP(wp);
print(fp.getPlanSize());
fp.clearWPType("pseudo");
print(fp.getPlanSize());

defined()

defined(symbol);

Source

Returns 1 (true) or 0 (false) depending on whether a variable exists.
symbol
A string that will be what the function searches for.

Example

var number = 12;
var check_exist = func {
    print("Variable 'number' ", defined("number") == 1 ? "exists" : "does not exist"); # 'number' does exist
    print("Variable 'number2' ", defined("number2") == 1 ? "exists" : "does not exist"); # 'number2' does not exist
}
check_exist();

directory()

directory(path);

Source

Returns a vector containing a list of the folders and files in a given file path or nil if the path doesn't exist. Hidden folders and files are not added to the vector.

Note The first two elements of the vector will be '.' and '..'. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.

path
Absolute file path.

Example

Gets the folders and files in $FG_ROOT, and then removes the extra first two elements (see note above).
var dir = directory(getprop("/sim/fg-root")); # get directory
dir = subvec(dir, 2); # strips off the first two elements
debug.dump(dir); # dump the vector

fgcommand()

fgcommand(cmd[, args]);

Part 1 | Part 2

Runs an fgcommand. See also $FG_ROOT/Docs/README.commands and Bindings for more information. See flightgear/src/Main/fg_commands.cxx (line 1425) for the full list of fgcommands. Note that fgcommands generated by addcommand() can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.
cmd
String that is the name of the command that is to be run.
args
If the fgcommand takes arguments, they are inputted using this argument. Can either be a props.Node object, or a hash (see examples below).

Examples

fgcommand("null"); # does nothing
var args = props.Node.new({'script': 'print("Running fgcommand");'});
if (fgcommand("nasal", args)) { # prints "Running fgcommand" and then one of these print statements
    print("Fgcommand succeeded");
} else {
    print("Fgcommand encountered a problem");
}
var args = { 'dialog-name': 'about' };
fgcommand("dialog-show", args); # shows the 'about' dialog

findAirportsByICAO()

findAirportsByICAO(search[, type]);

Source

Returns a vector containing airport ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.
search
Search string for the function. Can either be a partial or a full ICAO code.
Caution The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., "K"), might make FlightGear hang for a certain amount of time.
type
This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of "airport," "heliport," or "seaport."

Examples

var apts = findAirportsByICAO("KSF"); # finds all airports matching "KSF"
foreach(var apt; apts){
    print(apt.name, " (", apt.id, ")"); # prints them
}
var apts = findAirportsByICAO("SP0", "seaport"); # finds all seaplane bases matching "SP0"
foreach(var apt; apts){
    print(apt.name, " (", apt.id, ")"); # prints them
}
var apt = findAirportsByICAO("XBET"); # one way to check if an airport does exist"
if (size(apt) == 0) {
    print("Airport does not exist"); # this one will be printed
} else {
    print("Airport does exist");
}

findAirportsWithinRange()

findAirportsWithinRange([pos, ]range[, type]);

Source

Returns a vector of airport ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.
pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findAirportsWithinRange(lat, lon, range, type);.
range
Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.
type
This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of "airport," "heliport," or "seaport."

Examples

Searches for airports within 10 nm of KSFO.
var pos = airportinfo("KSFO");
var apts = findAirportsWithinRange(pos, 10);
foreach(var apt; apts){
    print(apt.name, " (", apt.id, ")");
}
Searches for seaplane bases within 15 nm of KSFO.
var pos = airportinfo("KSFO");
var apts = findAirportsWithinRange(pos, 15, "seaport");
foreach(var apt; apts){
    print(apt.name, " (", apt.id, ")");
}
Searches for airports within 10 nm of your current position.
var apts = findAirportsWithinRange(10);
foreach(var apt; apts){
    print(apt.name, " (", apt.id, ")");
}

findFixesByID()

findFixesByID([pos, ]id);

Source

Returns a vector containing fix ghost objects matching a given ID, sorted by range from a certain position.

Note Fixes are (usually) also known as waypoints.

pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findFixesByID(lat, lon, id);.
id
Full or partial ID of the fix to search for.
Note Inputting a partial ID does not work correctly (see here). It is best to just input a full ID.

Examples

var fixes = findFixesByID("POGIC");
foreach(var fix; fixes){
    print(fix.id, " - lat: ", fix.lat, " | lon: ", fix.lon); # prints information about POGIC
}
var fix = findFixesByID("ZUNAP");
fix = fix[0];
var (course, dist) = courseAndDistance(fix);
print("Turn to heading ", math.round(course), ". You have ", sprintf("%.2f", dist), " nm to go to reach ", fixes[0].id);

findNavaidByFrequency()

findNavaidByFrequency([pos, ]freq[, type]);

Source

Returns a navaid ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.
pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findNavaidByFrequency(lat, lon, freq, type);.
freq
Frequency, in megahertz, of the navaid to search for.
type
This will narrow the search to navaids of a certain type. Defaults to "all." For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 127).

Example

var navaid = findNavaidByFrequency(11.17);
print("ID: ", navaid.id); # prints info about the navaid
print("Name: ", navaid.name);
print("Latitude: ", navaid.lat);
print("Longitude: ", navaid.lon);
print("Elevation (AMSL): ", navaid.elevation, " m");
print("Type: ", navaid.type);
print("Frequency: ", sprintf("%.3f", navaid.frequency / 1000), " Mhz");
print("Range: ", navaid.range_nm, " nm");
if(navaid.course) print("Course: ", navaid.course);

findNavaidsByFrequency()

findNavaidsByFrequency([pos, ]freq[, type]);

Source

Returns a vector conatining navaid ghost objects for navaids that match a given frequency, sorted from nearest to furthest.
pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findNavaidsByFrequency(lat, lon, freq, type);.
freq
Frequency, in megahertz, of the navaid to search for.
type
This will narrow the search to navaids of a certain type. Defaults to "all." For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 127).

Example

var navaids = findNavaidsByFrequency(10.955);
foreach(var navaid; navaids){
    print("--");
    print("ID: ", navaid.id); # prints info about the navaid
    print("Name: ", navaid.name);
    print("Latitude: ", navaid.lat);
    print("Longitude: ", navaid.lon);
    print("Elevation (AMSL): ", navaid.elevation, " m");
    print("Type: ", navaid.type);
    print("Frequency: ", sprintf("%.3f", navaid.frequency / 1000), " Mhz");
    print("Range: ", navaid.range_nm, " nm");
    if(navaid.course) print("Course: ", navaid.course);
    print("--");
}

findNavaidsByID()

findNavaidsByID([pos, ]id[, type]);

Source

Returns a vector containing navaid ghost objects matching a given ID, sorted by range from a certain position.
pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findNavaidsByID(lat, lon, id, type);.
id
Full or partial ID of the fix to search for.
Note Inputting a partial ID does not work correctly (see here). It is best to just input a full ID.
type
This will narrow the search to navaids of a certain type. Defaults to "all." For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 127).

Example

var navaid = findNavaidsByID("MXW");
navaid = navaid[0];
print("ID: ", navaid.id); # prints info about 'MXW' (a VOR station)
print("Name: ", navaid.name);
print("Latitude: ", navaid.lat);
print("Longitude: ", navaid.lon);
print("Elevation (AMSL): ", navaid.elevation, " m");
print("Type: ", navaid.type);
print("Frequency: ", sprintf("%.3f", navaid.frequency / 1000), " Mhz");
print("Range: ", navaid.range_nm, " nm");

findNavaidsWithinRange()

findNavaidsWithinRange([pos, ]range[, type]);

Source

Returns a vector of navaid ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.
pos
Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost type
  • A hash with lat and lon members
  • A geo.Coord object
  • Two numbers separated by a comma, as if the function is taking three arguments. Example: findNavaidsWithinRange(lat, lon, range, type);.
range
Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to "all." For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 127).
type
This will narrow the search to navaids of a certain type.

Examples

Searches for navaids within 10 nm of KSFO.
var pos = airportinfo("KSFO");
var navs = findNavaidsWithinRange(pos, 10);
foreach(var nav; navs){
    print(nav.name, " (ID: ", nav.id, ")");
}
Searches for navaids within 10 nm of your current position.
var navs = findNavaidsWithinRange(10);
foreach(var nav; navs){
    print(nav.name, " (ID: ", nav.id, ")");
}

finddata()

finddata(path);

Source

Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and $FG_ROOT.
path
A relative path as a string.

Examples

var path = finddata("Aircraft/Generic");
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic
var path = finddata("Airports");
print(path); # prints the absolute path to <TerraSync dir>/Airports
var path = finddata("preferences.xml");
print(path); # prints the absolute path to $FG_ROOT/preferences.xml

flightplan()

flightplan([path]);

Source

Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.

path
Optional path to flight plan XML file.

Examples

Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the Route Manager first.
var fp = flightplan();
print(fp.getWP(fp.current).id);
Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as $FG_HOME/fp-demo.xml.
var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';
var fp = flightplan(path);
for(var i = 0; i < fp.getPlanSize(); i += 1){
    print(fp.getWP(i).id);
}

geodinfo()

geodinfo(lat, lon[, max_alt]);

Source

Returns a vector containing two entries or nil if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in $FG_ROOT/Materials), or nil if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also $FG_ROOT/Docs/README.materials):
  • light_coverage: The coverage of a single point of light in m2.
  • bumpiness: Normalized bumpiness factor for the material.
  • load_resistance: The amount of pressure in N/m2 the material can withstand without deformation.
  • solid: 1 (true) or false (0) depending on whether the material is solid or not.
  • names: Vector of scenery types (usually generated by TerraGear) that will use this material.
  • friction_factor: Normalized friction factor of the material.
  • rolling_friction: The rolling friction coefficient of the material.

Note that this function is a very CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.

lat
Latitude, inputted as a number.
lon
Longitude, inputted as a number.
max_alt
The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, nil will be returned.

Examples

Dumps information about ground underneath the aircraft.
var pos = geo.aircraft_position();
var info = geodinfo(pos.lat(), pos.lon());
debug.dump(info);
Prints whether the ground underneath the aircraft is solid or is water.
var pos = geo.aircraft_position();
var info = geodinfo(pos.lat(), pos.lon());
if (info != nil and info[1] != nil) {
    print("The ground underneath the aircraft is ", info[1].solid == 1 ? "solid." : "water.");
}

geodtocart()

geodtocart(lat, lon, alt);

Source

Converts geodetic coordinates This is a link to a Wikipedia article (latitude, longitude, and altitude) to Earth-centered, Earth-fixed This is a link to a Wikipedia article coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the WGS 84 This is a link to a Wikipedia article (6,378,137 metres). All argument are mandatory.
lat
Latitude, in degrees.
lon
Longitude, in degrees.
alt
Altitude, in metres.

Example

var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.
print("x: ", x); # prints "x: 6378137"
print("y: ", y); # prints "y: 0"
print("z: ", z); # prints "y: 0"

getprop()

getprop(arg[, arg[, ...]]);

Source

Returns the value of a node in the property tree or nil if the node does not exist or the value is not a number (NaN).
arg
There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is support (added by FlightGear commit 34ed79) for numeric arguments as indices. See example 2 below.

Examples

print("You have FlightGear v", getprop("/sim/version/flightgear")); # prints FlightGear version
Note that the example below will only work in FlightGear 3.2 and above.
for(var i = 0; i < 8; i += 1){
    print("View #", i + 1, " is named ", getprop("/sim/view", i, "name"));
}
Same as above, but is supported by all version of FlightGear.
for(var i = 0; i < 8; i += 1){
    print("View #", i + 1, " is named ", getprop("/sim/view[" ~ i ~ "]/name"));
}

greatCircleMove()

greatCircleMove([pos, ]course, dist);

Source

Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, lat and lon (latitude and longitude respectively).
pos
Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:
  • An airport, navaid, runway, taxiway, fix, or waypoint ghost object.
  • A hash with lat and lon members
  • A geo.Coord object
  • A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: greatCircleMove(lat,lon, ...).
course
Course to new set of coordinates, in degrees (in the range 0–360).
dist
Distance in nautical miles to the new set of coordinates.

Examples

var pos = greatCircleMove(0,0, 0, 1);
debug.dump(pos); # print hash with coordinates
var fix = findFixesByID("POGIC");
fix = fix[0];
var pos = greatCircleMove(fix, 45, 10);
debug.dump(pos); # print hash with coordinates

interpolate()

_interpolate()
interpolate(prop, value1, time1[, value2, time2[, ...]]);

Part 1 | Part 2

Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.
prop
String or props.Node object that indicates a node in the property tree to be used.
valuen
Target value to change the property to in the set amount of time. This should be a number.
timen
Time in seconds, that will be taken for the interpolation.

Examples

Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.
setprop("/test", 0); # (re-)set property
interpolate("/test",
    50, 5, # interpolate to 50 in 5 seconds
    10, 2, # interpolate to 10 in 2 seconds
    0, 5); # interpolate to 0 in 5 seconds
# Apply the left brake at 20% per second
var prop = "controls/gear/brake-left";
var dist = 1 - getprop(prop);
if (dist == 1) {
    interpolate(prop, 1, dist / 0.2);
}

isa()

isa(object, class);

Source

Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.
object
Object to check.
class
Class/object to check that object inherits from or is an instance of.

Examples

var coord = geo.Coord.new();
if(isa(coord, geo.Coord)){
    print("Variable 'coord' is an instance of class 'geo.Coord'"); # this one will be printed
} else {
    print("Variable 'coord' is not an instance of class 'geo.Coord'");
}
var coord = geo.Coord.new();
if(isa(coord, props.Node)){
    print("Variable 'coord' is an instance of class 'props.Node'");
} else {
    print("Variable 'coord' is not an instance of class 'props.Node'"); # this one will be printed
}
The example below demonstrates checking of inheritance.
var Const = {
    constant: 2,
    getConst: func {
        return me.constant;
    }
};

var Add = {
    new: func {
        return { parents: [Add, Const] };
    },

    addToConst: func(a){
        return a * me.getConst();
    }
};

var m = Add.new();
print(m.addToConst(4));

if(isa(m, Add)) print("Variable 'm' is an instance of class 'Add'"); # will be printed
if(isa(m, Const)) print("Variable 'm' is an instance of class 'Const'"); # will also be printed

logprint()

logprint(priority[, msg[, msg[, ...]]]);

Source

Concatenates a message and logs it with a given priority level. Unlike print() and printlog(), message outputted by this function will be logged in your fgfs.log file as coming, for example, the Nasal Console, rather than flightgear/src/Scripting/NasalSys.cxx.
priority
Number specifying the priority level of the outputted message:
Number Debug type
1 Bulk
2 Debug
3 Info
4 Warn
5 Alert
msg
The message. There is no limit to the arguments you give give. They will be concatenated together before logging.

Examples

# logs the value of pi to three decimal places with log level 3
logprint(3, "pi = ", sprintf("%.3f", math.pi));
logprint(5, "Alert! This is an important message!");

removecommand()

As you can guess, there's also a removecommand() function which will remove any command – even those implemented in C++! As such it can be very dangerous and remove core functionality, so use with caution.

removelistener()

Cquote1.png For those not following all the cvs logs: I've added a new function to Nasal a few days ago: removelistener(). It takes one argument -- the unique id number of a listener as returned by setlistener(): var foo = setlistener("/sim/foo", die); ... removelistener(foo); This can be used to remove all listeners in an <unload> part that were set by the <load> part of a scenery object: <load> listener = []; append(listener, setlistener("/sim/foo", die)); append(listener, setlistener("/sim/bar", func {}); ... </load> <unload> foreach (l; listener) { removelistener(l) } </unload> screen.nas stores all relevant listener ids in a hash, so that other parts can, for example, remove the mapping of pilot messages to screen and voice): removelistener(screen.listener["pilot"]); The id is 0 for the first listener, 1 for the second etc. removelistener() returns the total number of remaining listeners, or nil on error (i.e. if there was no listener known with this id). This can be used for statistics: id = setlistener("/sim/signals/quit", func {}); # let's not count this one num = removelistener(id); print("there were ", id, " Nasal listeners attached since fgfs was started"); print("of which ", num, " are still active"); m.
— Melchior FRANZ (Mar 2nd, 2006). [Flightgear-devel] Nasal: new command "removelistener()".
(powered by instant-Cquotes)
Cquote2.png

print()

Concatenates an arbitrary number of arguments to one string, appends a new-line, and prints it to the terminal. Returns the number of printed characters.

print("Just", " a ", "test");

setprop()

Sets a property value for a given node path string. Returns 1 on success or 0 if the property could not be set (i.e. was read-only).

setprop(<path> [, <path>, [...]], <value>);

All arguments but the last are concatenated to a path string, like getprop() above. The last value is written to the respective node. If the node isn't writable, then an error message is printed to the console.

Examples:

setprop("/sim/current-view/view-number", 2);
setprop("/controls", "engines/engine[0]", "reverser", 1);

Erasing a property from the property tree: a property that has been created, for example through setprop() has to be erased using the props namespace helper, like this:

props.globals.getNode("foo/bar").remove(); 		# take out the complete node
props.globals.getNode("foo").removeChild("bar"); 	# take out a certain child node

setlistener()

Caution
Cquote1.png it's relatively easy to do bad things unintentionally. Like tie a bit of code to an FDM property and run updates of a display 120 times per second rather than the 30 times you actually need. Like start a loop multiple times so that you update the same property 30 times per frame rather than the one time you actually need. It's actually pretty hard to catch these things, because the code is formally okay, does the right thing and just eats more performance than necessary, and there's no simple output telling you that you're running 30 loops rather than the one you expect.
Cquote2.png

settimer()

CautionImproper use of the settimer() API may cause resource leaks.

This typically is caused by the low-level nature of such code, requiring manual tracking of listeners and timers and manual reset and re-init handling.

It is instead recommended that you use the maketimer() API. Alternatively a wrapping helper class can be used to handle low-level APIs, which is the recommended way to support multiple FlightGear versions.

Runs a function after a given simulation time (default) or real time in seconds.

settimer(<function>, <time> [, <realtime=0>]);

The first argument is a function object (ie, "func { ... }"). Note that this is different from a function call (ie, "func ( ... )"). If you don't understand what this means, just remember to always enclose the first argument in any call to settimer with the word "func" and braces "{ }", and it will always work.

The second argument is a delay time. After this amount of time the function will be executed. For instance, if you want to print the words "My result" in five seconds, use this code:

settimer ( func { print ( "My result"); }, 5);

Inside the braces of the func object you can put any valid Nasal code, including a function call. In fact, if you want to call a function with certain values as arguments, the way to do it is to turn it into a function object by enclosing it with a func{}, for example:

myarg1="My favorite string";
myarg2=432;
settimer ( func { myfunction ( myarg1, myarg2); }, 25);

The third argument is optional and defaults to 0, which lets the time argument be interpreted as "seconds simulation time". In this case the timer doesn't run when FlightGear is paused. For user interaction purposes (measuring key press time, displaying popups, etc.) one usually prefers real time.

# simulation time example
var copilot_annoyed = func { setprop("/sim/messages/copilot", "Stop it! Immediately!") }
settimer(copilot_annoyed, 10);
# real time example
var popdown = func ( tipArg ) { 
    fgcommand("dialog-close", tipArg); 
}
 
var selfStatusPopupTip = func (label, delay = 10, override = nil) {	
    var tmpl = props.Node.new({
            name : "PopTipSelf", modal : 0, layout : "hbox",
            y: 140,
            text : { label : label, padding : 6 }
    });
    if (override != nil) tmpl.setValues(override);
 
    popdown(tipArgSelf);
    fgcommand("dialog-new", tmpl);
    fgcommand("dialog-show", tipArgSelf);
 
    currTimerSelf += 1;
    var thisTimerSelf = currTimerSelf;
 
    # Final argument 1 is a flag to use "real" time, not simulated time
    settimer(func { if(currTimerSelf == thisTimerSelf) { popdown(tipArgSelf) } }, delay, 1);
}

More information about using the settimer function to create loops.

maketimer()

2.12+


Caution
Cquote1.png it's relatively easy to do bad things unintentionally. Like tie a bit of code to an FDM property and run updates of a display 120 times per second rather than the 30 times you actually need. Like start a loop multiple times so that you update the same property 30 times per frame rather than the one time you actually need. It's actually pretty hard to catch these things, because the code is formally okay, does the right thing and just eats more performance than necessary, and there's no simple output telling you that you're running 30 loops rather than the one you expect.
Cquote2.png


Since FlightGear v2.12, there is a new API for making a timer that allows more control over what happens in a timer, as opposed to setting one and forgetting about it.

var timer = maketimer(<interval>, <function>);
var timer = maketimer(<interval>, <self>, <function>);
timer.start();
timer.stop();
timer.restart(<interval>);
timer.singleShot [read/write]
timer.isRunning [read-only]
# create timer with 1 second interval
var timer = maketimer(1, func(){ 
    print('timer called'); 
});
 
# start the timer (with 1 second inverval)
timer.start();
 
# restart timer with 4 second interval
timer.restart(4);
 
# fire one single time in 6 seconds
timer.singleShot = 1;
timer.restart(6);
var Tooltip = {
    new: func(){
        var m = { parents: [Tooltip] };
 
        m._hideTimer = maketimer(1.0, m, Tooltip._hideTimeout);
        m._hideTimer.singleShot = 1;
 
        return m;
    },
 
    run: func(){
        if(!me._hideTimer.isRunning) me._hideTimer.start();
    },
 
    _hideTimeout: func(){
        print("_hideTimeout");
    }
};
 
var t = Tooltip.new();
t.run();

history() (3.1+)

Function to expose flight history as aircraft.history()

var hist = aircraft.history();
 
# get history of aircraft position/orientation collapsing
# nodes with a distance smaller than the given minimum
# edge legth
debug.dump( hist.pathForHistory(<minimum-edge-length-meter>) );

systime()

Returns epoch time (time since 1970/01/01 00:00) in seconds as a floating point number with high resolution. This is useful for benchmarking purposes.

#benchmarking example:
var start = systime();
how_fast_am_I(123);
var end = systime();
print("took ", end - start, " seconds");

parsexml()

This function is an interface to the built-in Expat XML parser. It takes up to five arguments. The first is a mandatory absolute path to an XML file, the remaining four are optional callback functions, each of which can be nil (which is also the default value).

var ret = parsexml(<path> [, <start-elem> [, <end-elem> [, <data> [, <pi> ]]]]);
 
<start-elem>  ... called for every starting tag with two arguments: the tag name, and an attribute hash
<end-elem>    ... called for every ending tag with one argument: the tag name
<data>        ... called for every piece of data with one argument: the data string
<pi>          ... called for every "processing information" with two args: target and data string
 
<ret>         ... the return value is nil on error, and the <path> otherwise

Example:

var start = func(name, attr) {
    print("starting tag ", name);
    foreach (var a; keys(attr))
        print("\twith attribute ", a, "=", attr[a]);
}
var end = func(name) { print("ending tag ", name) }
var data = func(data) { print("data=", data) }
var pi = func(target, data) { print("processing instruction: target=", target, " data=", data) }
parsexml("/tmp/foo.xml", start, end, data, pi);

resolvepath()

SimGear features its own path resolving framework that takes a relative path and returns an absolute path, checking from base directories such as $FG_ROOT, $FG_HOME, $FG_AIRCRAFT, and the current aircraft directory (/sim/aircraft-dir). This function in Nasal takes a path string and returns the absolute path or an empty string if the path couldn't be resolved.

Example:

var guess_path = func(path...) {
    var path_concat = string.join(path, "/");
    var file_path = resolvepath(path_concat);
    if (file_path == "") die("Path not found: "~path_concat);
    return file_path;
}

HTTP module (2.99+)

Get remote data using the HTTP.

http.load()

Load resource identified by its URL into memory.

var request = http.load(<url>);
http.load("http://example.com/test.txt")
    .done(func(r) print("Got response: " ~ r.response));

http.save()

Save resource to a local file.

var request = http.save(<url>, <file_path>);
http.save("http://example.com/test.png", getprop('/sim/fg-home') ~ '/cache/test.png')
    .fail(func print("Download failed!"))
    .done(func(r) print("Finished request with status: " ~ r.status ~ " " ~ r.reason))
    .always(func print("Request complete (fail or success)"));

rand()

Return a random number as generated by sg_random.

srand()

Seed the random number generator based upon the current time. Returns 0.

md5() (3.1+)

Get the md5 hash of a string.

var hash = md5(<str>);
References