Nasal library
![]() |
The FlightGear forum has a subforum related to: Nasal Scripting |
![]() |
Nasal scripting |
---|
![]() |
Nasal internals |
---|
Memory Management (GC) |
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[, ...]]);
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]);
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 6 from 12 by AnotherNamespace.ret()
print(Namespace.key);
call()
call(func[, args[, me[, locals[, error]]]);
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 anyme
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]);
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);
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 (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 ![]() |
\n
|
9 | Horizontal tab ![]() |
\t
|
13 | Carriage return ![]() |
\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]);
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);
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]);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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, [...]]]);
Creates and returns a string formatted using ANSI C vsnprintf()
[1]. Below is a table of supported format specifiers.
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);
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]);
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]);
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.
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);
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 four source files:
- flightgear/src/Scripting/NasalCondition.cxx
- flightgear/src/Scripting/NasalPositioned.cxx
- flightgear/src/Scripting/NasalSys.cxx
- fgdata/Nasal/globals.nas
abort()
abort();
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);
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]);
Function for retrieval of airport, heliport, or seaport 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 seaport/heliport).
- name: Name of the airport/heliport/seaport.
- 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
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.
- <runway name>
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 seaport 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 seaport (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 seaport: ", info.name, " (", info.id, ")"); # print the name and ID of the nearest seaport 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);
}
cmdarg()
- _cmdarg()
cmdarg();
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>Press "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", props.Node.new({"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, blank the file, paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console, and notice that the button is labelled "Close" instead of "Change me."
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<name>cmdarg-demo</name>
<layout>vbox</layout>
<text>
<label>Press "Close" to activate the demonstration (a message in the console).</label>
</text>
<button>
<legend>Change me</legend>
<binding>
<command>dialog-close</command>
</binding>
</button>
<nasal>
<open><![CDATA[
var self = cmdarg();
self.getNode("button[0]/legend").setValue("Close");
]]></open>
</nasal>
</PropertyList>
For an example of cmdarg()
used with Canvas, please see Howto:Adding a canvas to a GUI dialog.
fgcommand()
Runs an internal "fgcommand", see Bindings for a full list of available items (since they are most often used in bindings for input devices). The first argument is the name of the command (e.g. "property-interpolate") and the second is a props.Node object or its ghost which is the "argument" to the fgcommand. (This ensures that fgcommands are universal since props.Node/SGPropertyNode objects can really be accessed from anywhere in FlightGear.) Each fgcommand returns 1 if it succeeded or 0 if it failed.
The profiling related fgcommands profiler-start and profiler-stop are documented at Built-in Profiler.
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.
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");
logprint()
Similar, but the first argument is reserved for number specifying the priority (i.e. matching a sgDebugPriority object: 1 for bulk, 2 for debug, 3 for info, and 4 for warn). Also see printlog() in globals.nas: it does essentially the same but with named levels ("bulk", etc.). (The latter relies on print(), however, and does not make use of the sophistication of sglog in dealing with source file and line number.)
getprop()
Returns the node value for a given path, or nil if the node doesn't exist or hasn't been initialized to a value yet.
getprop(<path> [, <path>, [...]]);
Several arguments will be added together to produce a path, with numeric arguments specifying indexes (as of FlightGear 3.1), so
getprop("canvas/by-index", "texture", 1, "name");
is the same as:
getprop("canvas/by-index/texture[1]/name");
Example:
print("The frame rate is ", getprop("/sim/frame-rate"), " FPS");
for (var i=0; i < 10; i += 1) {
print("View ", i, "'s name is: ", getprop("/sim/view", i, "name"));
}
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
interpolate()
Give the value from a value or a source node to a destination node in given time.
interpolate(<path>, <value>, <time> [, <value2>, <time2> [, ...]]);
Examples:
interpolate("controls/switches/nav-lights-pos", 1, 0.25); # After 25ms, nav-lights-pos = 1
interpolate("controls/gear/brake-left-pos", getprop("controls/gear/brake-left"), 1); # After 1s, brake-left-pos = brake-left
To interpolate with a constant speed, use the formula time = distance/speed
.
# Apply the brakes at 20%/second
var p = "controls/gear/brake-left";
var current = getprop(p);
var dist = 1-current;
if (dist) interpolate(p, 1, dist/.2);
setlistener()
settimer()
Caution Improper 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 |
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.11+)
As of 2.11, 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.0, 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');
}
};
geodinfo()
Returns information about geodetic coordinates. Takes two arguments: lat, lon (in degree) and returns a vector with two entries, or nil if no information could be obtained because the terrain tile wasn't loaded. The first entry is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material, or nil if there was no material information available, because there is, for instance, an untextured building at that spot or the scenery tile is not loaded.
var lat = getprop("/position/latitude-deg");
var lon = getprop("/position/longitude-deg");
var info = geodinfo(lat, lon);
if (info != nil) {
print("the terrain under the aircraft is at elevation ", info[0], " m");
if (info[1] != nil)
print("and it is ", info[1].solid ? "solid ground" : "covered by water");
}
A full data set looks like this:
debug.dump(geodinfo(lat, lon));
# outputs
[ 106.9892101062052, { light_coverage : 0, bumpiness : 0.5999999999999999, load_resistance : 1e+30,
solid : 0, names : [ "Lake", "Pond", "Reservoir", "Stream", "Canal" ], friction_factor : 1,
rolling_friction : 1.5 } ]
Note that geodinfo is a *very* CPU intensive operation, particularly in FG 2.4.0 and earlier, so use sparingly (forum discussion here).
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>) );
flightplan()
Function to retrieve the active flight-plan object, or load a flight plan from a file path.
Usage:
var fp = flightplan();
var fp = flightplan('/some/path/to/a/flightplan.xml');
In advance of converting the Map and NavDisplay to use the Canvas, James has improved the "flightplan()" extension function of the Nasal scripting interpreter to expose the full route-path vector for each flight plan leg as a vector on the leg.
var fp = flightplan();
for (var i=0; i<fp.getPlanSize(); i += 1)
{
var leg = fp.getWP(i);
debug.dump(leg.path());
}
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");
carttogeod()
Converts cartesian coordinates x/y/z to geodetic coordinates lat/lon/alt, which are returned as a vector. Units are degree and meter.
var geod = carttogeod(-2737504, -4264101, 3862172);
print("lat=", geod[0], " lon=", geod[1], " alt=", geod[2]);
# outputs
lat=37.49999782141546 lon=-122.6999914632327 alt=998.6042055172776
geodtocart()
Converts geodetic coordinates lat/lon/alt to cartesian coordinates x/y/z. Units are degree and meter.
var cart = geodtocart(37.5, -122.7, 1000); # lat/lon/alt(m)
print("x=", cart[0], " y=", cart[1], " z=", cart[2]);
# outputs
x=-2737504.667684828 y=-4264101.900993474 z=3862172.834656495
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
|