<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Johan+G</id>
	<title>FlightGear wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Johan+G"/>
	<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/Special:Contributions/Johan_G"/>
	<updated>2026-04-30T13:48:10Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Honeycomb_Input_Devices&amp;diff=143899</id>
		<title>Honeycomb Input Devices</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Honeycomb_Input_Devices&amp;diff=143899"/>
		<updated>2026-04-05T09:09:11Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +category: Joysticks and Yokes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides information about the input devices available from ''[https://flyhoneycomb.com Honeycomb Aeronautical]'' and their integration into FlightGear.&lt;br /&gt;
&lt;br /&gt;
== Alpha Lite Flight Controls (Yoke) ==&lt;br /&gt;
Classic yoke designed for GA aircrafts like the C172 or PA28. Left side with 4-way hat switch,  2-way rocker switch (e.g. trim switch) and two buttons (e.g. PTT and AP disconnect). Right side has two buttons and a 2-way rocker switche (e.g. rudder trim). &lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
to do&lt;br /&gt;
&lt;br /&gt;
== Alpha Flight Controls (Yoke) ==&lt;br /&gt;
The Alpha Flight Control is basically the same as the Alpha lite but with additional switches for e.g. battery, alternator, several lights and starter 'key' on the unit. Furthermore, the 2-way rocker switches on each side of the yoke are split &lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
Partly done&lt;br /&gt;
&lt;br /&gt;
== Bravo lite Throttle Quadrant ==&lt;br /&gt;
Four lever throttle quadrant with trim wheel, parking brake switch, gear lever and gear indication lights.&lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
to do&lt;br /&gt;
&lt;br /&gt;
== Bravo Throttle Quadrant ==&lt;br /&gt;
Very flexible throttle quadrant with six replaceable levers, gear lever, autopilot control panel, flap switch, annunciator panel and seven configurable switches. Shipped with lever sets for single and dual engine GA aircraft (throttle, mixture, propeller) as well as commercial jets (e.g. Boeing, CRJ).&lt;br /&gt;
&lt;br /&gt;
Separate throttle pack with Airbus style levers is sold separately.&lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Feature&lt;br /&gt;
!Status&lt;br /&gt;
!Comments&lt;br /&gt;
|-&lt;br /&gt;
|Gear lever&lt;br /&gt;
|done&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Gear indicator&lt;br /&gt;
|done&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Trim wheel&lt;br /&gt;
|done&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Thrust levers&lt;br /&gt;
|done&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|reversers&lt;br /&gt;
|wip&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|TOGA, AT disconnect&lt;br /&gt;
|wip&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Switches&lt;br /&gt;
|wip&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|AP mode control panel&lt;br /&gt;
|needs discussion&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Annunciator panel&lt;br /&gt;
|partly done, needs discussion&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Charlie Rudder Pedals ==&lt;br /&gt;
Rudder pedals with L/R break.&lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
{{Done}}&lt;br /&gt;
&lt;br /&gt;
== Foxtrott Aviation Stick ==&lt;br /&gt;
Joystick / Sidestick with several buttons and switches e.g. for aircraft lights, AP, radio, etc&lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
to do&lt;br /&gt;
&lt;br /&gt;
== Sierra TPM Module ==&lt;br /&gt;
Throttle, Propeller, Mixture for a GA aircraft like the C172. Also contains a trim wheel, parking brake, flap switch,gear lever and gear indication lights.&lt;br /&gt;
&lt;br /&gt;
'''Integration'''&lt;br /&gt;
&lt;br /&gt;
to do&lt;br /&gt;
&lt;br /&gt;
== see also ==&lt;br /&gt;
[[USB-HID]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Joysticks and Yokes]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Shadows&amp;diff=143898</id>
		<title>Shadows</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Shadows&amp;diff=143898"/>
		<updated>2026-04-05T09:05:11Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Updating this a bit. Project Rembrandt is no longer maintained and about to be deprecated, and shadows were back in FG 2024.1. But when was scenery object shadows introduced?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Shadows''' were available until [[FlightGear 1.0]]. The feature became deprecated with the [[OSG]] based [[FlightGear 1.9.0]]. However, the shift to OSG opened up the possibility for re-implementing shadows and many other new graphical features in future FlightGear versions (such as happened with [[3D Clouds]]).&lt;br /&gt;
&lt;br /&gt;
At some point scenery object shadows was reintroduced, probably when scenery objects like for example vegetation and random buildings became rendered instantiated rather then as separate objects.&lt;br /&gt;
&lt;br /&gt;
With FlightGear 2024.1 ([https://www.flightgear.org/download/releases/2024-1/ link to changelog]) aircraft shadows was reintroduced.&lt;br /&gt;
&lt;br /&gt;
== Shadow mapping ==&lt;br /&gt;
Shadow mapping is a technique that use a texture where distance of objects from the light has been recorded. During rendering, the distance to the light of the fragment rendered is compared to the shortest distance of the scene to the light, and if it is higher, the fragment is drawn using only ambient light. Otherwise, it is fully lit.&lt;br /&gt;
&lt;br /&gt;
[[Project Rembrandt]] was a new project that implemented shadow mapping for FlightGear.&lt;br /&gt;
&lt;br /&gt;
Another project which allowed aircraft developers to implement aircraft shadows was the [[ALS technical notes#ALS fuselage shadow effect|Atmospheric Light Scattering (ALS)]] project.&lt;br /&gt;
&lt;br /&gt;
== Related ==&lt;br /&gt;
* [[Howto: Add smooth (&amp;quot;Ambient Occlusion&amp;quot;) shadows in Blender]]&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear feature]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143721</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143721"/>
		<updated>2026-03-15T10:04:05Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +-minor spelling and grammar corrections; +external links: Some relevant WikiWikiWeb articles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|Code refactoring|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Unit test files ==&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested, and usually have a one to one relationship with the Nasal modules, that is each module have a test file, and each function have their test function.&lt;br /&gt;
&lt;br /&gt;
The test files usually have the prefix &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then the same name as the Nasal module to be tested. They are regular Nasal files, but have the file suffix &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test). For example &amp;lt;code&amp;gt;test_math.nut&amp;lt;/code&amp;gt; is the test file for &amp;lt;code&amp;gt;math.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, for example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful for example when a module have dependencies to other modules.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test functions and test cases ==&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions, like the names of the test files, usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and matches the names of the functions to test.&lt;br /&gt;
&lt;br /&gt;
The test functions set up variables etc, and then have one or more test cases using the functions described below:&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.html CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
* {{wikipedia|Test-driven development}}&lt;br /&gt;
* [https://wiki.c2.com/?CodeUnitTestFirst CodeUnitTestFirst - WikiWikiWeb]&lt;br /&gt;
* [https://wiki.c2.com/?UnitTest UnitTest - WikiWikiWeb]&lt;br /&gt;
* [https://wiki.c2.com/?TestDrivenDevelopment TestDrivenDevelopment - WikiWikiWeb]&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143716</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143716"/>
		<updated>2026-03-15T03:00:44Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* See also */ +heading: Wiki articles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|Code refactoring|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Unit test files ==&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested, and usually have a one to one relationship with the Nasal modules; each module have a test file, and each function have their test function.&lt;br /&gt;
&lt;br /&gt;
The test files usually have the prefix &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then the same name as the Nasal module to be tested. They are regular Nasal files, but have the file suffix &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test). For example &amp;lt;code&amp;gt;test_math.nut&amp;lt;/code&amp;gt; is the test file for &amp;lt;code&amp;gt;math.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful for example when a module have dependencies to other modules.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test functions and test cases ==&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions, like the names of the test files, usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and matches the names of the functions to test.&lt;br /&gt;
&lt;br /&gt;
The test functions set up variables etc, and then have one or more test cases using the functions described below:&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.html CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143715</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143715"/>
		<updated>2026-03-15T02:55:50Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Writing unit test files */ +- Rephrasing; +- Restructuring; - unitTest.equal() which, looking at the code, does not seem to generate an error&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|Code refactoring|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Unit test files ==&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested, and usually have a one to one relationship with the Nasal modules; each module have a test file, and each function have their test function.&lt;br /&gt;
&lt;br /&gt;
The test files usually have the prefix &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then the same name as the Nasal module to be tested. They are regular Nasal files, but have the file suffix &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test). For example &amp;lt;code&amp;gt;test_math.nut&amp;lt;/code&amp;gt; is the test file for &amp;lt;code&amp;gt;math.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful for example when a module have dependencies to other modules.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test functions and test cases ==&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions, like the names of the test files, usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and matches the names of the functions to test.&lt;br /&gt;
&lt;br /&gt;
The test functions set up variables etc, and then have one or more test cases using the functions described below:&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the actual value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.html CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143714</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143714"/>
		<updated>2026-03-15T02:40:27Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* External links */ fix broken link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|Code refactoring|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The files with the unit tests, the test files, are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.html CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Template:Nasal_Navigation&amp;diff=143713</id>
		<title>Template:Nasal Navigation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Template:Nasal_Navigation&amp;diff=143713"/>
		<updated>2026-03-15T01:37:24Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +link: Nasal unit tests&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;{{Informative template|1=&lt;br /&gt;
Navigation box for core [[Nasal]] articles.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Put this at the top of the article:&lt;br /&gt;
 {{obr}}'''Nasal Navigation'''{{cbr}}&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Navigation templates]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;includeonly&amp;gt;{{nocat|{{{nocat|}}}|Nasal}}&amp;lt;/includeonly&amp;gt;{{forum|30|Nasal Scripting}}&lt;br /&gt;
&lt;br /&gt;
{{Sidebar with collapsible lists&lt;br /&gt;
| name     = Nasal Navigation&lt;br /&gt;
| pretitle = [[File:Nasallogo3.png|link=Nasal]]&lt;br /&gt;
| title    = [[Nasal|Nasal scripting]]&lt;br /&gt;
| expanded = {{{expanded|}}}&lt;br /&gt;
&lt;br /&gt;
| content1   = &lt;br /&gt;
* [[What is Nasal]]?&lt;br /&gt;
* [[Nasal success stories|Success stories]]&lt;br /&gt;
* [[Nasal FAQ]]&lt;br /&gt;
* [[Nasal Snippets]]&lt;br /&gt;
* [[Howto:Syntax highlighting for Nasal]]&lt;br /&gt;
&lt;br /&gt;
| list2title = Tools and utilities&lt;br /&gt;
| list2      = [[Nasal Console|Console]] &amp;amp;bull; [[Interactive Nasal Console|REPL Console]] &amp;amp;bull; [[Nasal Browser|Browser]]&lt;br /&gt;
&lt;br /&gt;
| list3title = Getting started&lt;br /&gt;
| list3      =&lt;br /&gt;
* [[Nasal Best Practices]]&lt;br /&gt;
* [[Nasal for C++ programmers]]&lt;br /&gt;
* [[Creating new Nasal scripts]]&lt;br /&gt;
* [[Nasal Hello World]]&lt;br /&gt;
* [[Howto:Create a new Nasal module]]&lt;br /&gt;
* [[Using Nasal functions]]&lt;br /&gt;
* [[Nasal Variables]]&lt;br /&gt;
* [[Nasal Namespaces]]&lt;br /&gt;
* [[Howto:Understand Namespaces and Methods|Namespace and Methods]]&lt;br /&gt;
* [[Nasal Namespaces in-depth]]&lt;br /&gt;
* [[Nasal Conditionals]]&lt;br /&gt;
* [[Nasal Loops]]&lt;br /&gt;
* [[Numbers in Nasal]]&lt;br /&gt;
* [[Nasal Operators]]&lt;br /&gt;
* [[Howto:Use vectors and foreach loops to write shorter code]]&lt;br /&gt;
* [[Howto:Start using vectors and hashes in Nasal]]&lt;br /&gt;
* [[Nasal library]] ([[Nasal library#Core library functions|core]]/[[Nasal library#Extension functions|extension]] functions)&lt;br /&gt;
** [[Nasal library/clipboard|&amp;lt;code&amp;gt;clipboard&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/debug|&amp;lt;code&amp;gt;debug&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/geo|&amp;lt;code&amp;gt;geo&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/io|&amp;lt;code&amp;gt;io&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/math|&amp;lt;code&amp;gt;math&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/os.path|&amp;lt;code&amp;gt;os.path&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/props|&amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal String Manipulation|&amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt; namespace]]&lt;br /&gt;
** [[Nasal library/flightplan|flightplan library]]&lt;br /&gt;
* [[Nasal Flightplan]]&lt;br /&gt;
* [[Object oriented programming in Nasal|OOP Introduction]]&lt;br /&gt;
* [[Object Oriented Programming with Nasal]]&lt;br /&gt;
* [[Nasal Callbacks Explained]]&lt;br /&gt;
* [[Exception handling with Nasal]]&lt;br /&gt;
* [[Using listeners and signals with Nasal]]&lt;br /&gt;
* [[Developing and debugging Nasal code]]&lt;br /&gt;
&lt;br /&gt;
| list4title = Advanced Tutorials&lt;br /&gt;
| list4      =&lt;br /&gt;
* [[Caching Nasal function calls]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (WIP)&lt;br /&gt;
* [[Nasal Meta-Programming]]&lt;br /&gt;
* [[A GPX flight logger in Nasal]]&lt;br /&gt;
* [[Multiplayer scripting in Nasal]]&lt;br /&gt;
* [[Howto:Making HTTP Requests from Nasal]]&lt;br /&gt;
* [[Howto:Transmit properties over MP]]&lt;br /&gt;
* [[Howto:Terrain sampling in Nasal]]&lt;br /&gt;
* [[Howto:Create a new system in Nasal]]&lt;br /&gt;
* [[Howto:Load a Nasal file at runtime]]&lt;br /&gt;
* [[Howto:Control the route manager in Nasal]]&lt;br /&gt;
* [[Howto:Get a number of elevation offsets for a number of objects]]&lt;br /&gt;
* [[Howto:Nasal in scenery object XML files]]&lt;br /&gt;
* [[Howto:Create animation XML files from Nasal]]&lt;br /&gt;
* [[Howto:Port I/O from Nasal]]&lt;br /&gt;
* [[Howto:Continuation-passing style programming in Nasal]]&lt;br /&gt;
* [[Howto:Start worker threads using listeners in Nasal]]&lt;br /&gt;
* [[Howto:Developing a DSL interpreter in Nasal]]&lt;br /&gt;
* [[Howto:Nasal Metaprogramming]]&lt;br /&gt;
* [[Nasal/JavaScript Subset]]&lt;br /&gt;
* [[Nasal unit tests]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal Internals}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143712</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143712"/>
		<updated>2026-03-15T01:24:26Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +fix: Correct Wikipedia link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|Code refactoring|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The files with the unit tests, the test files, are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.htm CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143711</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143711"/>
		<updated>2026-03-15T01:22:50Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +- Rephrasing the first section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values.&lt;br /&gt;
&lt;br /&gt;
By writing unit tests for functions you are about to write you can check that the functions work as intended by using test cases that compare expected returns and actual returns.&lt;br /&gt;
&lt;br /&gt;
Unit tests will also allow you to check if you introduce bugs when you extend, fix bugs, {{wikipedia|refactor}}, or otherwise maintain your Nasal code.&lt;br /&gt;
&lt;br /&gt;
They will also later help other check that they don't mess up your code when they continue to maintain it.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The files with the unit tests, the test files, are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.htm CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143710</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143710"/>
		<updated>2026-03-14T22:01:24Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Writing unit test files */ + Test files are usually placed in the same directory as the Nasal modules&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests with test cases for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function. I twill also later help other to continue to maintain the Nasal files.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The files with the unit tests, the test files, are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
The test files are usually placed in the same directory as the Nasal modules that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.htm CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143709</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143709"/>
		<updated>2026-03-14T21:37:09Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* External links */ +link: Assertions in the CppUnit documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests with test cases for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function. I twill also later help other to continue to maintain the Nasal files.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://people.freedesktop.org/~mmohrhard/cppunit/group___assertions.htm CppUnit Documentation - Making assertions] - The unit test framework that runs the unit tests.&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143708</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143708"/>
		<updated>2026-03-14T21:32:48Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* unitTest.assert() */ fix: Remove double parameter in Template:Nasal doc&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests with test cases for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function. I twill also later help other to continue to maintain the Nasal files.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143707</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143707"/>
		<updated>2026-03-14T21:29:48Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Test functions and test cases */ +template:WIP; + New section describing what seems to be the syntax, comparing flightgear/src/Scripting/NasalUnitTesting.cxx with the assertions described in the CppUnit Cookbook. Have yet to test. Would be grateful for any help.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests with test cases for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function. I twill also later help other to continue to maintain the Nasal files.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
{{WIP|Parts of the information below may be inaccurate. The author is currently trying to understand how this works. Would be grateful for help.}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;assert&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fail&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;assert_doubles_equal&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;equal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert(bool[, message])&lt;br /&gt;
| text        = Asserts that a boolean is true. If false the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;Test assert failed&amp;quot;.&lt;br /&gt;
| mode        = &lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=47|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1&lt;br /&gt;
| param1      = bool&lt;br /&gt;
| param2      = message&lt;br /&gt;
| param1text  = A boolean. Usually the return from the function to test. Can be an expression.&lt;br /&gt;
| param2text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1     = &lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.fail() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.fail([message])&lt;br /&gt;
| text        = Will fail the test with an optional message, the file path, line number, and the runtime error &amp;quot;Test failed&amp;quot;.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=72|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = message&lt;br /&gt;
| param1text  = An optional string with a message to show with the file path and line number.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_equal(a, b[, message])&lt;br /&gt;
| text        = Compares a and b. If not equal the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=94|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = message&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.assert_doubles_equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.assert_doubles_equal(a, b, tolerance[, message])&lt;br /&gt;
| text        = Compares two doubles. If the difference larger or equal to the tolerance the test fails with an optional message, the file path, line number, and the runtime error &amp;quot;assert_doubles_equal failed&amp;quot;, or if defined the message.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=115|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param3      = tolerance&lt;br /&gt;
| param4      = message&lt;br /&gt;
| param1text  = A double. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A double. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| param3text  = A double with the tolerance.&lt;br /&gt;
| param4text  = An optional string with a message to show with the file path and line number. Will also be the runtime error. Defaults to &amp;quot;assert_doubles_equal failed&amp;quot;.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== unitTest.equal() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
| syntax      = unitTest.equal(a, b)&lt;br /&gt;
| text        = Compares two values.&lt;br /&gt;
| source      = {{flightgear file|p=src/Scripting/NasalUnitTesting.cxx|l=136|t=Source}}&lt;br /&gt;
| version     = Likely 2024.1.2&lt;br /&gt;
| param1      = a&lt;br /&gt;
| param2      = b&lt;br /&gt;
| param1text  = A value. Usually the expected value to be returned. Can be an expression.&lt;br /&gt;
| param2text  = A value. Usually the value returned from the function to be tested. Can be an expression.&lt;br /&gt;
| example1text = &lt;br /&gt;
| example1     = &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FlightGear_wiki:Village_pump&amp;diff=143695</id>
		<title>FlightGear wiki:Village pump</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FlightGear_wiki:Village_pump&amp;diff=143695"/>
		<updated>2026-03-14T12:36:45Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Nasal unit test assertion documentation */ New section. Is there any other documentation than the source code and Nasal unit test files?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NEWSECTIONLINK__&lt;br /&gt;
{{Archives|[[/Archive 2012|2012]]|[[/Archive 2013|2013]]|[[/Archive 2014|2014]]|[[/Archive 2015|2015]]|[[/Archive 2016|2016]]|[[/Archive 2017|2017]]|[[/Archive 2018|2018]]|[[/Archive 2019|2019]]|[[/Archive 2020|2020]]|[[/Archive 2021|2021]]|[[/Archive 2022|2022]]}}&lt;br /&gt;
{{shortcut|FGW:VP}}&lt;br /&gt;
&lt;br /&gt;
Welcome to the '''Village Pump'''. This page is used to discuss the technical issues, operations and guidelines of the [[FlightGear wiki]].&lt;br /&gt;
&lt;br /&gt;
Please &amp;lt;span class=&amp;quot;plainlinks&amp;quot;&amp;gt;[{{fullurl:{{FULLPAGENAME}}|action=edit&amp;amp;section=new}} add new topics]&amp;lt;/span&amp;gt; to the '''bottom''' of this page.&lt;br /&gt;
&lt;br /&gt;
Old discussions should be moved to a [[FlightGear wiki:Village pump/Archive YEAR]]. These discussions can then be moved to a relevant talk page if appropriate.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== MediaWiki updated to 1.39.6 ==&lt;br /&gt;
&lt;br /&gt;
We have updated MediaWiki to its latest LTS version 1.39.6 today. Although we've tested the update, chances are that we've missed certain scenarios or features. Please report any issues that you may encounter.&lt;br /&gt;
&lt;br /&gt;
[[User:Gijs|Gijs]] ([[User talk:Gijs|talk]]) 09:31, 30 January 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Embedded YouTube videos broken ==&lt;br /&gt;
&lt;br /&gt;
It seems that the [[mw:Extension:EmbedVideo|EmbedVideo extension]] was removed at some point. Consequently, pages such as [[Howto:Creating FlightGear Promo Videos]] are broken. At present, the best replacements seems to be [[mw:Extension:EmbedVideo_(fork)|a fork]] and [[mw:Extension:YouTube|Extension:YouTube]].&lt;br /&gt;
&lt;br /&gt;
—'''''[[User:Red Leader|&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;Red Leader&amp;lt;/span&amp;gt;]]''''' ([[User talk:Red Leader|talk]], [[Special:Contributions/Red Leader|contribs]]) 19:21, 30 January 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
: Hi,&lt;br /&gt;
: Thanks for the report. It was introduced by today's update, but should be fixed now.&lt;br /&gt;
: [[User:Gijs|Gijs]] ([[User talk:Gijs|talk]]) 21:21, 30 January 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
:: Confirmed, thanks. And I’d completely forgotten about {{[[Template:Wikipedia|wikipedia]]}}. 😅&lt;br /&gt;
:: '''''[[User:Red Leader|&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;Red Leader&amp;lt;/span&amp;gt;]]''''' ([[User talk:Red Leader|talk]], [[Special:Contributions/Red Leader|contribs]]) 21:57, 30 January 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Forum registration problem ==&lt;br /&gt;
&lt;br /&gt;
Hello,&lt;br /&gt;
I would like to register on the forum, but I tried several times without success. I sent an email to the administrator some time ago, but it seems to have been lost. I don't know what to do, is there somebody here who could help? Please tell me if you need additional information.&lt;br /&gt;
Thanks very much in advance. [[User:Kestrel|Kestrel]] ([[User talk:Kestrel|talk]]) 13:14, 25 March 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
: Hi,&lt;br /&gt;
: That's unfortunate! You can find my email address at https://forum.flightgear.org/contact.php Please try again if you've done that before, I'll actively monitor the address for any incoming messages, and let me know if you don't get a reply.&lt;br /&gt;
: [[User:Gijs|Gijs]] ([[User talk:Gijs|talk]]) 15:01, 25 March 2024 (UTC) (forum admin)&lt;br /&gt;
&lt;br /&gt;
== Sidebar link for &amp;quot;Build server&amp;quot; is broken ==&lt;br /&gt;
&lt;br /&gt;
Hello, &lt;br /&gt;
&lt;br /&gt;
Just wanted to bring to someones attention that the URL for &amp;quot;Build server&amp;quot; currently 404s. Not sure if this resource still exists or if its URL has changed.&lt;br /&gt;
&lt;br /&gt;
Cheers!&lt;br /&gt;
-Joshua&lt;br /&gt;
&lt;br /&gt;
: Hi Joshua,&lt;br /&gt;
: The URL is still correct, but the build server is temporarily down due to a server failure. It's being worked on, so should be back up &amp;quot;soon&amp;quot;.&lt;br /&gt;
: [[User:Gijs|Gijs]] ([[User talk:Gijs|talk]]) 12:29, 3 September 2024 (UTC)&lt;br /&gt;
&lt;br /&gt;
== FlightGear Wikipedia article image needs correctly sourced Flightgear wiki screenshot ==&lt;br /&gt;
https://en.wikipedia.org/wiki/FlightGear&lt;br /&gt;
&lt;br /&gt;
The image used for the Flightgear Wikipedia article appears to have been deleted, because it was sourced from the following Flightgear-wiki screenshot page:&lt;br /&gt;
&lt;br /&gt;
[[:File:Bo 105 over Sint Marteen.png]]&lt;br /&gt;
&lt;br /&gt;
which referred to an imgur link instead of the following thread, which gives a CC license based on a google search of the file name:&lt;br /&gt;
&lt;br /&gt;
https://forum.flightgear.org/viewtopic.php?f=88&amp;amp;t=32163&amp;amp;start=0&lt;br /&gt;
&lt;br /&gt;
Discussion on Wikipedia:&lt;br /&gt;
&lt;br /&gt;
https://en.wikipedia.org/wiki/Wikipedia:Files_for_discussion/2024_December_7#File:FlightGear_Flight_Sim_Bo_105_over_Sint_Marteen.png&lt;br /&gt;
&lt;br /&gt;
&amp;gt; ''Subsequent comments should be made on the appropriate discussion page (such as the file's talk page or in a [https://en.wikipedia.org/wiki/Wikipedia:Deletion_review deletion review]).''&lt;br /&gt;
&lt;br /&gt;
The person who uploaded it, or someone from the wiki, should ideally fix the FlightGear wiki page, and do a deletion review for the Wikipedia file. The file may have been used in multiple pages since screenshots are uncommon on Wikipedia due to proprietary apps. Feel free to remove this comment once it's taken care of. Good luck&lt;br /&gt;
&lt;br /&gt;
== Issues with three templates ==&lt;br /&gt;
&lt;br /&gt;
Hi,&lt;br /&gt;
&lt;br /&gt;
The [[Template:Fgmeta_commit|fgmeta commit]] template appears to be broken. It used to work in the first ''Note'' [[Scripted_Compilation_on_Linux_Debian/Ubuntu#For_the_curious:_the_SSH_way|here]] (see the URL).&lt;br /&gt;
&lt;br /&gt;
Also, I tried to use [[Template:Repo_link#Site:_Generic]] to create a link to the [https://gitlab.kitware.com/cmake/cmake.git CMake repo], but didn't manage: IIRC, the link text was empty.&lt;br /&gt;
&lt;br /&gt;
Finally, [[Template:Fgmeta_source]] doesn't seem to to what the doc. says for ''simplepath'': with&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;{{fgmeta source&lt;br /&gt;
| path = download_and_compile.sh&lt;br /&gt;
| simplepath = 1&lt;br /&gt;
}}&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the link text starts with a slash, so I used&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;{{fgmeta source&lt;br /&gt;
| path = download_and_compile.sh&lt;br /&gt;
| text = download_and_compile.sh&lt;br /&gt;
}}&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is of course more redundant.&lt;br /&gt;
&lt;br /&gt;
Thanks for considering! :-)&lt;br /&gt;
&lt;br /&gt;
[[User:Rominet|Rominet]] ([[User talk:Rominet|talk]])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: Good catch.  I need to look into that one.  I may not have coded the commit part of GitLab into the master {{tl|repo_link}} template.&lt;br /&gt;
&lt;br /&gt;
: [[User:Bugman|Bugman]] ([[User talk:Bugman|talk]]) 15:41, 13 March 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
:: All fixed.  It was quite simple - the {{tl|fgmeta_source}} template was missing the &amp;lt;code&amp;gt;proj&amp;lt;/code&amp;gt; parameter.  I've also fixed {{tl|fgmeta_clone}}.&lt;br /&gt;
&lt;br /&gt;
:: [[User:Bugman|Bugman]] ([[User talk:Bugman|talk]]) 15:53, 13 March 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
::: Thanks for the reply and fixes, Bugman! (And sorry for the delay, I didn't see any notification...) I see the first item is indeed addressed. Regarding the second item, well, I see no place where I'd really want to use this now on the d&amp;amp;c page, so didn't test it. As for the third one, I still get &amp;lt;code&amp;gt;/download_and_compile.sh&amp;lt;/code&amp;gt; in the Preview when I try the above {{tl|fgmeta_source}} snippet with &amp;lt;code&amp;gt;simplepath = 1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
::: I looked at {{tl|fgmeta_clone}} too; it looks nice but seems to default to the &amp;lt;code&amp;gt;git://&amp;lt;/code&amp;gt; protocol. Would it be possible to make it default to using &amp;lt;code&amp;gt;https://&amp;lt;/code&amp;gt;? Thanks again. :-)&lt;br /&gt;
 &lt;br /&gt;
::: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]])&lt;br /&gt;
&lt;br /&gt;
:::: Not a problem.  For the &amp;lt;code&amp;gt;simplepath&amp;lt;/code&amp;gt; parameter, that is the designed behaviour in the {{tl|repo link}} template.  In most cases our repositories have complex directory structures and the desired output is the absolute file path, removing the protocol and URL parts of the path.  Do you wish to simply have the file name there?  No one had asked for that before, so I never added an option for that.  In the case of the source repositories, you often want to remove the first part of the absolute path so that for example in SimGear, {{simgear source|path=simgear/scene/util/QuadTreeBuilder.hxx|simplepath=1}} (&amp;lt;nowiki&amp;gt;{{simgear source|path=simgear/scene/util/QuadTreeBuilder.hxx|simplepath=1}}&amp;lt;/nowiki&amp;gt;) would be shown as {{simgear source|path=simgear/scene/util/QuadTreeBuilder.hxx|text=scene/util/QuadTreeBuilder.hxx}} (&amp;lt;nowiki&amp;gt;{{simgear source|path=simgear/scene/util/QuadTreeBuilder.hxx|text=scene/util/QuadTreeBuilder.hxx}}&amp;lt;/nowiki&amp;gt;.  This is not scriptable in MediaWiki so I left it all for the text parameter.&lt;br /&gt;
&lt;br /&gt;
:::: For the protocol, I have now added the &amp;lt;code&amp;gt;protocol&amp;lt;/code&amp;gt; parameter.  E.g. &amp;lt;nowiki&amp;gt;{{fgmeta clone | protocol = https}}&amp;lt;/nowiki&amp;gt;.  That will give &amp;lt;code&amp;gt;{{fgmeta clone | protocol = https}}&amp;lt;/code&amp;gt; (as of today generated as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;git clone https://gitlab.com/flightgear/fgmeta.git&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
:::: [[User:Bugman|Bugman]] ([[User talk:Bugman|talk]]) 08:51, 24 March 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
::::: Thanks Bugman, I've updated the [[Scripted_Compilation_on_Linux_Debian/Ubuntu|download_and_compile.sh page]] to use &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fgmeta clone | protocol = https}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; as you proposed; it works great. For the link to download_and_compile.sh, I can continue to use &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fgmeta source | path = download_and_compile.sh | text = download_and_compile.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, no problem.&lt;br /&gt;
&lt;br /&gt;
::::: As far as I'm concerned, everything is resolved here, so this thread/section can be deleted if someone wants to “clean up”.&lt;br /&gt;
&lt;br /&gt;
::::: ''Other subject:'' the only remaining thing is that I didn't receive any notification, neither for your replies here nor for the January changes by [[User:Jebba|Jebba]] to the [[Scripted_Compilation_on_Linux_Debian/Ubuntu|download_and_compile.sh page]] that is on my watchlist. I used to receive email notifications for these things years ago, haven't changed my preferences regarding this and don't see any setting that would explain why I don't receive these notifications anymore. Maybe email sending by the wiki software is not quite well configured according to current standards (SPF, DKIM, DMARC)?&lt;br /&gt;
&lt;br /&gt;
::::: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]])&lt;br /&gt;
&lt;br /&gt;
== Improve the 'simgear clone', 'flightgear clone' and 'fgdata clone' templates ==&lt;br /&gt;
&lt;br /&gt;
Hi,&lt;br /&gt;
&lt;br /&gt;
In order to improve [[Fedora_Packages_and_Compiling]], it would be nice to have the &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{simgear clone}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{flightgear clone}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fgdata clone}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; templates on par with &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fgmeta clone}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (support for the &amp;lt;code&amp;gt;protocol&amp;lt;/code&amp;gt; parameter, etc.). I tried&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{simgear clone | protocol = https | opt = --single-branch --branch release/2024.1 | post = simgear}}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
but it doesn't seem to work, as the preview shows this:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;git clone --single-branch --branch release/2024.1 git://gitlab.com simgear&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(ditto with &amp;lt;code&amp;gt;flightear clone&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fgdata clone&amp;lt;/code&amp;gt;). Thanks in advance :-)&lt;br /&gt;
&lt;br /&gt;
[[User:Rominet|Rominet]] ([[User talk:Rominet|talk]])&lt;br /&gt;
&lt;br /&gt;
: I needed a while to work out the best strategy for these changes.  I've now gone through all of the {{tl|repo link}} cloning subtemplates listed on {{tl|repo link/doc related}} and added the &amp;lt;code&amp;gt;project&amp;lt;/code&amp;gt; parameter to the templates and the optional &amp;lt;code&amp;gt;protocol&amp;lt;/code&amp;gt; parameter.  These changes were required as the &amp;lt;code&amp;gt;gl&amp;lt;/code&amp;gt; section of the {{tl|repo_link}} template is constructed differently to the &amp;lt;code&amp;gt;sf&amp;lt;/code&amp;gt; section.  I've also updated the git cloning documentation {{tl|repo link/doc git clone}}.  I hope this covers everything you need.&lt;br /&gt;
&lt;br /&gt;
: [[User:Bugman|Bugman]] ([[User talk:Bugman|talk]]) 07:48, 1 April 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
:: Thanks a lot, Bugman, this works great! Your changes allowed me to perform [https://wiki.flightgear.org/w/index.php?title=Fedora_Packages_and_Compiling&amp;amp;type=revision&amp;amp;diff=141687&amp;amp;oldid=141513 this edit] in [[Fedora_Packages_and_Compiling]]. The only remaining &amp;lt;code&amp;gt;git clone&amp;lt;/code&amp;gt; command written in full on that page is for FG's fork of OpenSceneGraph at GitLab. I understand that choosing a proper name for an applicable &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{... clone}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; template probably requires some careful thinking... ;-)&lt;br /&gt;
&lt;br /&gt;
:: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]])&lt;br /&gt;
&lt;br /&gt;
== Default text in Template:Repo link ==&lt;br /&gt;
&lt;br /&gt;
Hi,&lt;br /&gt;
&lt;br /&gt;
The default ''text'' in {{tl|Repo link}} gives somewhat pleasant link texts such as&lt;br /&gt;
 flightgear/flightgear/src/Scripting/NasalSys.cxx&lt;br /&gt;
when the ''site'' is &amp;lt;code&amp;gt;sourceforge&amp;lt;/code&amp;gt;, however the result looks like a bug for other sites (though I understand this may be as intended). To be clear, the URLs are correct but the default link texts look weird because they start with the unqualified site name rather than the fully-qualified domain name. For instance,&lt;br /&gt;
 {{obr}}fgmeta source{{cbr}}&lt;br /&gt;
yields [https://gitlab.com/flightgear/fgmeta gitlab/flightgear/fgmeta/next] rather than [https://gitlab.com/flightgear/fgmeta gitlab.com/flightgear/fgmeta/next]. I suggest to replace &amp;lt;code&amp;gt;gitlab&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;gitlab.com&amp;lt;/code&amp;gt; in the default text (where the HTML comment says “Site advertising”) and do the same for &amp;lt;code&amp;gt;github&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Note: the Gitorious web site doesn't exist anymore, maybe that should be removed from the template?&lt;br /&gt;
&lt;br /&gt;
Thanks for considering; I can do the replacement if you wish.&lt;br /&gt;
&lt;br /&gt;
[[User:Rominet|Rominet]] ([[User talk:Rominet|talk]]) 10:38, 12 November 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
: Sounds sensible, so please go ahead!&lt;br /&gt;
: Regarding Gitorious, it may be nice to link to an archived version of the repos, such as https://archive.softwareheritage.org/browse/origin/directory/?origin_url=https://gitorious.org/flightgear/fgdata.git&amp;amp;visit_type=git Altough this only makes sense for repos or branches that are not part of our current GitLab...&lt;br /&gt;
: [[User:Gijs|Gijs]] ([[User talk:Gijs|talk]]) 13:58, 12 November 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
:: [https://wiki.flightgear.org/w/index.php?title=Template:Repo_link&amp;amp;action=history Done], thanks Gijs!&lt;br /&gt;
::&lt;br /&gt;
&lt;br /&gt;
:: One should be careful using this, though, because the resulting link ''texts'' may be non-existing URLs (after prepending the protocol) despite looking like valid ones, as shown by the first example in [[User:Rominet/Sandbox#FGMeta-Python repo templates]]!&lt;br /&gt;
&lt;br /&gt;
:: So, to avoid people getting trapped by this, maybe the default link text should start with something like &amp;lt;code&amp;gt;[GitLab]/...&amp;lt;/code&amp;gt; rather than the &amp;lt;code&amp;gt;gitlab.com/...&amp;lt;/code&amp;gt; I just put (to make it clear that such link texts are not supposed to be “Internet addresses”).&lt;br /&gt;
:: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]]) 16:20, 12 November 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
::: This is now implemented. For instance, &amp;lt;code&amp;gt;{{obr}}fgmeta-python source {{!}} path = pyproject.toml {{!}} tag  = 1.0.0b1 {{cbr}}&amp;lt;/code&amp;gt; results in {{fgmeta-python source | path = pyproject.toml | tag  = 1.0.0b1 }}. This also holds for GitHub and GitoriousIsNowClosed. SourceForge has no such prefix for now (it initially didn't, and editing its part of {{tl|Repo link}} requires a bit more temerity than what I have at the moment :-)).&lt;br /&gt;
::: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]]) 09:15, 14 November 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
: That's a good idea.  Note for Gitorious - I added this because there are a number of historical documents/pages that refer to developments that occurred on Gitorious which were not migrated to SourceForge.  I originally made the Gitorious links work by pointing to the archive by the Internet Archive (see [[Gitorious]]).  However that archive has been offline for a few years now.  The Internet Archive are storing all the data so it may return one day.&lt;br /&gt;
&lt;br /&gt;
: [[User:Bugman|Bugman]] ([[User talk:Bugman|talk]]) 22:06, 13 November 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Beware of shortened Git commit IDs ==&lt;br /&gt;
&lt;br /&gt;
While fixing broken links due to the {{tl|foo commit}} templates not specifying &amp;lt;code&amp;gt;proj=flightgear&amp;lt;/code&amp;gt; (this was implicit with &amp;lt;code&amp;gt;site=sf&amp;lt;/code&amp;gt; but isn't with &amp;lt;code&amp;gt;site=gl&amp;lt;/code&amp;gt;), I found that URLs at GitLab don't accept the same shortened Git commit IDs as those at SourceForge:&lt;br /&gt;
 https://sourceforge.net/p/flightgear/fgdata/ci/807062 - OK&lt;br /&gt;
 https://gitlab.com/flightgear/fgdata/-/commit/807062  - NOK&lt;br /&gt;
 https://gitlab.com/flightgear/fgdata/-/commit/807062d - OK&lt;br /&gt;
 https://gitlab.com/flightgear/fgdata/-/commit/807062d0b6f858885547f27325e829c2bf42a12b - OK&lt;br /&gt;
&lt;br /&gt;
As a consequence, there are probably a lot of broken links in the wiki due to shortened commit IDs. Better always use the long ones...&lt;br /&gt;
&lt;br /&gt;
{{Note|Commit ID 807062 in FGData is not ambiguous at the time of this writing.}}&lt;br /&gt;
[[User:Rominet|Rominet]] ([[User talk:Rominet|talk]]) 15:45, 3 December 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
: I have modified {{tl|Simgear_commit}}, {{tl|Flightgear_commit}}, {{tl|Fgdata_commit}}, etc. (12 templates!) to show a commit ID truncated to 7 characters in the automatically generated link text, so there is no excuse for not using the full commit IDs anymore when using these templates (see the result [[Howto:Building_FlightGear_without_HiDPI_support#Commits|here]] for instance). Thanks to [[User:Gijs|Gijs]] for installing [https://en.wikipedia.org/wiki/Module:Ustring Module:Ustring] which made this possible!&lt;br /&gt;
&lt;br /&gt;
: Note that manually provided ''text'' arguments are not truncated.&lt;br /&gt;
: {{tip|In case you feel like replacing shortened commit IDs with full ones in {{tl|foo commit}} template calls, the full commit IDs can be found like so:&lt;br /&gt;
 cd /path/to/repository&lt;br /&gt;
 git rev-parse ''shortened-id1'' ''shortened-id2'' ...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
: [[User:Rominet|Rominet]] ([[User talk:Rominet|talk]]) 18:05, 25 December 2025 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Nasal unit test assertion documentation ==&lt;br /&gt;
I just started an article about [[Nasal unit tests]] but could need some help improving the section about the tests.&lt;br /&gt;
&lt;br /&gt;
The only documentation I find is the {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx|text=C++ source code}} and the existing test files (&amp;lt;code&amp;gt;test_*.nut&amp;lt;/code&amp;gt;) in {{fgdata source|path=Nasal|text=fgdata/Nasal}} (side note: it took me several days to figure out where the &amp;lt;code&amp;gt;nasal-test&amp;lt;/code&amp;gt; FGCommand was implemented).&lt;br /&gt;
&lt;br /&gt;
—[[User:Johan G|Johan G]] ([[User_talk:Johan_G|Talk]] | [[Special:Contributions/Johan_G|contribs]]) 12:36, 14 March 2026 (UTC)&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143694</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143694"/>
		<updated>2026-03-14T12:26:18Z</updated>

		<summary type="html">&lt;p&gt;Johan G: + first section: Slightly clearer phrasing.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests with test cases for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function. I twill also later help other to continue to maintain the Nasal files.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.fail()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_doubles_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_Unit_Testing_Framework&amp;diff=143693</id>
		<title>Nasal Unit Testing Framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_Unit_Testing_Framework&amp;diff=143693"/>
		<updated>2026-03-14T12:22:50Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +template: Historical&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{historical|date = 14 March 2026}}&lt;br /&gt;
{{for|the Nasal unit testing implemented 2020|Nasal unit tests}}&lt;br /&gt;
{{infobox subsystem&lt;br /&gt;
|image =&lt;br /&gt;
|name =Nasal Unit Testing Framework&lt;br /&gt;
|started= 02/2014 (stalled)&lt;br /&gt;
|description = Unit Testing support for Nasal&lt;br /&gt;
|status = RFC &lt;br /&gt;
|maintainers  = F-JYL, dbelcham, Hooray, Philosopher&lt;br /&gt;
|developers = dbelcham, F-JYL (since 02/2014),&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Template:Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Status 06/2013 ==&lt;br /&gt;
We do not currently have any established unit testing framework for Nasal. For the time being, this whole discussion is just about coming up with the requirements and a possible design. &lt;br /&gt;
The intent is to explore the idea of being able to run isolated unit tests against Nasal scripts. The idea was being able to do something like this:&lt;br /&gt;
&lt;br /&gt;
See also {{flightgear commit|d7a680}}.&lt;br /&gt;
&lt;br /&gt;
=== Concept ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
standalone-nasal.exe ATR72-FMC-tests.nas&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and see output like this:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
When_calculating_deflection_that_is_greater_than_the_provided_maximum&lt;br /&gt;
the_provided_maximum_should_be_returned (failed: expected 180 was 99)&lt;br /&gt;
&lt;br /&gt;
When_calculating_deflection_from_33_degrees_to_90_degrees&lt;br /&gt;
the_result_should_be_67_degrees (failed: expected 67 was 66)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== The standalone Nasal interpreter ===&lt;br /&gt;
Based on what I read about the current stand-alone interpreter this should be fairly easy to do. That should now be possible, the nasal-standalone branch builds successfully for Windows (make sure to have boost available for building cppbind):&lt;br /&gt;
&lt;br /&gt;
Here's the Nasal standalone interpreter as part of SimGear: {{gitorious source|proj=fg|repo=hoorays-simgear|branch=topics/nasal-standalone|view=shortlog}}&lt;br /&gt;
Just check out the branch named &amp;quot;nasal-bin&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To build it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd $SG_SRC&lt;br /&gt;
mkdir BUILD&lt;br /&gt;
cd BUILD&lt;br /&gt;
cmake ../ -DENABLE_TESTS=ON -DSIMGEAR_HEADLESS=ON -DENABLE_SOUND=OFF -DENABLE_LIBSVN=OFF&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Basically&lt;br /&gt;
&lt;br /&gt;
* create and use a separate build folder, separate from the source tree&lt;br /&gt;
* configure the build via -DENABLE_TESTS=ON -DSIMGEAR_HEADLESS=ON -DENABLE_SOUND=OFF -DENABLE_LIBSVN=OFF&lt;br /&gt;
* build&lt;br /&gt;
* report back &lt;br /&gt;
&lt;br /&gt;
Note that there's no need to actually install anything (make install), because we are just using the SimGear library to build a standalone nasal-bin binary, nothing else.&lt;br /&gt;
&lt;br /&gt;
Let us know if there are still any windows-specific build errors so that we can fix the config file. It should give you a &amp;quot;nasal-bin.exe&amp;quot; in $SG_BUILD/simgear/nasal/ that runs just Nasal scripts, no FG APIs whatsoever - you '''need''' to pass a valid Nasal script when running the file.&lt;br /&gt;
&lt;br /&gt;
You should be able to &amp;quot;make test&amp;quot; to run a bunch of standard Nasal tests from the original Nasal repository, which are to be found in $SG_SRC/nasal/tests: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
Meanwhile, the build actually works for Windows using the MingW compiler - providing a nasal-bin.exe, which I cannot test currently because I don't have a Windows VM available: http://www.speedyshare.com/MbBTA/download/nasal-bin.exe{{dead link}}&lt;br /&gt;
&lt;br /&gt;
To run it, open a shell (START/EXECUTE cmd/command) and go to the folder of the binary, add a simple Nasal script and run &amp;quot;nasal-bin script.nas&amp;quot;, i.e. use one of the scripts in: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
=== Roadmap ===&lt;br /&gt;
* get the stand-alone interpreter compiling and running against both windows and Linux (currently there are a bunch of libraries used that aren't available on Windows) {{done}}&lt;br /&gt;
* build a set of Nasal scripts that provide stubs for native fg calls (getprop/setprop/etc) {{Not done}}&lt;br /&gt;
* build out testing script with the ability to verify values and report failures to the console {{Not done}}&lt;br /&gt;
* send a patch upstream, so that a standalone Nasal interpreter is included in each upcoming release {{Not done}}&lt;br /&gt;
&lt;br /&gt;
Once we have those three things we would be able to write and execute tests independent of FG and still have them be meaningful. The key thing to remember is that this would be only for isolated unit tests. For integration tests (verifying that different systems, whether 2 different scripts/methods/application, work together correctly) we would need to think about a different approach.&lt;br /&gt;
&lt;br /&gt;
It should be doable to teach the nasal-bin.exe to check $FG_ROOT, and use that if available to load a semi-plausible FG environment (API-wise) - using some fancy meta-programming tricks, most of the default APIs could probably be wrapped, without too much manual work involved. Philosopher could be truly instrumental here, because he really has a deep understanding of some of the more esoteric tricks that can be done in Nasal space, referring to advanced uses of compile(), bind(), call(), closure() and caller() - which make meta-programming a fantastic experience. Basically, familiarity with this handful of APIs, can save tons of time: http://plausible.org/nasal/&lt;br /&gt;
&lt;br /&gt;
There's quite a lot of stuff possible in Nasal, that nobody ever used in FG - Philosopher has started writing a bunch of tutorials, for example see: [[Nasal Meta-Programming]]&lt;br /&gt;
&lt;br /&gt;
And you can take a look at some of the scripts in the standalone branch, which support fancy constructs like dependency resolution using import(&amp;quot;foo&amp;quot;); but also completely sandboxed/wrapped environments: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
These are regression tests developed by the Nasal developer himself, so not true unit testing - but only regression tests for the interpreter itself.&lt;br /&gt;
&lt;br /&gt;
=== Contributing ===&lt;br /&gt;
If possible, new code should be contributed to the maintainers, ideally even a branch of FG_ROOT, because that will make it easier to directly integrate such a unit testing system with all the code we got in $FG_ROOT/Nasal.&lt;br /&gt;
&lt;br /&gt;
== Problem ==&lt;br /&gt;
One of the things that is most frustrating and time consuming when working with Nasal scripts is the brute force and manual nature of testing the scripts. A simple misspelling in a custom script can take 5-10, or more, minutes to fix from the point of finding it (shut down FG, change script, startup FG and get back to a point in the sim where the code will execute). I realize that Nasal is tightly coupled to FG at this point and that most scripts won't run without access to the property tree. That doesn't mean that it can't be done though (mocks, fakes, stubs, etc).&lt;br /&gt;
&lt;br /&gt;
Most scripts in FlightGear use a plethora of APIs and FG-specific modules, so FG has become a runtime dependency (APIs, data structures like the property tree, and &amp;quot;live&amp;quot; state),&lt;br /&gt;
&lt;br /&gt;
== Test/Fail Passes ==&lt;br /&gt;
Just because a language is dynamic doesn't mean that the code-test-fail/pass feedback loop has to be a long one. Just look at dynamic language communities like Ruby and you'll see that automated testing (both behavioral and state), in combination with continuous integration, is used to try to move those failures from application run-time to test suite run-time. Dynamic language communities do this with a lot of success (both in tightening the feedback loop and improving quality).&lt;br /&gt;
&lt;br /&gt;
== Wrapping dependencies ==&lt;br /&gt;
FGs loading of scripts is stopping us from being able to change-reload right in app. What I was thinking is to remove the app (FG in this case) from the equation completely. For a lot of systems scripts the only interaction that they have with FG is through the property tree. Between these interactions the nasal systems scripts being developed for most aircraft are simply state based; they read some values, do some calculations and set some values. If we know the inputs (method parameters and getprop calls), we know the outputs (method return values and setprop calls).&lt;br /&gt;
What I do in other languages is to replace the call to those &amp;quot;external dependencies&amp;quot; (in this case getprop and setprop) with known implementation. So a call to getprop(&amp;quot;/orientation/yaw-deg&amp;quot;) in a specific test scenario would be configured to always return &amp;quot;33.5&amp;quot;. My understanding of the inner workings of Nasal are limited, but I would think that one should be able to override get/setprop due to the dynamic nature of the language. That said, I can't find any definitions for those in the nasal-standalone codebase.&lt;br /&gt;
&lt;br /&gt;
If someone could point me to where the get/setprop stuff is then I'd be a step closer to exploring my theory of having standalone Nasal running against developer defined property tree values as a mechanism for automated testing.&lt;br /&gt;
&lt;br /&gt;
Wrapping APIs is simple to do in Nasal, too - without even requiring C/C++ changes, a standalone testbed could be scripted in Nasal like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var tree = {};&lt;br /&gt;
&lt;br /&gt;
var isalpha = func(n) n &amp;gt;= `a` and n &amp;lt;= `z` or n &amp;gt;= `A` and n &amp;lt;= `Z`;&lt;br /&gt;
var isdigit = func(n) n &amp;gt;= `0` and n &amp;lt;= `9`;&lt;br /&gt;
&lt;br /&gt;
var sanitize = func(p) {&lt;br /&gt;
    if (!p) die();&lt;br /&gt;
    if (p[0] == `/`) p = substr(p, 1, nil);&lt;br /&gt;
    parts = split(&amp;quot;/&amp;quot;, p);&lt;br /&gt;
    for (var i=0; i&amp;lt;size(parts); i+=1) {&lt;br /&gt;
        if (parts[i] == &amp;quot;&amp;quot;) {&lt;br /&gt;
            if (i == size(parts)-1)&lt;br /&gt;
                parts = parts[:i-1];&lt;br /&gt;
            else parts = parts[:i-1] ~ parts[i+1:];&lt;br /&gt;
            i-=1;&lt;br /&gt;
        } else {&lt;br /&gt;
            for (var j=0; j&amp;lt;size(parts[i]); j+=1) {&lt;br /&gt;
                if (parts[i][j] == `[`) break;&lt;br /&gt;
                if (parts[i][j] != `-` and !isalpha(parts[i][j]) and&lt;br /&gt;
                    parts[i][j] != `_` and !isdigit(parts[i][j]) and&lt;br /&gt;
                    parts[i][j] != `.`) die(&amp;quot;bad character in name &amp;quot;~parts[i]~&amp;quot; at index &amp;quot;~j~&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            if (j == size(parts[i]))&lt;br /&gt;
                parts[i] ~= &amp;quot;[0]&amp;quot;;&lt;br /&gt;
            elsif (parts[i][-1] != `]`) die(&amp;quot;bad index specifier in string &amp;quot;~parts[i]~&amp;quot;.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    var p = &amp;quot;/&amp;quot;;&lt;br /&gt;
    foreach (var part; parts)&lt;br /&gt;
        p ~= part;&lt;br /&gt;
    return p;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
# wrappers for the FG setprop/getprop APIs:&lt;br /&gt;
var setprop = func(p, value) tree[ sanitize(p) ] = value;&lt;br /&gt;
var getprop = func(p) return tree[ sanitize(p) ];&lt;br /&gt;
 &lt;br /&gt;
# some tests:&lt;br /&gt;
&lt;br /&gt;
var path = [&amp;quot;/foo/bar&amp;quot;, &amp;quot;/foo[0]/bar[0]&amp;quot;,&amp;quot;/foo[0]/bar[0]/&amp;quot;, &amp;quot;/foo/bar[0]&amp;quot;,&amp;quot;/foo[0]/bar/&amp;quot;,&amp;quot;/foo[0]/bar/&amp;quot;];&lt;br /&gt;
var value = &amp;quot;MyUniqueValue&amp;quot;;&lt;br /&gt;
setprop(path[0], value);&lt;br /&gt;
foreach(var p; path)&lt;br /&gt;
  if (getprop(p) != value) die(&amp;quot;sanitize() implementation is broken&amp;quot;);&lt;br /&gt;
print(&amp;quot;sanitize() looks good!\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# init your tree:&lt;br /&gt;
setprop(&amp;quot;/orientation/yaw-deg&amp;quot;, 33.5);&lt;br /&gt;
print(&amp;quot;yaw-deg is:&amp;quot;, getprop(&amp;quot;/orientation/yaw-deg&amp;quot;) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Basically, you can override ANYTHING in Nasal - even library/extension functions - see above, you don't even need to look at the Nasal C code.&lt;br /&gt;
&lt;br /&gt;
We would need to use custom script-specific wrappers, instead of the main FG/Nasal APIs and modules - so that your Nasal code *never* uses the APIs directly, that way you can easily have different implementations - i.e.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var debug_profile = {};&lt;br /&gt;
var runtime_profile = {};&lt;br /&gt;
var current_profile = nil;&lt;br /&gt;
&lt;br /&gt;
runtime_profile.systime = systime;&lt;br /&gt;
debug_profile.systime = my_systime;&lt;br /&gt;
&lt;br /&gt;
# set the API profile:&lt;br /&gt;
current_profile = runtime_profile;&lt;br /&gt;
&lt;br /&gt;
# And then only ever make calls through active_profile.systime():&lt;br /&gt;
&lt;br /&gt;
print( current_profile.systime() );&lt;br /&gt;
&lt;br /&gt;
# or simply override the global symbols during initialization:&lt;br /&gt;
var systime = current_profile.systime;&lt;br /&gt;
print( systime() );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Suites ==&lt;br /&gt;
&lt;br /&gt;
I'd rather not have to launch FG to run my tests. Ideally I'd like to be able to build up a suite of tests that I can run in an automated fashion to ensure that all are still operating as expected at any time. In my close to ideal world I would want to be able to execute the automate tests (and exercise the scripts as a result) dozens of times per hour. Launching FG and manually triggering this from the console would limit me to a few times per hour. Add in more complicate scripts which require extensive state in the property tree to test specific scenarios and I might be lucky to manually execute these tests a couple of times an hour. Manually launching the tests would also probably mean that I'd have to remember to configure and run each and every scenario...something I would never remember to do. Each time I forgot I'd possibly be introducing issues into my code.&lt;br /&gt;
&lt;br /&gt;
The rigour around this isn't for everyone, but it is how I derive the most confidence that I'm delivering the highest quality code possible. This all came to light because of a defect in the ATR the Omega95 and I have built (mostly him) that could have easily been found if we had had some automate scenario tests written.&lt;br /&gt;
&lt;br /&gt;
I'm working with the ATR's FMC. The time and difficulty with it is that if I want to change and retest any of the different segments in the flight plan (especially the SID and STAR) I need to be able to reset my self to a specific position and property tree state. If I'm testing the transition from waypoint 1-&amp;gt;2 in the SID then I need to start pre waypoint 1 which means on the ground, starting up, keying in the flight plan, etc. Worse is the transition from the flight plan to the first STAR waypoint. If I could write automated tests for all of these scenarios plus dozens of others I can think of then my development-testing feedback loop would tighten immensely.&lt;br /&gt;
&lt;br /&gt;
I had the idea, which you implemented above, of just overriding the get/setprop un the scripts. I guess taking that Sudafed might pay off in more ways than one.&lt;br /&gt;
Ultimately what I'd like to have is an implementation of something like the jUnit/xUnit/nUnit testing frameworks. Tonight's goal will be to hack out a rough implementation that allows for isolation of the property tree.&lt;br /&gt;
&lt;br /&gt;
== Integration &amp;amp; Adoption ==&lt;br /&gt;
Unit testing support in Nasal would certainly be beneficial - but it would need to be added to FG at a library-level, i.e. in $FG_ROOT/Nasal, so that people have to use it, and have an advantage when using it - sort of like the RoR example you mentioned previously.&lt;br /&gt;
&lt;br /&gt;
I could see that being useful for many things, even outside aircraft development - but thinking in th most generic terms, we need to find a compromise that will not just work for specialists who have a decade of unit testing experience, but also our average aircraft developers.&lt;br /&gt;
&lt;br /&gt;
Scripting-wise, I think we really only got a handful of people here who regularly write Nasal code and who would also see the merits and potentially adopt the system.&lt;br /&gt;
&lt;br /&gt;
People would only be likely to actually use that if they have a corresponding developers background, so it would need to be designed right into the framework and touch lots of places in $FG_ROOT and $FG_AIRCRAFT - I only see a handful of aircraft developers here who would go that route and actually have the mental capacity, and developer mentality to see the merits here.&lt;br /&gt;
&lt;br /&gt;
Probably,a handful of people would be able to use it, but if it's well documented, and if it actually supports features not provided otherwise, it could gain traction - so it would need to be more compelling than the current workflow obviously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A unit testing framework is definitely going to be useful for $FG_ROOT as a whole, not just aircraft/instrument developers. Obviously, one of the first steps will be documenting the whole thing with tutorials, so that people can start adopting it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143692</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143692"/>
		<updated>2026-03-14T12:17:51Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +- Move the stub template to the section about test functions and test cases. I can't find any other documentation than the C++ source code at the moment.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
{{stub|section=1}}&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.fail()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_doubles_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=User:Johan_G/Things_I_have_done&amp;diff=143691</id>
		<title>User:Johan G/Things I have done</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=User:Johan_G/Things_I_have_done&amp;diff=143691"/>
		<updated>2026-03-14T12:14:13Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* New pages */ + Nasal unit tests&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These are ''some'' of the '''things I have done''' on the wiki.&lt;br /&gt;
&lt;br /&gt;
''For a complete list see [[Special:Contributions/Johan_G]].''&lt;br /&gt;
&lt;br /&gt;
== New pages ==&lt;br /&gt;
* [[Howto:Add blackout and redout settings]]&lt;br /&gt;
* [[Howto:Add multi-key commands to an aircraft]]&lt;br /&gt;
* [[Aerial refueling improvement ideas and resources]]&lt;br /&gt;
* [[Aircraft interception]]&lt;br /&gt;
* [[Blacklist add-on]]&lt;br /&gt;
* [[Dassault Mirage F1]]&lt;br /&gt;
* [[FlightGear wiki:FlightGear screenshot categories]]&lt;br /&gt;
* [[Flightplan XML formats]]&lt;br /&gt;
* [[Hand camera HK 101B add-on]]&lt;br /&gt;
* [[Head tracking]]&lt;br /&gt;
* [[Help:Tables]]&lt;br /&gt;
* [[Log time-stamper add-on]]&lt;br /&gt;
* [[Nasal unit tests]]&lt;br /&gt;
* [[Pilotage and dead reckoning]]&lt;br /&gt;
* [[Pilot List]]&lt;br /&gt;
* [[TerraMaster]]&lt;br /&gt;
* [[Time in FlightGear]]&lt;br /&gt;
&lt;br /&gt;
== Larger edits ==&lt;br /&gt;
* Replaced the text in [[Howto: Edit a livery]] ([http://wiki.flightgear.org/index.php?title=Howto:_Edit_a_livery&amp;amp;oldid=36141 original text]) with my own draft ([http://wiki.flightgear.org/index.php?title=User:Johan_G/Howto:_Edit_a_livery&amp;amp;oldid=39121 Final draft]).&lt;br /&gt;
* [[Help:Templates]] – Rewritten it more or less completely ([http://wiki.flightgear.org/index.php?title=Help%3ATemplates&amp;amp;diff=64365&amp;amp;oldid=44878 diff])&lt;br /&gt;
* [[Help:Formatting]] – Complete rewrite of the former [http://wiki.flightgear.org/index.php?title=Help:Tutorial&amp;amp;oldid=42573 Help:Tutorial]&lt;br /&gt;
* [[Help:Your first article]] – Complete rewrite of [http://wiki.flightgear.org/index.php?title=Help:Your_first_article&amp;amp;oldid=64321 Help:Your first article]&lt;br /&gt;
&lt;br /&gt;
== Templates ==&lt;br /&gt;
* A lot of template documentation&lt;br /&gt;
* Unification of the template documentation style (manually editing all existing template documentation!)&lt;br /&gt;
* {{tl|Welcome to the wiki}}&lt;br /&gt;
* {{tl|alds}} – For use on the [[Airliner Development Status]] page&lt;br /&gt;
* {{tl|due date}} – When you want to show one text before and another one after a certain date.&lt;br /&gt;
* {{tl|issue}} – For referencing bug tracker issues (be that bugs or feature requests).&lt;br /&gt;
* {{tl|key press}} – For illustrating key presses and combinations.&lt;br /&gt;
* {{tl|LangSwitch}} – For showing different texts depending on the language of a page.&lt;br /&gt;
* {{tl|rule of thumb}} – For rules of thumb, like 2 kt is about 1 m/s.&lt;br /&gt;
* {{tl|wikipedia}} – For links to Wikipedia.&lt;br /&gt;
* {{tl|yes}}, {{tl|partial}}, {{tl|no}} and {{tl|n/a}} – For use in various tables.&lt;br /&gt;
* Split up [http://wiki.flightgear.org/index.php?title=Template:Issue_Tracker&amp;amp;oldid=83057 &amp;lt;nowiki&amp;gt;{{Issue Tracker}}&amp;lt;/nowiki&amp;gt;] into {{tl|register to sf}}, {{tl|create ticket}} and {{tl|tickets}}.&lt;br /&gt;
* {{tl|fg screenshot cat}} – To lessen the typing when adding descriptions to FlightGear screenshot categories.&lt;br /&gt;
* {{tl|screenshot cat}} - To add to pages about subjects there are a screenshot category for.&lt;br /&gt;
* {{tl|readme file}} – To link to readme files in [[$FG_DATA]]/Docs.&lt;br /&gt;
* {{tl|fgaddon revision}}, {{tl|fgdata commit}}, {{tl|simgear commit}} and {{tl|flightgear commit}} – For links to revision/commit summaries on the repositories.&lt;br /&gt;
&lt;br /&gt;
== Uploaded images ==&lt;br /&gt;
* [[Special:ListFiles/Johan_G]]&lt;br /&gt;
&lt;br /&gt;
[[Category:User pages]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Software_testing&amp;diff=143690</id>
		<title>Software testing</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Software_testing&amp;diff=143690"/>
		<updated>2026-03-14T12:12:20Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +category: Software testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Note|There’s already the test_suite in {{fg src file|path=test_suite}} using [[Cppunit effort|CppUnit]], thanks to some hard work by [[User:Bugman|Edward]]. We need more tests written for it; submissions are welcome. (Pick an area of interest)&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36972720/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{See also|Cppunit effort}}&lt;br /&gt;
&lt;br /&gt;
FlightGear developers use various testing tools. This includes automated testing via unit tests in [[SimGear]] and a full test suite with multiple test categories in the [[FlightGear Git|flightgear repository]], as well as manual in-sim testing. Writing tests is one of the best ways to jump into FlightGear development.&lt;br /&gt;
&lt;br /&gt;
One improving area is unit testing: certain areas and features (e.g., carrier start) now 'can't break.' As we add testing in additional areas (e.g., Multi-player, AI, protocols, and replay are all possible), we increase the baseline quality and have a clearer idea when we make incompatible changes. (The idea is that we capture the 'supported API' in the tests: when an aircraft deviates from that, we can decide to add another test case, fix the aircraft, etc). Of course, there are some pretty significant areas where Automated Testing Is Hard (TM).&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37078825/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SimGear ==&lt;br /&gt;
&lt;br /&gt;
Unit testing for the [[SimGear]] sources uses the CMake CTest unit testing infrastructure. Several tests use the BOOST unit testing infrastructure tied to the build system using CTest; however, the FlightGear developers are shifting towards eliminating BOOST, so CPPUnit and CTest tests are preferred.&lt;br /&gt;
&lt;br /&gt;
=== Building and running the SimGear tests ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ make&lt;br /&gt;
$ ctest&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== FlightGear ==&lt;br /&gt;
&lt;br /&gt;
Testing of the [[FlightGear Git|flightgear sources]] is via a comprehensive test suite implemented using [https://www.freedesktop.org/wiki/Software/cppunit/ CppUnit], a port of the famous JUnit framework.&lt;br /&gt;
&lt;br /&gt;
=== Building the test suite ===&lt;br /&gt;
&lt;br /&gt;
You must build FlightGear from the source using cmake to run the tests. See [[Building FlightGear]] for details.&lt;br /&gt;
&lt;br /&gt;
Once you have your cmake build environment, do the following:&lt;br /&gt;
# Change to your FlightGear build* directory&lt;br /&gt;
# Enable building the tests by setting a cmake variable:  &amp;lt;code&amp;gt;cmake -DBUILD_TESTING=ON .&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ensure the &amp;lt;code&amp;gt;[[$FG ROOT|$FG_ROOT]]&amp;lt;/code&amp;gt; environment variable points to fgdata e.g. &amp;lt;code&amp;gt;$FG_INSTALL_DIR/share/fgdata&amp;lt;/code&amp;gt;&lt;br /&gt;
# Build the test suite:  &amp;lt;code&amp;gt;make test_suite&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Building the test suite will also run a test because you will typically want to write a test and then immediately compile and run it.&lt;br /&gt;
&lt;br /&gt;
 * ls in this directory should include CMakeCache.txt, cmake_install, and others. You do not want to run cmake in the flightgear sources directory, which includes files such as AUTHORS, COPYING, INSTALL, etc.&lt;br /&gt;
&lt;br /&gt;
On a Windows MSVC-based build environment, after generating files with cmake, run the following code:&lt;br /&gt;
# cmake --build . --config RelWithDebInfo --target test_suite/test_suite&lt;br /&gt;
&lt;br /&gt;
=== Running the test suite ===&lt;br /&gt;
&lt;br /&gt;
To run the test suite, simply run &amp;lt;code&amp;gt;./test_suite/fgfs_test_suite&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Executing fgfs_test_suite will run the entire test suite and print a Synopsis of results, as shown below.&lt;br /&gt;
&lt;br /&gt;
 Synopsis&lt;br /&gt;
 ========&lt;br /&gt;
 &lt;br /&gt;
 System/functional tests ....................................... [ OK ]&lt;br /&gt;
 Unit tests .................................................... [ OK ]&lt;br /&gt;
 Simgear unit tests ............................................ [ OK ]&lt;br /&gt;
 FGData tests .................................................. [ OK ]&lt;br /&gt;
 Synopsis ...................................................... [ OK ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can also run individual test cases. Run &amp;lt;code&amp;gt;./test_suite/fgfs_test_suite -h&amp;lt;/code&amp;gt; to see the various options&lt;br /&gt;
&lt;br /&gt;
For example, fgfs_test_suite --log-level=alert -d -u GPSTests will run the GPS unit tests while displaying the output.&lt;br /&gt;
&lt;br /&gt;
=== Why write unit tests? ===&lt;br /&gt;
&lt;br /&gt;
A well-tested piece of software will have a much lower bug count/load. An extensive test suite with &amp;quot;'unit tests,'&amp;quot; system/functional tests, GUI tests, installer tests, and other categories of tests can significantly help in this regard.&lt;br /&gt;
&lt;br /&gt;
The benefits of not just chasing clear &amp;quot;wins&amp;quot; are great: An excellent learning experience for new developers; the ability to catch latent, unreported bugs; making it easier to refactor current code by creating a safety net; making it easier for current developers to accept new contributions (when accompanied with passing tests); helping other test writers by contributing to the standard test suite infrastructure; and being able to check for memory leaks or other issues via Valgrind easily.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36977686/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are a new developer, jump in and write any test! It does not need to catch a bug. Do whatever you wish! Just dive into this shallow end, and you'll see that the water is not cold.&lt;br /&gt;
&lt;br /&gt;
You are writing a test as a safety net. You write the test to pass, make your changes, and then make sure that the test still passes. Then, you push both the test and core changes.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36977465/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It'd be better to work in a specific area of interest to you and submit merge requests. When reviewed, that would usually trigger some C++ feedback, but we aren't looking for perfection here. The feedback you receive during the open and public review process increases our overall pool of knowledge of what best practice looks like, even if a given commit is less than perfect.&lt;br /&gt;
&lt;br /&gt;
Having 10 or 20 people actively contributing correct and reasonable code is more important than three people contributing perfect, micro-optimised C++. &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36951247/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Benefits of unit testing ===&lt;br /&gt;
There are lots of benefits to writing tests that pass.&lt;br /&gt;
&lt;br /&gt;
Benefits include :&lt;br /&gt;
&lt;br /&gt;
* Learning! New developers can learn a ton from writing several passing tests in the area they are interested in. This is one of the quickest ways to learn about a pre-existing and mature code base. You have zero worries about breaking things.&lt;br /&gt;
&lt;br /&gt;
* Latent bug uncovering. One will probably fail every ten tests you write, expecting them to pass. Tests may uncover unexpected behavior that a developer can improve.&lt;br /&gt;
&lt;br /&gt;
* Refactoring. If we had 10,000 passing tests (assuming universal test coverage), large-scale refactoring of the entire code base would be quick and reliable. It would enable refactoring on a scale currently unimaginable. I cannot emphasize enough how much of a benefit this would be.&lt;br /&gt;
&lt;br /&gt;
* Developer turnover. Again, if we had 10,000 passing tests (assuming universal test coverage), it would encourage new developers, giving them confidence that their changes will not cause problems. It is a safety net. It also would provide existing developers peace of mind when a new developer works in one of the dark parts of FlightGear that no current developer understands (there are plenty of those).&lt;br /&gt;
&lt;br /&gt;
* Test suite infrastructure. The more passing tests written, the better the test suite infrastructure will become. We can already do a lot, but adding more passing tests will help other test writers.&lt;br /&gt;
&lt;br /&gt;
* Memory checking. Running a single test through Valgrind is fantastic. Running FlightGear through Valgrind is close to impossible. One can write tests that pass but are useful under Valgrind to catch memory leaks!&lt;br /&gt;
&lt;br /&gt;
* Code quality and standards. If a test compiles on all OSes without warning, it passes, and Valgrind gives you an ok, it is good enough. You don't need to be a C++ expert to dive into this shallow end of the pool.&lt;br /&gt;
&lt;br /&gt;
=== Bootstrapping completely new tests ===&lt;br /&gt;
To start diving straight into the test suite code, firstly copy what has been done in this commit:  {{repo link&lt;br /&gt;
| site   = sf&lt;br /&gt;
| user = edauvergne&lt;br /&gt;
| repo   = flightgear&lt;br /&gt;
| commit = 8474df&lt;br /&gt;
| view   = commit&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Just modify all names for a JSBSim test (or any other test fixture you&lt;br /&gt;
want to code). You should then be able to compile and check that your&lt;br /&gt;
new test dummy () test passes as expected. You can then slowly build up&lt;br /&gt;
from this basic infrastructure as you learn the fgfs internals, c++,&lt;br /&gt;
and git skills required for implementing your test on your fork's new&lt;br /&gt;
development branch :)&lt;br /&gt;
&lt;br /&gt;
For a step by step description of how to add new tests &lt;br /&gt;
 see: [[Software Testing/Adding CPPUnit Tests]]&lt;br /&gt;
&lt;br /&gt;
For a detailed explanation of everything in the flightgear/test_suite folder, top to bottom level, &lt;br /&gt;
 see: [[Software Testing/Flightgear Test Suite Details]]&lt;br /&gt;
&lt;br /&gt;
===Headless testing===&lt;br /&gt;
{{Main article|FlightGear Headless}}&lt;br /&gt;
&lt;br /&gt;
For an FDM+systems test, we should run FG without a renderer (which is what the test_suite does) to benchmark the pure C++ performance of whatever system we care about (FDM or whatever). But a few hours playing with 'perf' on Linux or Instruments on macOS will show you that OSG + the GL drivers use 80% of our CPU time, and hence Amdhal's law will always get you.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36977666/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Graphics testing===&lt;br /&gt;
{{See also|FlightGear Benchmark}}&lt;br /&gt;
&lt;br /&gt;
Create test-case scenes where you can quickly measure differences and compare via screenshots. The brain/eye/memory are terrible at this stuff, setup something you can load from the command line via a script to test old/new versions, if possible. &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36959002/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, a test would use several rc files (.[[fgfsrc]] variants, with different renderers and threading modes, including static values for:&lt;br /&gt;
*c172p at some parking in a detailed airport&lt;br /&gt;
*camera set with a specific direction and field of view&lt;br /&gt;
*AW with specific METARs around (if not possible, BW with specific METAR)&lt;br /&gt;
*fixed rendering settings ( &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36975122/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can load a [[Instant Replay|replay tape]] on startup. Since the FDM and User interface are unavailable, replays are suitable for testing rendering and performance.&lt;br /&gt;
&lt;br /&gt;
But essentially, small amounts of shell script + Nasal hacking, can implement any of these methods,  and any of them would be welcome additions. The unit-test framework is excellent for lower-level tests run by developers (i.e., 'Does the API call produce the right results in the system?'), but a smoke test that regular users can run would be ideal. &lt;br /&gt;
&lt;br /&gt;
A rendering performance would likely do the following:&lt;br /&gt;
&lt;br /&gt;
*Select some particular rendering settings (clouds, draw distance, etc)&lt;br /&gt;
*Run a saved fgtape recording&lt;br /&gt;
* Record the mean/min/max FPS during this and save it in some text file/copy to the clipboard&lt;br /&gt;
&lt;br /&gt;
So yes, if anyone wants to work on the above, the code is all there. Please jump in and start hacking. I don't think it needs any more from the core code, but as always, please ask if it does.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36975213/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, when describing a /rendering/ test (to establish FPS), the advantage of a replay tape is that the actual position (and, therefore, the rendered scene) will be 100% consistent across different computers.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that the CPU use of the FDM+systems is typically &amp;lt; 10% of our total CPU use, even when running OSG single-threaded, so for a rendering performance test, whether the FD is run or not is probably noise compared to other things that do run (Nasal, Canvas for example)&lt;br /&gt;
&lt;br /&gt;
Also, the Multi-monitor setup is an area that could use additional unit testing. &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36904782/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Fgdata==&lt;br /&gt;
===Nasal scripting (comments)===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
The now builtin CppUnit framework can solve all the issues&lt;br /&gt;
identified in the old [[Nasal Unit Testing Framework]] wiki article and the discussions it points to&lt;br /&gt;
and provide the full framework required.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36990615/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some very simple tests running now for the route manager, which relies on Nasal. We're skipping a few of the bigger Nasal modules (local weather, jetways) and have a few lingering issues in some other modules, but the basic concept is working. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
An exciting further step, which you might wish to discuss with Edward, is writing test checks *in* Nasal since this could be quite a fast way to test some areas of the code. There are several ways that could work, and I don't know if Edward has always planned something around this, so I won't preempt that conversation.&lt;br /&gt;
&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36764781/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we have route-manager tests which validate route_manager.nas is working correctly, and we have Canavs tests ({{fg src file|path=test_suite/simgear_tests/canvas|}}) which poke the Nasal API. &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36991200/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need more FGData testing via the test suite.&lt;br /&gt;
&lt;br /&gt;
James has suggested adding CppUnit assertions to Nasal so others can write tests in pure Nasal. James would make these changes in C++.  &lt;br /&gt;
In addition, some C++ code in a test would scan a directory for files matching a pattern, e.g., test_XYZ.nas, and run each of those automatically.&lt;br /&gt;
&lt;br /&gt;
The idea for testing Nasal would be that you write a small CppUnit&lt;br /&gt;
interface in C++ in $FG_SRC/test_suite/*_tests/ (the FGData Nasal&lt;br /&gt;
testing would be in a fgdata_tests/ directory). This would register&lt;br /&gt;
each test which points to the script in&lt;br /&gt;
$FG_SRC/test_suite/shared_data/nasal/, and the setUp() and tearDown()&lt;br /&gt;
functions would use helper functions in the fgtest namespace to start&lt;br /&gt;
and stop Nasal. The Nasal scripts could then call the CppUnit&lt;br /&gt;
assertion macros wrapped up as Nasal functions for communicating&lt;br /&gt;
failures and errors to the test suite. &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36991150/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Scanning for scripts is a great idea. Then, developers (core and content) could write tests using pure Nasal.&lt;br /&gt;
&lt;br /&gt;
However, all scripts found and executed will be seen as a single test within the test suite. So maybe we should have a $FG_SRC/test_suite/nasal_staging/ directory for the initial development of such auto-scanned scripts. But then we have someone shift them into $FG_SRC/test_suite/system_tests/, $FG_SRC/test_suite/unit_tests/, or $FG_SRC/test_suite/fgdata_tests/ later on?  That would give better diagnostics and would avoid long-term clutter.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36991198/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*First, we should probably hard code tests into the C++ framework. For this, the CppUnit assertion macros will have to be wrapped up as Nasal functions.&lt;br /&gt;
*Implement the scanning code as we need some CMake magic (probably using file(COPY, ...)).&lt;br /&gt;
*Finally, we must determine if and how to improve the Nnasaldebugging output.&lt;br /&gt;
&lt;br /&gt;
the code could go into a subdirectory in $FG_SRC/test_suite/fgdata_tests/, and the Nasal script in $FG_SRC/test_suite/shared_data/nasal/.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Core development]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Software_Testing/Flightgear_Test_Suite_Details&amp;diff=143689</id>
		<title>Software Testing/Flightgear Test Suite Details</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Software_Testing/Flightgear_Test_Suite_Details&amp;diff=143689"/>
		<updated>2026-03-14T12:11:28Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +category: Software testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Definitions =&lt;br /&gt;
&lt;br /&gt;
==Test Case==&lt;br /&gt;
 A single assertion about one or two variable values that is testable.&lt;br /&gt;
==Test Fixture==&lt;br /&gt;
 In CppUnit, a group of tests is called a TestFixture. A TestFixture provides a common environment for multiple test cases.&lt;br /&gt;
==App Component==&lt;br /&gt;
 Major parts of the application, usually corresponding to folders in flightgear/src or simgear/simgear&lt;br /&gt;
==Test Category== &lt;br /&gt;
 The type of testing: unit, system and simgear (library) tests&lt;br /&gt;
==Test Suite==&lt;br /&gt;
 All the tests and everything needed to support them&lt;br /&gt;
&lt;br /&gt;
= Top Level flightgear/test_suite folder =&lt;br /&gt;
&lt;br /&gt;
Take a look at the '''flightgear/test_suite''' folder in a file browser preferably one that allows you to see the folders on multiple levels such as a code editor like VsCode. Start in the top level of flightgear's repo sources look for a directory named '''test_suite'''.  Note that test_suite is not part of '''flightgear/src'''.&lt;br /&gt;
&lt;br /&gt;
'''flightgear/test_suite''' contains two kinds of testing resources. &lt;br /&gt;
&lt;br /&gt;
* '''Programs and data used in tests'''&lt;br /&gt;
* '''Groups of CPPUnit tests'''&lt;br /&gt;
&lt;br /&gt;
==Programs and data used in test modules==&lt;br /&gt;
&lt;br /&gt;
* individual files directly under test_suite&lt;br /&gt;
* test_data folder&lt;br /&gt;
* FGTestApi folder&lt;br /&gt;
* Groups of CPPUnit tests&lt;br /&gt;
&lt;br /&gt;
==Test Categories==&lt;br /&gt;
&lt;br /&gt;
* fgdata_tests - placeholder&lt;br /&gt;
* gui_tests - placeholder&lt;br /&gt;
* simgear_tests&lt;br /&gt;
* system_tests&lt;br /&gt;
* unit_tests&lt;br /&gt;
&lt;br /&gt;
Each of the last three test_suite/*_tests folders contain sub-folders covering some part of Flightgear's code.  They may roughly correspond to the folders under flightgear/src, but that is not a requirement.    Some areas may be covered in more than one *_tests folder.  FDM for example in unit_tests and system_tests.  &lt;br /&gt;
&lt;br /&gt;
== Folder Levels ==&lt;br /&gt;
&lt;br /&gt;
Note at this point that the maximum Folder depth is 3:&lt;br /&gt;
&lt;br /&gt;
# flightgear/test_suite Folder.&lt;br /&gt;
# Test-category folders ex: unit_tests, system_tests, simgear_tests&lt;br /&gt;
# App Component folders ex: FDM, Network, canvas.  These roughly correspond to specific flightgear/src folders, &lt;br /&gt;
&lt;br /&gt;
Expand the '''unit_tests''' test category folder&lt;br /&gt;
Expand the '''Network''' app component folder&lt;br /&gt;
&lt;br /&gt;
CPPUNIT test fixtures are implemented in test_*.cxx and test_*.hhx files int the Component category folders.&lt;br /&gt;
&lt;br /&gt;
== CMakeLists.txt, testSuite.cxx and TestSuite.cxx files ==&lt;br /&gt;
&lt;br /&gt;
CMakeLists.txt files exist at every level&lt;br /&gt;
TestSuite.cxx files are in each application component folders (bottom level)&lt;br /&gt;
testSuite.cxx is only at the top level&lt;br /&gt;
&lt;br /&gt;
# '''test_suite/CMakeLists.txt''' &amp;amp; test_suite/testSuite.cxx&lt;br /&gt;
# '''test_suite/[test-category]/CMakeLists.txt''' &lt;br /&gt;
# '''test_suite/[test-category]/[app-component]/CMakeLists.txt'''  and '''Test_suite.cxx'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
More to follow {{WIP}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_Unit_Testing_Framework&amp;diff=143688</id>
		<title>Nasal Unit Testing Framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_Unit_Testing_Framework&amp;diff=143688"/>
		<updated>2026-03-14T12:10:34Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +hatnote: For the Nasal unit testing implemented 2020, see Nasal unit tests; +category: Software testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{for|the Nasal unit testing implemented 2020|Nasal unit tests}}&lt;br /&gt;
{{infobox subsystem&lt;br /&gt;
|image =&lt;br /&gt;
|name =Nasal Unit Testing Framework&lt;br /&gt;
|started= 02/2014 (stalled)&lt;br /&gt;
|description = Unit Testing support for Nasal&lt;br /&gt;
|status = RFC &lt;br /&gt;
|maintainers  = F-JYL, dbelcham, Hooray, Philosopher&lt;br /&gt;
|developers = dbelcham, F-JYL (since 02/2014),&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Template:Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Status 06/2013 ==&lt;br /&gt;
We do not currently have any established unit testing framework for Nasal. For the time being, this whole discussion is just about coming up with the requirements and a possible design. &lt;br /&gt;
The intent is to explore the idea of being able to run isolated unit tests against Nasal scripts. The idea was being able to do something like this:&lt;br /&gt;
&lt;br /&gt;
See also {{flightgear commit|d7a680}}.&lt;br /&gt;
&lt;br /&gt;
=== Concept ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
standalone-nasal.exe ATR72-FMC-tests.nas&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and see output like this:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
When_calculating_deflection_that_is_greater_than_the_provided_maximum&lt;br /&gt;
the_provided_maximum_should_be_returned (failed: expected 180 was 99)&lt;br /&gt;
&lt;br /&gt;
When_calculating_deflection_from_33_degrees_to_90_degrees&lt;br /&gt;
the_result_should_be_67_degrees (failed: expected 67 was 66)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== The standalone Nasal interpreter ===&lt;br /&gt;
Based on what I read about the current stand-alone interpreter this should be fairly easy to do. That should now be possible, the nasal-standalone branch builds successfully for Windows (make sure to have boost available for building cppbind):&lt;br /&gt;
&lt;br /&gt;
Here's the Nasal standalone interpreter as part of SimGear: {{gitorious source|proj=fg|repo=hoorays-simgear|branch=topics/nasal-standalone|view=shortlog}}&lt;br /&gt;
Just check out the branch named &amp;quot;nasal-bin&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To build it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd $SG_SRC&lt;br /&gt;
mkdir BUILD&lt;br /&gt;
cd BUILD&lt;br /&gt;
cmake ../ -DENABLE_TESTS=ON -DSIMGEAR_HEADLESS=ON -DENABLE_SOUND=OFF -DENABLE_LIBSVN=OFF&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Basically&lt;br /&gt;
&lt;br /&gt;
* create and use a separate build folder, separate from the source tree&lt;br /&gt;
* configure the build via -DENABLE_TESTS=ON -DSIMGEAR_HEADLESS=ON -DENABLE_SOUND=OFF -DENABLE_LIBSVN=OFF&lt;br /&gt;
* build&lt;br /&gt;
* report back &lt;br /&gt;
&lt;br /&gt;
Note that there's no need to actually install anything (make install), because we are just using the SimGear library to build a standalone nasal-bin binary, nothing else.&lt;br /&gt;
&lt;br /&gt;
Let us know if there are still any windows-specific build errors so that we can fix the config file. It should give you a &amp;quot;nasal-bin.exe&amp;quot; in $SG_BUILD/simgear/nasal/ that runs just Nasal scripts, no FG APIs whatsoever - you '''need''' to pass a valid Nasal script when running the file.&lt;br /&gt;
&lt;br /&gt;
You should be able to &amp;quot;make test&amp;quot; to run a bunch of standard Nasal tests from the original Nasal repository, which are to be found in $SG_SRC/nasal/tests: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
Meanwhile, the build actually works for Windows using the MingW compiler - providing a nasal-bin.exe, which I cannot test currently because I don't have a Windows VM available: http://www.speedyshare.com/MbBTA/download/nasal-bin.exe{{dead link}}&lt;br /&gt;
&lt;br /&gt;
To run it, open a shell (START/EXECUTE cmd/command) and go to the folder of the binary, add a simple Nasal script and run &amp;quot;nasal-bin script.nas&amp;quot;, i.e. use one of the scripts in: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
=== Roadmap ===&lt;br /&gt;
* get the stand-alone interpreter compiling and running against both windows and Linux (currently there are a bunch of libraries used that aren't available on Windows) {{done}}&lt;br /&gt;
* build a set of Nasal scripts that provide stubs for native fg calls (getprop/setprop/etc) {{Not done}}&lt;br /&gt;
* build out testing script with the ability to verify values and report failures to the console {{Not done}}&lt;br /&gt;
* send a patch upstream, so that a standalone Nasal interpreter is included in each upcoming release {{Not done}}&lt;br /&gt;
&lt;br /&gt;
Once we have those three things we would be able to write and execute tests independent of FG and still have them be meaningful. The key thing to remember is that this would be only for isolated unit tests. For integration tests (verifying that different systems, whether 2 different scripts/methods/application, work together correctly) we would need to think about a different approach.&lt;br /&gt;
&lt;br /&gt;
It should be doable to teach the nasal-bin.exe to check $FG_ROOT, and use that if available to load a semi-plausible FG environment (API-wise) - using some fancy meta-programming tricks, most of the default APIs could probably be wrapped, without too much manual work involved. Philosopher could be truly instrumental here, because he really has a deep understanding of some of the more esoteric tricks that can be done in Nasal space, referring to advanced uses of compile(), bind(), call(), closure() and caller() - which make meta-programming a fantastic experience. Basically, familiarity with this handful of APIs, can save tons of time: http://plausible.org/nasal/&lt;br /&gt;
&lt;br /&gt;
There's quite a lot of stuff possible in Nasal, that nobody ever used in FG - Philosopher has started writing a bunch of tutorials, for example see: [[Nasal Meta-Programming]]&lt;br /&gt;
&lt;br /&gt;
And you can take a look at some of the scripts in the standalone branch, which support fancy constructs like dependency resolution using import(&amp;quot;foo&amp;quot;); but also completely sandboxed/wrapped environments: {{gitorious source|proj=fg|repo=hoorays-simgear|view=tree|branch=topics/nasal-standalone|path=simgear/nasal/tests}}&lt;br /&gt;
&lt;br /&gt;
These are regression tests developed by the Nasal developer himself, so not true unit testing - but only regression tests for the interpreter itself.&lt;br /&gt;
&lt;br /&gt;
=== Contributing ===&lt;br /&gt;
If possible, new code should be contributed to the maintainers, ideally even a branch of FG_ROOT, because that will make it easier to directly integrate such a unit testing system with all the code we got in $FG_ROOT/Nasal.&lt;br /&gt;
&lt;br /&gt;
== Problem ==&lt;br /&gt;
One of the things that is most frustrating and time consuming when working with Nasal scripts is the brute force and manual nature of testing the scripts. A simple misspelling in a custom script can take 5-10, or more, minutes to fix from the point of finding it (shut down FG, change script, startup FG and get back to a point in the sim where the code will execute). I realize that Nasal is tightly coupled to FG at this point and that most scripts won't run without access to the property tree. That doesn't mean that it can't be done though (mocks, fakes, stubs, etc).&lt;br /&gt;
&lt;br /&gt;
Most scripts in FlightGear use a plethora of APIs and FG-specific modules, so FG has become a runtime dependency (APIs, data structures like the property tree, and &amp;quot;live&amp;quot; state),&lt;br /&gt;
&lt;br /&gt;
== Test/Fail Passes ==&lt;br /&gt;
Just because a language is dynamic doesn't mean that the code-test-fail/pass feedback loop has to be a long one. Just look at dynamic language communities like Ruby and you'll see that automated testing (both behavioral and state), in combination with continuous integration, is used to try to move those failures from application run-time to test suite run-time. Dynamic language communities do this with a lot of success (both in tightening the feedback loop and improving quality).&lt;br /&gt;
&lt;br /&gt;
== Wrapping dependencies ==&lt;br /&gt;
FGs loading of scripts is stopping us from being able to change-reload right in app. What I was thinking is to remove the app (FG in this case) from the equation completely. For a lot of systems scripts the only interaction that they have with FG is through the property tree. Between these interactions the nasal systems scripts being developed for most aircraft are simply state based; they read some values, do some calculations and set some values. If we know the inputs (method parameters and getprop calls), we know the outputs (method return values and setprop calls).&lt;br /&gt;
What I do in other languages is to replace the call to those &amp;quot;external dependencies&amp;quot; (in this case getprop and setprop) with known implementation. So a call to getprop(&amp;quot;/orientation/yaw-deg&amp;quot;) in a specific test scenario would be configured to always return &amp;quot;33.5&amp;quot;. My understanding of the inner workings of Nasal are limited, but I would think that one should be able to override get/setprop due to the dynamic nature of the language. That said, I can't find any definitions for those in the nasal-standalone codebase.&lt;br /&gt;
&lt;br /&gt;
If someone could point me to where the get/setprop stuff is then I'd be a step closer to exploring my theory of having standalone Nasal running against developer defined property tree values as a mechanism for automated testing.&lt;br /&gt;
&lt;br /&gt;
Wrapping APIs is simple to do in Nasal, too - without even requiring C/C++ changes, a standalone testbed could be scripted in Nasal like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var tree = {};&lt;br /&gt;
&lt;br /&gt;
var isalpha = func(n) n &amp;gt;= `a` and n &amp;lt;= `z` or n &amp;gt;= `A` and n &amp;lt;= `Z`;&lt;br /&gt;
var isdigit = func(n) n &amp;gt;= `0` and n &amp;lt;= `9`;&lt;br /&gt;
&lt;br /&gt;
var sanitize = func(p) {&lt;br /&gt;
    if (!p) die();&lt;br /&gt;
    if (p[0] == `/`) p = substr(p, 1, nil);&lt;br /&gt;
    parts = split(&amp;quot;/&amp;quot;, p);&lt;br /&gt;
    for (var i=0; i&amp;lt;size(parts); i+=1) {&lt;br /&gt;
        if (parts[i] == &amp;quot;&amp;quot;) {&lt;br /&gt;
            if (i == size(parts)-1)&lt;br /&gt;
                parts = parts[:i-1];&lt;br /&gt;
            else parts = parts[:i-1] ~ parts[i+1:];&lt;br /&gt;
            i-=1;&lt;br /&gt;
        } else {&lt;br /&gt;
            for (var j=0; j&amp;lt;size(parts[i]); j+=1) {&lt;br /&gt;
                if (parts[i][j] == `[`) break;&lt;br /&gt;
                if (parts[i][j] != `-` and !isalpha(parts[i][j]) and&lt;br /&gt;
                    parts[i][j] != `_` and !isdigit(parts[i][j]) and&lt;br /&gt;
                    parts[i][j] != `.`) die(&amp;quot;bad character in name &amp;quot;~parts[i]~&amp;quot; at index &amp;quot;~j~&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            if (j == size(parts[i]))&lt;br /&gt;
                parts[i] ~= &amp;quot;[0]&amp;quot;;&lt;br /&gt;
            elsif (parts[i][-1] != `]`) die(&amp;quot;bad index specifier in string &amp;quot;~parts[i]~&amp;quot;.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    var p = &amp;quot;/&amp;quot;;&lt;br /&gt;
    foreach (var part; parts)&lt;br /&gt;
        p ~= part;&lt;br /&gt;
    return p;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
# wrappers for the FG setprop/getprop APIs:&lt;br /&gt;
var setprop = func(p, value) tree[ sanitize(p) ] = value;&lt;br /&gt;
var getprop = func(p) return tree[ sanitize(p) ];&lt;br /&gt;
 &lt;br /&gt;
# some tests:&lt;br /&gt;
&lt;br /&gt;
var path = [&amp;quot;/foo/bar&amp;quot;, &amp;quot;/foo[0]/bar[0]&amp;quot;,&amp;quot;/foo[0]/bar[0]/&amp;quot;, &amp;quot;/foo/bar[0]&amp;quot;,&amp;quot;/foo[0]/bar/&amp;quot;,&amp;quot;/foo[0]/bar/&amp;quot;];&lt;br /&gt;
var value = &amp;quot;MyUniqueValue&amp;quot;;&lt;br /&gt;
setprop(path[0], value);&lt;br /&gt;
foreach(var p; path)&lt;br /&gt;
  if (getprop(p) != value) die(&amp;quot;sanitize() implementation is broken&amp;quot;);&lt;br /&gt;
print(&amp;quot;sanitize() looks good!\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# init your tree:&lt;br /&gt;
setprop(&amp;quot;/orientation/yaw-deg&amp;quot;, 33.5);&lt;br /&gt;
print(&amp;quot;yaw-deg is:&amp;quot;, getprop(&amp;quot;/orientation/yaw-deg&amp;quot;) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Basically, you can override ANYTHING in Nasal - even library/extension functions - see above, you don't even need to look at the Nasal C code.&lt;br /&gt;
&lt;br /&gt;
We would need to use custom script-specific wrappers, instead of the main FG/Nasal APIs and modules - so that your Nasal code *never* uses the APIs directly, that way you can easily have different implementations - i.e.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var debug_profile = {};&lt;br /&gt;
var runtime_profile = {};&lt;br /&gt;
var current_profile = nil;&lt;br /&gt;
&lt;br /&gt;
runtime_profile.systime = systime;&lt;br /&gt;
debug_profile.systime = my_systime;&lt;br /&gt;
&lt;br /&gt;
# set the API profile:&lt;br /&gt;
current_profile = runtime_profile;&lt;br /&gt;
&lt;br /&gt;
# And then only ever make calls through active_profile.systime():&lt;br /&gt;
&lt;br /&gt;
print( current_profile.systime() );&lt;br /&gt;
&lt;br /&gt;
# or simply override the global symbols during initialization:&lt;br /&gt;
var systime = current_profile.systime;&lt;br /&gt;
print( systime() );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Suites ==&lt;br /&gt;
&lt;br /&gt;
I'd rather not have to launch FG to run my tests. Ideally I'd like to be able to build up a suite of tests that I can run in an automated fashion to ensure that all are still operating as expected at any time. In my close to ideal world I would want to be able to execute the automate tests (and exercise the scripts as a result) dozens of times per hour. Launching FG and manually triggering this from the console would limit me to a few times per hour. Add in more complicate scripts which require extensive state in the property tree to test specific scenarios and I might be lucky to manually execute these tests a couple of times an hour. Manually launching the tests would also probably mean that I'd have to remember to configure and run each and every scenario...something I would never remember to do. Each time I forgot I'd possibly be introducing issues into my code.&lt;br /&gt;
&lt;br /&gt;
The rigour around this isn't for everyone, but it is how I derive the most confidence that I'm delivering the highest quality code possible. This all came to light because of a defect in the ATR the Omega95 and I have built (mostly him) that could have easily been found if we had had some automate scenario tests written.&lt;br /&gt;
&lt;br /&gt;
I'm working with the ATR's FMC. The time and difficulty with it is that if I want to change and retest any of the different segments in the flight plan (especially the SID and STAR) I need to be able to reset my self to a specific position and property tree state. If I'm testing the transition from waypoint 1-&amp;gt;2 in the SID then I need to start pre waypoint 1 which means on the ground, starting up, keying in the flight plan, etc. Worse is the transition from the flight plan to the first STAR waypoint. If I could write automated tests for all of these scenarios plus dozens of others I can think of then my development-testing feedback loop would tighten immensely.&lt;br /&gt;
&lt;br /&gt;
I had the idea, which you implemented above, of just overriding the get/setprop un the scripts. I guess taking that Sudafed might pay off in more ways than one.&lt;br /&gt;
Ultimately what I'd like to have is an implementation of something like the jUnit/xUnit/nUnit testing frameworks. Tonight's goal will be to hack out a rough implementation that allows for isolation of the property tree.&lt;br /&gt;
&lt;br /&gt;
== Integration &amp;amp; Adoption ==&lt;br /&gt;
Unit testing support in Nasal would certainly be beneficial - but it would need to be added to FG at a library-level, i.e. in $FG_ROOT/Nasal, so that people have to use it, and have an advantage when using it - sort of like the RoR example you mentioned previously.&lt;br /&gt;
&lt;br /&gt;
I could see that being useful for many things, even outside aircraft development - but thinking in th most generic terms, we need to find a compromise that will not just work for specialists who have a decade of unit testing experience, but also our average aircraft developers.&lt;br /&gt;
&lt;br /&gt;
Scripting-wise, I think we really only got a handful of people here who regularly write Nasal code and who would also see the merits and potentially adopt the system.&lt;br /&gt;
&lt;br /&gt;
People would only be likely to actually use that if they have a corresponding developers background, so it would need to be designed right into the framework and touch lots of places in $FG_ROOT and $FG_AIRCRAFT - I only see a handful of aircraft developers here who would go that route and actually have the mental capacity, and developer mentality to see the merits here.&lt;br /&gt;
&lt;br /&gt;
Probably,a handful of people would be able to use it, but if it's well documented, and if it actually supports features not provided otherwise, it could gain traction - so it would need to be more compelling than the current workflow obviously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A unit testing framework is definitely going to be useful for $FG_ROOT as a whole, not just aircraft/instrument developers. Obviously, one of the first steps will be documenting the whole thing with tutorials, so that people can start adopting it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143687</id>
		<title>Nasal unit tests</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_unit_tests&amp;diff=143687"/>
		<updated>2026-03-14T12:02:16Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Created page with &amp;quot;{{stub}} '''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function.  == Writing unit test files == The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).  The name of the test files usually begin with &amp;lt;code...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
'''Nasal unit tests''' is way to test that Nasal functions returns expected values. Writing unit tests for functions allow you to run tests that check if you made errors when writing the function or check if you introduced bugs when extending or maintaining the function.&lt;br /&gt;
&lt;br /&gt;
== Writing unit test files ==&lt;br /&gt;
The Nasal test files are regular Nasal files but with the file ending &amp;lt;code&amp;gt;.nut&amp;lt;/code&amp;gt; (for Nasal unit test).&lt;br /&gt;
&lt;br /&gt;
The name of the test files usually begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt; and then usually use the same file name as the module that are to be tested.&lt;br /&gt;
&lt;br /&gt;
=== Optional header comment block ===&lt;br /&gt;
Test files often begin with a comment block with a header with a short description of what the file is, followed by a commented line with how to run the test, foe example something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
# Test file for multiplayer time synchronization&lt;br /&gt;
# File:    test_mp_time_sync.nut&lt;br /&gt;
# Author:  Iam Cardholder&lt;br /&gt;
# Created: 2026-03-14&lt;br /&gt;
# Licence: GPLv2 or later&lt;br /&gt;
#-------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
# fgcommand(&amp;quot;nasal-test&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;:&amp;quot;test_mp_time_sync.nut&amp;quot;}));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optional setup and tear-down functions ===&lt;br /&gt;
Optionally the unit test file can have a &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; function. These are useful if for example some other module need to be loaded.&lt;br /&gt;
&lt;br /&gt;
Most of the time these functions are used for printing to the console and/or log that the test has started or finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Optional setup function&lt;br /&gt;
var setUp = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests begin&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# Optional tear-down function&lt;br /&gt;
var tearDown = func {&lt;br /&gt;
    logprint(LOG_INFO, &amp;quot;mp_time_sync tests finished&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test functions and test cases ===&lt;br /&gt;
The names of the test functions usually matches the names of the functions to test except they, like the name of the test file, begin with &amp;lt;code&amp;gt;test_&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tests are written as functions with one or more test cases. If needed modules can be loaded, variables can be defined etc. that will then be used for each test case.&lt;br /&gt;
&lt;br /&gt;
You can have as many test cases as needed and each test case can be using using one of four different tests:&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.fail()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.assert_doubles_equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;unitTest.equal()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
The tests are run with the FGCommands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to Nasal test file&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fgcommand(&amp;quot;nasal-test-dir&amp;quot;, props.node.new({&amp;quot;path&amp;quot;:&amp;quot;&amp;lt;path to directory with Nasal test files&amp;gt;&amp;quot;}));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Software testing]]&lt;br /&gt;
* [[Nasal Unit Testing Framework]] (An older effort.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Scripting/NasalUnitTesting.cxx}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
* {{fgdata source|path=Nasal/std.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/string.nut}}&lt;br /&gt;
* {{fgdata source|path=Nasal/test_emesary.nut}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;br /&gt;
[[Category:Software testing]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Category:Software_testing&amp;diff=143686</id>
		<title>Category:Software testing</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Category:Software_testing&amp;diff=143686"/>
		<updated>2026-03-14T09:59:28Z</updated>

		<summary type="html">&lt;p&gt;Johan G: More specific header.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{en|Category with articles related to [[software testing]].}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Core development]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Category:Software_testing&amp;diff=143685</id>
		<title>Category:Software testing</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Category:Software_testing&amp;diff=143685"/>
		<updated>2026-03-14T09:56:11Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Created page with &amp;quot;{{en|Articles related to software testing.}}  Category:Core development Category:Nasal&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{en|Articles related to [[software testing]].}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Core development]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Add_procedures_to_the_route_manager&amp;diff=143676</id>
		<title>Howto:Add procedures to the route manager</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Add_procedures_to_the_route_manager&amp;diff=143676"/>
		<updated>2026-03-08T16:06:52Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Waypoints */ Table cleanup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{Autoflight Navigation}}&lt;br /&gt;
&lt;br /&gt;
A free way to get more SID and STAR procedures for the FlightGear Route Planner.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
 | url    =  https://forum.flightgear.org/viewtopic.php?p=313123#p313123 &lt;br /&gt;
 | title  =  &amp;lt;nowiki&amp;gt; routeplanner stir sid &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | author =  &amp;lt;nowiki&amp;gt; Sarith &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | date   =  Jun 24th, 2017 &lt;br /&gt;
 | added  =  Jun 24th, 2017 &lt;br /&gt;
 | script_version = 0.40 &lt;br /&gt;
 }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make some procedures yourself. Charts are easy to find and a quick search will find you the syntax. This is what FlightGear is all about; doing it yourself and sharing it to others. A quick search would also find existing packages.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
 | url    =  https://forum.flightgear.org/viewtopic.php?p=313128#p313128 &lt;br /&gt;
 | title  =  &amp;lt;nowiki&amp;gt; Re: routeplanner stir sid &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | author =  &amp;lt;nowiki&amp;gt; Parnikkapore &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | date   =  Jun 24th, 2017 &lt;br /&gt;
 | added  =  Jun 24th, 2017 &lt;br /&gt;
 | script_version = 0.40 &lt;br /&gt;
 }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Installing procedure files ==&lt;br /&gt;
=== Manually ===&lt;br /&gt;
SID and STAR procedures are found in the &amp;lt;code&amp;gt;procedures.xml&amp;lt;/code&amp;gt; file for an airport. There is at most one procedures file per airport and it is located in the root of the airport directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[[$FG_SCENERY]]/Airports/I/C/A/ICAO.procedures.xml&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the ICAO code of your airport replacing &amp;lt;code&amp;gt;ICAO&amp;lt;/code&amp;gt;.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
 | url    =  https://forum.flightgear.org/viewtopic.php?p=313362#p313362 &lt;br /&gt;
 | title  =  &amp;lt;nowiki&amp;gt; Re: routeplanner stir sid &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | author =  &amp;lt;nowiki&amp;gt; Parnikkapore &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | date   =  Jun 28th, 2017 &lt;br /&gt;
 | added  =  Jun 28th, 2017 &lt;br /&gt;
 | script_version = 0.40 &lt;br /&gt;
 }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to import SID or STAR procedures for Phuket International Airport (VTSP), you name the file &amp;lt;code&amp;gt;VTSP.procedures.xml&amp;lt;/code&amp;gt; and put it in the directory&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[[$FG_SCENERY]]/Airports/V/T/S/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In there you will likely find for example &amp;lt;code&amp;gt;VTSP.twr.xml&amp;lt;/code&amp;gt;, but not an existing &amp;lt;code&amp;gt;VTSP.procedures.xml&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VTSP&amp;lt;/code&amp;gt; in the directory and &amp;lt;code&amp;gt;procedures.xml&amp;lt;/code&amp;gt; file name with your airport code.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
 | url    =  https://forum.flightgear.org/viewtopic.php?p=313409#p313409 &lt;br /&gt;
 | title  =  &amp;lt;nowiki&amp;gt; Re: routeplanner stir sid &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | author =  &amp;lt;nowiki&amp;gt; Parnikkapore &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
 | date   =  Jun 29th, 2017 &lt;br /&gt;
 | added  =  Jun 29th, 2017 &lt;br /&gt;
 | script_version = 0.40 &lt;br /&gt;
 }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bash script for organizing files ===&lt;br /&gt;
ZirconiumX wrote a bash script for putting the XML files into the the right directories in the scenery.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  | url    =  https://forum.flightgear.org/viewtopic.php?p=280561#p280561 &lt;br /&gt;
  | title  =  &amp;lt;nowiki&amp;gt; Re: Non-Navigraph SID/STAR xml files &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | author =  &amp;lt;nowiki&amp;gt; ZirconiumX &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | date   =  Mar 26th, 2016 &lt;br /&gt;
  | added  =  Mar 26th, 2016 &lt;br /&gt;
  | script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&amp;lt;!-- Where do you even get the original files? Navigraph? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for file in $(ls *.xml)&lt;br /&gt;
do&lt;br /&gt;
   # First, rename all the files to the correct convention&lt;br /&gt;
   mv &amp;quot;$file&amp;quot; &amp;quot;${file%.xml}.procedures.xml&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   # Then move it to the correct place&lt;br /&gt;
   icao=${file%.xml}&lt;br /&gt;
   icao1=$(echo $icao | cut -c 1)&lt;br /&gt;
   icao2=$(echo $icao | cut -c 2)&lt;br /&gt;
   icao3=$(echo $icao | cut -c 3)&lt;br /&gt;
   mkdir -p $icao1/$icao2/$icao3&lt;br /&gt;
   mv &amp;quot;${icao}.procedures.xml&amp;quot; &amp;quot;$icao1/$icao2/$icao3/${icao}.procedures.xml&amp;quot;&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigraph procedures for linux users ===&lt;br /&gt;
&lt;br /&gt;
* Create a directory to install procedures, let's say navigraph&lt;br /&gt;
* Download the bash script and make it executable&lt;br /&gt;
* Download the IFMS data from navigraph down load page [https://navigraph.com/downloads navigraph downloads] (paid subscription)&lt;br /&gt;
* Expand the data&lt;br /&gt;
* Create an Airports directory&lt;br /&gt;
* Start the navigraph.sh script with the directories as parameters : ie ./navigraph.sh navdata_native_2511 Airports&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bash script inspired from the one from ZirconiumX&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/bash&lt;br /&gt;
#&lt;br /&gt;
# Organize navigraph procedures&lt;br /&gt;
# &lt;br /&gt;
#&lt;br /&gt;
if (( $# &amp;lt; 2 ))&lt;br /&gt;
then&lt;br /&gt;
    printf &amp;quot;%b&amp;quot; &amp;quot;Error. Not enough arguments.\n&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    printf &amp;quot;%b&amp;quot; &amp;quot;usage: navigraph.sh fromdirectory todirectory\n&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    printf &amp;quot;use directories without trailing slash&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
fromDir=$1&lt;br /&gt;
toDir=$2&lt;br /&gt;
&lt;br /&gt;
for file in $(ls $fromDir/*.xml)&lt;br /&gt;
do&lt;br /&gt;
   filename=${file##*/}&lt;br /&gt;
   name=${filename%.*} # delete everything after last dot &lt;br /&gt;
   upcase=${name^^} # uppercase everything&lt;br /&gt;
&lt;br /&gt;
   target=&amp;quot;$upcase.procedures.xml&amp;quot;&lt;br /&gt;
   # Then move it to the correct place&lt;br /&gt;
    icao=${name^^}&lt;br /&gt;
    icao1=$(echo $icao | cut -c 1)&lt;br /&gt;
    icao2=$(echo $icao | cut -c 2)&lt;br /&gt;
    icao3=$(echo $icao | cut -c 3)&lt;br /&gt;
&lt;br /&gt;
    dirToCreate=$toDir/$icao1/$icao2/$icao3&lt;br /&gt;
    echo &amp;quot;creating dir $dirToCreate&amp;quot;&lt;br /&gt;
    mkdir -p $dirToCreate&lt;br /&gt;
    echo &amp;quot;copy $fromDir/$filename to $toDir/$icao1/$icao2/$icao3/$target&amp;quot;&lt;br /&gt;
    cp $fromDir/$filename $toDir/$icao1/$icao2/$icao3/$target&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Now you can start flightgear using this directory as a scenery source &amp;lt;blockquote&amp;gt;...&lt;br /&gt;
&lt;br /&gt;
-- fg-scenery=/home/user/fg/Navigraph&lt;br /&gt;
&lt;br /&gt;
...&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
In the explanation below, the name like &amp;quot;ProceduresDB&amp;quot; is the tag - so it's actually &amp;lt;code&amp;gt;&amp;amp;lt;ProceduresDB&amp;amp;gt;&amp;lt;/code&amp;gt; in the XML file.&lt;br /&gt;
The subsequent parameters, like &amp;quot;build&amp;quot; are attributes - so it's actually &amp;lt;code&amp;gt;&amp;amp;lt;ProceduresDB build=&amp;quot;&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; in the XML file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ProceduresDB [build] (one of these per file)&lt;br /&gt;
    |&lt;br /&gt;
    Airport [ICAOcode] (one of these per file)&lt;br /&gt;
        |&lt;br /&gt;
        Sid [Name, Runways (comma separated - example: &amp;quot;08L,08R&amp;quot;)]  (many of these per Airport)&lt;br /&gt;
            |&lt;br /&gt;
            Sid_Waypoint [ID (unique sequential integer)] (many of these per Sid)&lt;br /&gt;
                |&lt;br /&gt;
                Name&lt;br /&gt;
                Type&lt;br /&gt;
                Latitude&lt;br /&gt;
                Longitude&lt;br /&gt;
                Speed&lt;br /&gt;
                Altitude&lt;br /&gt;
                AltitudeCons&lt;br /&gt;
                AltitudeRestriction (one of &amp;quot;above&amp;quot; or &amp;quot;below&amp;quot; or &amp;quot;at&amp;quot;)&lt;br /&gt;
                Hdg_Crs (optional. example value = &amp;quot;1&amp;quot;)&lt;br /&gt;
                Hdg_Crs_value (optional. heading degrees)&lt;br /&gt;
                Sp_Turn (optional. example value = &amp;quot;Auto&amp;quot;)&lt;br /&gt;
            ...&lt;br /&gt;
            Sid_Transition [Name] (just one of these per Sid)&lt;br /&gt;
                |&lt;br /&gt;
                SidTr_Waypoint [ID] (many of these per Sid_Transition)&lt;br /&gt;
&lt;br /&gt;
        Star [Name] (many of these per Airport)&lt;br /&gt;
            |&lt;br /&gt;
            Star_Waypoint [ID] (many of these per Star)&lt;br /&gt;
            ...&lt;br /&gt;
            Star_Transition [Name] (just one of these per Star)&lt;br /&gt;
                |&lt;br /&gt;
                StarTr_Waypoint [ID] (many of these per Star_Transition)&lt;br /&gt;
&lt;br /&gt;
        Approach [Name] (e.g., ILS08L - uses IL, if appropriate, and the runway name by convention)&lt;br /&gt;
            |&lt;br /&gt;
            App_Waypoint [ID]&lt;br /&gt;
            App_Transition [Name] (just one of these per Approach)&lt;br /&gt;
                |&lt;br /&gt;
                AppTr_Waypoint [ID] (many of these per App_Transition)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Waypoints ==&lt;br /&gt;
As of FlightGear 2024.1.4, the following types of waypoints are supported&amp;lt;ref&amp;gt;{{flightgear source|path=src/Navaids/LevelDXML.cxx}}&amp;lt;/ref&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Waypoint type !! Description !! Required Waypoint Tags&lt;br /&gt;
|-&lt;br /&gt;
| Normal || Waypoint for proceeding directly to the next one, after a flyby or a flyover, as set by &amp;lt;code&amp;gt;Flytype&amp;lt;/code&amp;gt;. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Runway || Same as a for a normal waypoint, except with added integrity check to confirm that the runway exists in the airport. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Hold || Enter a holding. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hld_Turn&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hld_Time_or_Dist&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hld_td_value&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hld_rad_value&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Vectors || Expect vectors by ATC. || &amp;lt;code&amp;gt;Airport&amp;lt;/code&amp;gt; (supertag)&lt;br /&gt;
|-&lt;br /&gt;
| VorRadialIntc / Intc (both are the same) || Fly &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt; until a VOR radial is intercepted, then fly radial to next waypoint. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;RadialtoIntercept&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| DmeIntc || Fly &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt; until &amp;lt;code&amp;gt;DMEtoIntercept&amp;lt;/code&amp;gt; away from required DME, then fly an arc until the next waypoint. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DMEtoIntercept&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ConstHdgtoAlt || Fly &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt; until Altitude is reached, then proceed to next waypoint. || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Altitude&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| PBD || || &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Latitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Longitude&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Hdg_Crs_value&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DMEtoIntercept&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sample procedures.xml files ==&lt;br /&gt;
Here are some links to example XML files which follows the format specified above:&lt;br /&gt;
* {{github source&lt;br /&gt;
 | proj = jojo2357&lt;br /&gt;
 | repo = flightgear-star-sid-manager&lt;br /&gt;
 | path = 2020.4/Airports/K/A/T/KATL.procedures.xml&lt;br /&gt;
 }}&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  | url    =  https://forum.flightgear.org/viewtopic.php?p=313583#p313583 &lt;br /&gt;
  | title  =  &amp;lt;nowiki&amp;gt; Re: routeplanner stir sid &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | author =  &amp;lt;nowiki&amp;gt; eric &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | date   =  Jul 2nd, 2017 &lt;br /&gt;
  | added  =  Jul 2nd, 2017 &lt;br /&gt;
  | script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{github source&lt;br /&gt;
 | proj = terrasync&lt;br /&gt;
 | repo = main&lt;br /&gt;
 | path = Airports/L/F/R/LFRB.procedures.xml&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Sources for procedure files ==&lt;br /&gt;
=== Installing free community procedures ===&lt;br /&gt;
The FAA provides the [https://www.faa.gov/air_traffic/flight_info/aeronav/digital_products/cifp/ CIFP] which provides procedure data for airports under the purview of the FAA, namely the US.&lt;br /&gt;
&lt;br /&gt;
There is a free project on {{github source&lt;br /&gt;
 | user = jojo2357&lt;br /&gt;
 | repo = flightgear-star-sid-manager&lt;br /&gt;
 | text = GitHub&lt;br /&gt;
 }} that aims to take FAA data and format it into the Level-D required by FlightGear. For instructions on how to install, see {{github source&lt;br /&gt;
 | user = jojo2357&lt;br /&gt;
 | repo = flightgear-star-sid-manager&lt;br /&gt;
 | path = README.MD#installation&lt;br /&gt;
 | text = this section&lt;br /&gt;
 }} of the README.&lt;br /&gt;
&lt;br /&gt;
This project uses GitHub Actions in order to automatically update the procedures at the start of a new AIRAC cycle.&lt;br /&gt;
&lt;br /&gt;
=== Commercial sources ===&lt;br /&gt;
A process to import SID/STARs from Navigraph Level D (obtaining this data requires paid subscription to Navigraph) was described on the forum.&lt;br /&gt;
&lt;br /&gt;
Quoting here for reference:{{cite web&lt;br /&gt;
  | url=https://forum.flightgear.org/viewtopic.php?f=11&amp;amp;t=37975&amp;amp;start=15#p416078&lt;br /&gt;
  | title  =  &amp;lt;nowiki&amp;gt; Re: How to get SIDs and STARs in Airbus A320 mcdu &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | author =  &amp;lt;nowiki&amp;gt; mpotra &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  | quote = :&lt;br /&gt;
# Download Level-D navdata from Navigraph (On the Downloads -&amp;gt; Manual Download page)&lt;br /&gt;
# This is ZIP file with a Windows Installer executable inside. If you're on Windows already, run the installer, select a version (Prepar3D/PD3) and then skip to step 4.&lt;br /&gt;
# The executable is a Inno Setup executable. In Linux you can extract the contents with `innoextractor -e leveld_2311.exe` for example, which will output a &amp;quot;code$AppName&amp;quot; directory.&lt;br /&gt;
# In the extracted directory you'll find a &amp;quot;navdata&amp;quot; folder containing XML files with procedures for all airports for the AIRAC cycle with filenames as &amp;quot;&amp;lt;ICAO&amp;gt;.xml&amp;quot;.&lt;br /&gt;
# You can either rename in bulk or single file from &amp;quot;&amp;lt;ICAO&amp;gt;.xml&amp;quot; to &amp;quot;&amp;lt;ICAO&amp;gt;.procedures.xml&amp;quot; for desired airports.&lt;br /&gt;
# Create a new directory somewhere (anywhere you want) say &amp;quot;AIRAC/2311&amp;quot;, and inside it create the &amp;quot;Airports&amp;quot; directory. (camel-case formatted - &amp;quot;airports&amp;quot; with small letter A didn't work for me)&lt;br /&gt;
# Inside this &amp;quot;Airports&amp;quot; directory, create new subdirectories for each airport you want to bring in, using the [I]/[C]/[A] format. For example, if you want to add Vienna airport LOWW, create the subdirectories  &amp;quot;Airports/L/O/W&amp;quot; - do not create the fourth subdirectory [W] (it won't work)&lt;br /&gt;
# Copy the renamed file from step 5 into this last subdirectory. For example, copy &amp;quot;LOWW.procedures.xml&amp;quot; into &amp;quot;AIRAC/2311/Airports/L/O/W&amp;quot; directory, resulting in &amp;quot;AIRAC/2311/Airports/L/O/W/LOWW.procedures.xml&amp;quot;.  Example 2: for Budapest airport copy &amp;quot;LHBP.procedures.xml&amp;quot; into &amp;quot;AIRAC/2311/Airports/L/H/B&amp;quot;, resulting in &amp;quot;AIRAC/2311/Airports/L/H/B/LHBP.procedures.xml&amp;quot;&lt;br /&gt;
# In FlightGear, add the &amp;quot;AIRAC/2311&amp;quot; directory to your sceneries paths.&lt;br /&gt;
# Reload FlightGear and enjoy SID/STARs in your MCDU&lt;br /&gt;
  }}&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Route manager]]&lt;br /&gt;
* [[Flightplan XML formats]]&lt;br /&gt;
&lt;br /&gt;
=== Mailing list threads ===&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/mailman/flightgear-devel/thread/222DD5B1-B7F1-421B-84AE-A60353203085@flightgear.org/ &amp;lt;nowiki&amp;gt;[Flightgear-devel&amp;lt;nowiki&amp;gt;]&amp;lt;/nowiki&amp;gt; Procedures SID/STAR/Approach]&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{flightgear source|path=src/Navaids/LevelDXML.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Navaids/LevelDXML.cxx}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter&amp;diff=143675</id>
		<title>FlightGear Newsletter</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter&amp;diff=143675"/>
		<updated>2026-03-08T15:22:05Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Archive */ Updating table to show newsletters for January and February 2026.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:magagazine.png|right]]&lt;br /&gt;
&lt;br /&gt;
The '''FlightGear Newsletter''' is intended to provide a collection of the latest developments from the worldwide [[FlightGear]] community. With so much development ongoing, it's almost impossible for anyone to keep up. The newsletter was started in July 2009, and is presented on a monthly basis.  It is a collaborative effort that is created, edited and distributed via the [[FlightGear Wiki]], and all FlightGear users, contributors and developers are invited to contribute.&lt;br /&gt;
&lt;br /&gt;
=== Archive ===&lt;br /&gt;
&amp;lt;div class=&amp;quot;noresize&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;prettytable&amp;quot;&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2026&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2025&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2024&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2023&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2022&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2021&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2020&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2019&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2018&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2017&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2016&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2015&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2014&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2013&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2012&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2011&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2010&lt;br /&gt;
! align=&amp;quot;left&amp;quot; | 2009&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2026|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2026|February]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2025|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2025|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2025|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2025|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2025|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2025|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2025|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2025|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2025|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2025|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2025|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2025|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2024|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2024|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2024|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2024|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2024|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2024|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2024|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2024|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2024|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2024|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2024|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2024|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2023|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2023|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2023|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2023|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2023|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2023|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2023|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2023|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2023|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2023|October]] &lt;br /&gt;
* [[FlightGear Newsletter November 2023|November]] &lt;br /&gt;
* [[FlightGear Newsletter December 2023|December]] &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2022|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2022|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2022|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2022|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2022|May]] &lt;br /&gt;
* June&lt;br /&gt;
* [[FlightGear Newsletter July 2022|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2022|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2022|September]]&lt;br /&gt;
* [[FlightGear Newsletter November 2022|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2022|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2021|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2021|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2021|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2021|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2021|May]] &lt;br /&gt;
* [[FlightGear Newsletter June 2021|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2021|July]] &lt;br /&gt;
* [[FlightGear Newsletter August 2021|August]] &lt;br /&gt;
* [[FlightGear Newsletter September 2021|September]] &lt;br /&gt;
* [[FlightGear Newsletter October 2021|October]] &lt;br /&gt;
* [[FlightGear Newsletter November 2021|November]] &lt;br /&gt;
* [[FlightGear Newsletter December 2021|December]] &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2020|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2020|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2020|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2020|April]] &lt;br /&gt;
* [[FlightGear Newsletter May 2020|May]] &lt;br /&gt;
* [[FlightGear Newsletter June 2020|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2020|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2020|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2020|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2020|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2020|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2020|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2019|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2019|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2019|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2019|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2019|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2019|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2019|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2019|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2019|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2019|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2019|November]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2018|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2018|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2018|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2018|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2018|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2018|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2018|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2018|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2018|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2018|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2018|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2018|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2017|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2017|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2017|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2017|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2017|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2017|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2017|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2017|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2017|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2017|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2017|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2017|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2016|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2016|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2016|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2016|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2016|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2016|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2016|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2016|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2016|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2016|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2016|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2016|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2015|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2015|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2015|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2015|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2015|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2015|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2015|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2015|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2015|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2015|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2015|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2015|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2014|January]] &lt;br /&gt;
* [[FlightGear Newsletter February 2014|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2014|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2014|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2014|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2014|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2014|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2014|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2014|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2014|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2014|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2014|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2013|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2013|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2013|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2013|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2013|May]] &lt;br /&gt;
* [[FlightGear Newsletter June 2013|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2013|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2013|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2013|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2013|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2013|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2013|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2012|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2012|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2012|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2012|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2012|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2012|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2012|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2012|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2012|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2012|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2012|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2012|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2011|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2011|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2011|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2011|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2011|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2011|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2011|July]] &lt;br /&gt;
* [[FlightGear Newsletter August 2011|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2011|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2011|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2011|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2011|December]] &lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter January 2010|January]]&lt;br /&gt;
* [[FlightGear Newsletter February 2010|February]]&lt;br /&gt;
* [[FlightGear Newsletter March 2010|March]]&lt;br /&gt;
* [[FlightGear Newsletter April 2010|April]]&lt;br /&gt;
* [[FlightGear Newsletter May 2010|May]]&lt;br /&gt;
* [[FlightGear Newsletter June 2010|June]]&lt;br /&gt;
* [[FlightGear Newsletter July 2010|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2010|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2010|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2010|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2010|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2010|December]]&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&lt;br /&gt;
* [[FlightGear Newsletter July 2009|July]]&lt;br /&gt;
* [[FlightGear Newsletter August 2009|August]]&lt;br /&gt;
* [[FlightGear Newsletter September 2009|September]]&lt;br /&gt;
* [[FlightGear Newsletter October 2009|October]]&lt;br /&gt;
* [[FlightGear Newsletter November 2009|November]]&lt;br /&gt;
* [[FlightGear Newsletter December 2009|December]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some newsletter editions have been translated in other languages:&lt;br /&gt;
* [[File:De.gif|16px|link=:De:FlightGear Newsletter]] [[:De:FlightGear Newsletter|Deutsch]]&lt;br /&gt;
* [[File:Es.gif|16px|link=:Es:FlightGear Newsletter]] [[:Es:FlightGear Newsletter|Español]]&lt;br /&gt;
* [[File:Fr.gif|16px|link=:Fr:FlightGear Newsletter]] [[:Fr:FlightGear Newsletter|Français]]&lt;br /&gt;
&lt;br /&gt;
=== You are invited to contribute! ===&lt;br /&gt;
We would like to emphasize that the monthly newsletter can not live without the contributions of FlightGear users and developers. The FlightGear Newsletter is a community-driven newsletter, which means that it is created and edited by people like ''you''. You don't need to be a long-time FlightGear user (or even a developer) to contribute to the newsletter. &lt;br /&gt;
&lt;br /&gt;
In fact, helping write the monthly FlightGear newsletter is an excellent way for getting started contributing to the FlightGear community very quickly and very easily. &lt;br /&gt;
&lt;br /&gt;
Even if you don't have to add anything yourself, just reviewing and improving additions by others is also highly appreciated, as are efforts to help translate newsletters or add screen shots (e.g. taken from the forum or mailing lists) to the newsletter. Screen shots can be uploaded at [[Special:Upload]].&lt;br /&gt;
&lt;br /&gt;
Everyone with a wiki account (free to [[Special:UserLogin|register]]) can edit the newsletter and every contribution is welcome. So if you know about any FlightGear related projects such as for example updated scenery or aircraft, please do feel invited to add such news to the newsletter.&lt;br /&gt;
&lt;br /&gt;
A simple template providing a basic structure for each upcoming newsletter can be taken from [[Talk:Next newsletter]]. This template can be copied/pasted into each new newsletter to help users getting started providing contents.&lt;br /&gt;
&lt;br /&gt;
The draft for the upcoming newsletter can always be found at [[Next newsletter]].&lt;br /&gt;
&lt;br /&gt;
If English is not your native language, please don't be concerned about contributing to the newsletter, you can always easily ask fellow FlightGear users to review or proof-read your changes. Also, one of the easiest ways to get started is simply copying/pasting text from forum or mailing list discussions, such as announcements (e.g., new aircraft, new scenery, etc.).&lt;br /&gt;
&lt;br /&gt;
Another neat option to get started is adding links to FlightGear-related YouTube videos. For this, we have a dedicated section in each newsletter: &amp;quot;[[FlightGear Newsletter {{#time: F Y | last month }}#FlightGear on YouTube|FlightGear on YouTube]].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to embed a video, please see {{mediawiki|Extension:EmbedVideo#Usage}}&lt;br /&gt;
&lt;br /&gt;
If you are looking for other ways to get involved, please see [[Volunteer]].&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear Newsletter| ]]&lt;br /&gt;
[[Category:Community|Newsletter]]&lt;br /&gt;
[[Category:Lists|Newsletter]]&lt;br /&gt;
&lt;br /&gt;
[[de:FlightGear Newsletter]]&lt;br /&gt;
[[es:FlightGear Newsletter]]&lt;br /&gt;
[[fr:FlightGear Newsletter]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Category:World_Scenery_3.0&amp;diff=143582</id>
		<title>Category:World Scenery 3.0</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Category:World_Scenery_3.0&amp;diff=143582"/>
		<updated>2026-01-25T07:47:39Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +cat Scenery enhancement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Articles relating to WS 3.0&lt;br /&gt;
&lt;br /&gt;
[[Category:Scenery enhancement]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Timed_Loops&amp;diff=142661</id>
		<title>Timed Loops</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Timed_Loops&amp;diff=142661"/>
		<updated>2025-10-12T12:51:03Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Spelling fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Software|title=Timed Loops Add-on|developedby=FGUK|initialrelease=2017|writtenin=Nasal|type=Add-on|license=[[GNU General Public License]] v2}}&lt;br /&gt;
&lt;br /&gt;
'''Timed Loops''', also known as '''Mach Loop Challenge''' by FGUK is an add-on with timed courses challenges trough the valleys of the Mach Loop in Wales, Great Britain.&lt;br /&gt;
&lt;br /&gt;
== The Mach Loop ==&lt;br /&gt;
The ''Mach Loop''&amp;lt;ref&amp;gt;{{wikipedia |Mach Loop}}&amp;lt;/ref&amp;gt; (also known as the ''Machynlleth Loop'') is a part of the United Kingdom Low Flying System and lies within Low Flying Area 7 (LFA7), which covers most of Wales.&lt;br /&gt;
&lt;br /&gt;
The system of valleys lies 13 km (8 mi) east of Barmouth between the towns of Dolgellau to the north and Machynlleth to the south&lt;br /&gt;
&lt;br /&gt;
== The Mach Loop Challenge ==&lt;br /&gt;
This challenge is based on that in the BAE Typhoon training simulator. There are a series of Gates the pilot must pass through to complete the challenge. As with most peacetime military flying operations flight operations are limited to subsonic.&lt;br /&gt;
&lt;br /&gt;
Your course time will be printed on screen as you exit the course providing that no gates are missed and that the speed limit was not exceeded.&lt;br /&gt;
&lt;br /&gt;
There are 5 versions of the challenge available in this add-on.&lt;br /&gt;
&lt;br /&gt;
# '''Mach loop – Unlimited''' – As above, but with no max speed limit.&lt;br /&gt;
# '''Heli Race''' – Helicopter version of the loop&lt;br /&gt;
# '''Moli Loop''' – An extended course in the same area. Speed restrictions of &amp;lt; M1.0 applies.&lt;br /&gt;
# '''Long loop''' – Mach loop extending into Moli loop.&lt;br /&gt;
# '''Unlimited mach loop''' - Version of the loop with no speed restrictions.&lt;br /&gt;
&lt;br /&gt;
The scenarios should now be available from your AI/Scenarios menu in sim.&lt;br /&gt;
&lt;br /&gt;
The scenarios are closest to Llanbedr Airfield (EGOD), where it is useful to start on runway 05. If you prefer you could start at RAF Valley (EGOV) and fly south to EGOD to pick up the loop.&lt;br /&gt;
&lt;br /&gt;
== Videos ==&lt;br /&gt;
An example of the challenge can be seen in these videos:&lt;br /&gt;
&lt;br /&gt;
* F-14 in 144.0s : https://www.youtube.com/embed/jIdnQfwU1W4&lt;br /&gt;
* Vulcan B2 https://www.youtube.com/embed/XK0Y1sfijLY&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link |t=23670 |title=FlightNight 9/8/14 Mach loop challenge}}&lt;br /&gt;
* {{forum link |t=39519 |title=Mach Loop Challenge markers showing as aircraft not hexagons}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{fgaddon source |path=Addons/TimedLoops}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Timed_Loops&amp;diff=142660</id>
		<title>Timed Loops</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Timed_Loops&amp;diff=142660"/>
		<updated>2025-10-12T12:48:46Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Minor cleanup; +headings; +related: Some forum topics, source; Note: A screenshot or two would be nice&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Software|title=Timed Loops Add-on|developedby=FGUK|initialrelease=2017|writtenin=Nasal|type=Add-on|license=[[GNU General Public License]] v2}}&lt;br /&gt;
&lt;br /&gt;
'''Timed Loops''', also known as '''Mach Loop Challenge''' by FGUK is an aad-on with a timed course challenge trough the valleys of the Mach Loop in Wales, Great Britain.&lt;br /&gt;
&lt;br /&gt;
== The Mach Loop ==&lt;br /&gt;
The ''Mach Loop''&amp;lt;ref&amp;gt;{{wikipedia |Mach Loop}}&amp;lt;/ref&amp;gt; (also known as the ''Machynlleth Loop'') is a part of the United Kingdom Low Flying System and lies within Low Flying Area 7 (LFA7), which covers most of Wales.&lt;br /&gt;
&lt;br /&gt;
The system of valleys lies 13 km (8 mi) east of Barmouth between the towns of Dolgellau to the north and Machynlleth to the south&lt;br /&gt;
&lt;br /&gt;
== The Mach Loop Challenge ==&lt;br /&gt;
This challenge is based on that in the BAE Typhoon training simulator. There are a series of Gates the pilot must pass through to complete the challenge. As with most peacetime military flying operations flight operations are limited to subsonic.&lt;br /&gt;
&lt;br /&gt;
Your course time will be printed on screen as you exit the course providing that no gates are missed and that the speed limit was not exceeded.&lt;br /&gt;
&lt;br /&gt;
There are 5 versions of the challenge available in this add-on.&lt;br /&gt;
&lt;br /&gt;
# '''Mach loop – Unlimited''' – As above, but with no max speed limit.&lt;br /&gt;
# '''Heli Race''' – Helicopter version of the loop&lt;br /&gt;
# '''Moli Loop''' – An extended course in the same area. Speed restrictions of &amp;lt; M1.0 applies.&lt;br /&gt;
# '''Long loop''' – Mach loop extending into Moli loop.&lt;br /&gt;
# '''Unlimited mach loop''' - Version of the loop with no speed restrictions.&lt;br /&gt;
&lt;br /&gt;
The scenarios should now be available from your AI/Scenarios menu in sim.&lt;br /&gt;
&lt;br /&gt;
The scenarios are closest to Llanbedr Airfield (EGOD), where it is useful to start on runway 05. If you prefer you could start at RAF Valley (EGOV) and fly south to EGOD to pick up the loop.&lt;br /&gt;
&lt;br /&gt;
== Videos ==&lt;br /&gt;
An example of the challenge can be seen in these videos:&lt;br /&gt;
&lt;br /&gt;
* F-14 in 144.0s : https://www.youtube.com/embed/jIdnQfwU1W4&lt;br /&gt;
* Vulcan B2 https://www.youtube.com/embed/XK0Y1sfijLY&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link |t=23670 |title=FlightNight 9/8/14 Mach loop challenge}}&lt;br /&gt;
* {{forum link |t=39519 |title=Mach Loop Challenge markers showing as aircraft not hexagons}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{fgaddon source |path=Addons/TimedLoops}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Aircraft_failure_system&amp;diff=142622</id>
		<title>Aircraft failure system</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Aircraft_failure_system&amp;diff=142622"/>
		<updated>2025-10-04T23:58:10Z</updated>

		<summary type="html">&lt;p&gt;Johan G: + Mention that this article relates to FlightGear version 3.0 or older; +related: Link to forum topic&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;: ''For FlightGear version 3.1 or newer see [[A Failure Management Framework for FlightGear]].''&lt;br /&gt;
&lt;br /&gt;
Want an engine or instrument to fail mid-flight? Flightgear has such a system, but not every aircraft supports it fully.&lt;br /&gt;
The failure system is disabled by default, the system can be accessed in Flightgear in the Equipment menu.&lt;br /&gt;
&lt;br /&gt;
== Flying with failures ==&lt;br /&gt;
=== Using the Failure System ===&lt;br /&gt;
Should you for example wish to fail an instrument, you can open the Instrument failure panel as seen below.&lt;br /&gt;
&lt;br /&gt;
[[File:Instrument Failures.png|The instrument failure panel]]&lt;br /&gt;
&lt;br /&gt;
A checked field means that the instrument has not failed. To fail it, simply uncheck it, and click apply. If the aircraft suports it, it will fail immidiatly.&lt;br /&gt;
&lt;br /&gt;
For each instrument, there is also a MTBF ([http://en.wikipedia.org/wiki/Mean_time_between_failures Mean time between failures]), where you can specify the how often an instrument should fail. MTBF is in seconds.&lt;br /&gt;
&lt;br /&gt;
To make the instrument work again, simply open the panel, check it, and click apply.&lt;br /&gt;
&lt;br /&gt;
Systems works in the same way as instruments, but some components have a MCBF (Mean cyclus between failures) field, instead of a MTBF. A cycle is each time the system is changed into the other direction. E.g. for flaps each time it is moved in or out, opposite of last movement counts for a cycle:&lt;br /&gt;
&lt;br /&gt;
[[File:System Failures.png|The system failure panel]]&lt;br /&gt;
&lt;br /&gt;
=== Using the Random Failure Manager ===&lt;br /&gt;
The random failure manager is a convenient way to enter the same MTBF/MCBF for all systems/instruments.&lt;br /&gt;
&lt;br /&gt;
[[File:Random Failures.png|The random failure panel]]&lt;br /&gt;
&lt;br /&gt;
If you want a red message on the screen to tell when it fails, the click the checkbox for that.&lt;br /&gt;
&lt;br /&gt;
== Adapting an aircraft to fail ==&lt;br /&gt;
Some things will work pretty much out of the box. For example engine and control surfaces. Some control surfaces like elevons, spoilers and instruments like CDU you have to either find a matchable serviceable property and make the object depend on it or make your own.&lt;br /&gt;
&lt;br /&gt;
Serviceable properties are found various places.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/instrumentation/airspeed-indicator/serviceable&lt;br /&gt;
&lt;br /&gt;
/gear/serviceable&lt;br /&gt;
&lt;br /&gt;
/systems/vacuum/serviceable&lt;br /&gt;
&lt;br /&gt;
/controls/flight/elevator/serviceable&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are either true or false.&lt;br /&gt;
&lt;br /&gt;
Some things depend on more serviceables, for example airspeed indicator can fail on its own, or the electrical system can fail on which it depend.&lt;br /&gt;
&lt;br /&gt;
For some systems/instruments, when it becomes unserviceable, some properties will also become read-only. For example the rudder will get 'stuck'. Some elements of Flightgear give some Alerts in the console, when the try to change a property and cannot. For example JSBSim will output an error when trying to change the rudder position after it failed, when you try to yaw the plane. Most of these messages can be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Making a custom failure ===&lt;br /&gt;
At the moment the panels for failure cannot be extended with custom elements, but you can write in NASAL your own code on when/how to fail a system.&lt;br /&gt;
&lt;br /&gt;
Make a serviceable property where you have other properties for that system. And then write some script/xml to make the system depend on that property.&lt;br /&gt;
&lt;br /&gt;
Examples of what you could make a custom failure for:&lt;br /&gt;
&lt;br /&gt;
Brakes, CDU, instrumentation light, HUD, Canopy, doors, cabin pressure system, autopilot, radar etc.&lt;br /&gt;
&lt;br /&gt;
=== Extending the random failure manager ===&lt;br /&gt;
With Nasal the random failure manager can be extended with additional failures, but until Flightgear 3.4 they will not show up in the GUI panels.&lt;br /&gt;
&lt;br /&gt;
Notice the failure system has been changed for Flightgear 3.2, even though the GUI has not changed yet (scheduled for Flightgear 3.4). The [[A_Failure_Management_Framework_for_FlightGear|new system]] will allow components to fail at various levels instead of only 0% or 100% failed. It is also more flexible and easier to extend and modify.&lt;br /&gt;
&lt;br /&gt;
==== Flightgear 3.0 example (Do not use for 3.2) ====&lt;br /&gt;
&lt;br /&gt;
Example of adding Canvas HUD, Canopy and Instrumentation lights to the list of systems that might fail:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;# random failure code:&lt;br /&gt;
&lt;br /&gt;
  var fail = { SERVICEABLE : 1, JAM : 2, ENGINE: 3};&lt;br /&gt;
  var type = { MTBF : 1, MCBF: 2 };&lt;br /&gt;
  var failure_root = &amp;quot;/sim/failure-manager&amp;quot;;&lt;br /&gt;
  #HUD&lt;br /&gt;
  var prop = &amp;quot;/instrumentation/head-up-display&amp;quot;;&lt;br /&gt;
  failures.breakHash[prop] = {&lt;br /&gt;
    type: type.MTBF, failure: fail.SERVICEABLE, desc: &amp;quot;Head up display&amp;quot;};&lt;br /&gt;
  var o = failures.breakHash[prop];&lt;br /&gt;
  var t = &amp;quot;/mtbf&amp;quot;;&lt;br /&gt;
  props.globals.initNode(failure_root ~ prop ~ t, 0);&lt;br /&gt;
  props.globals.initNode(prop ~ &amp;quot;/serviceable&amp;quot;, 1, &amp;quot;BOOL&amp;quot;);&lt;br /&gt;
  # Instrument light:&lt;br /&gt;
  prop = &amp;quot;/instrumentation/instrumentation-light&amp;quot;;&lt;br /&gt;
  failures.breakHash[prop] = {&lt;br /&gt;
    type: type.MTBF, failure: fail.SERVICEABLE, desc: &amp;quot;Instrumentation light&amp;quot;};&lt;br /&gt;
  props.globals.initNode(failure_root ~ prop ~ t, 0);&lt;br /&gt;
  props.globals.initNode(prop ~ &amp;quot;/serviceable&amp;quot;, 1, &amp;quot;BOOL&amp;quot;);&lt;br /&gt;
  # Canopy:&lt;br /&gt;
  prop = &amp;quot;/fdm/jsbsim/fcs/canopy&amp;quot;;&lt;br /&gt;
  failures.breakHash[prop] = {&lt;br /&gt;
    type: type.MTBF, failure: fail.SERVICEABLE, desc: &amp;quot;Canopy&amp;quot;};&lt;br /&gt;
  props.globals.initNode(failure_root ~ prop ~ t, 0);&lt;br /&gt;
  props.globals.initNode(prop ~ &amp;quot;/serviceable&amp;quot;, 1, &amp;quot;BOOL&amp;quot;);&lt;br /&gt;
  # Set all MTBF to 24 hours:&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/display-on-screen&amp;quot;, 1);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mcbf-0&amp;quot;, 0);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mcbf-500&amp;quot;, 1);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mcbf&amp;quot;, 500);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mtbf-0&amp;quot;, 0);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mtbf-86400&amp;quot;, 1);&lt;br /&gt;
  setprop(&amp;quot;/sim/failure-manager/global-mtbf&amp;quot;, 86400);&lt;br /&gt;
  failures.setAllMCBF(500);&lt;br /&gt;
  failures.setAllMTBF(86400);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example for canopy it will make a property called:&lt;br /&gt;
&lt;br /&gt;
/fdm/jsbsim/fcs/canopy/serviceable&lt;br /&gt;
&lt;br /&gt;
In the above code its MTBF is set to 24 hours, but this can be changed in the Random Failure panel.&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link |t=21855 |title=How does serviceable and failures work?}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
For FlightGear v 3.0 or older (according to {{forum link |p=227190 |title=this}}).&lt;br /&gt;
&lt;br /&gt;
* $FGDATA/Nasal/failures.nas for more info.&lt;br /&gt;
&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Input_device&amp;diff=141546</id>
		<title>Input device</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Input_device&amp;diff=141546"/>
		<updated>2025-03-17T12:22:24Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Forum topics */ + [HOWTO] Use more than 32 buttons on Windows via HID&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Could you imagine a pilot in his or her [[:Category:Cessna|Cessna]] controlling the machine with a keyboard alone? For getting the proper feeling of flight you will need a '''joystick/yoke''' plus [[rudder]] pedals, right? &lt;br /&gt;
&lt;br /&gt;
FlightGear has integrated joystick support, which automatically detects any joystick, yoke, or pedals attached. Just try it! If this does work for you, lean back and be happy! You can see what FlightGear has detected your joystick as in the Help &amp;gt; Joystick Configuration dialog from the [[menu]]. &lt;br /&gt;
&lt;br /&gt;
Unfortunately, for the above mentioned versatility, chances are your joystick does not work out of the box. This article explains you how to make FlightGear recognise your device&lt;br /&gt;
&lt;br /&gt;
== Joystick or yoke? ==&lt;br /&gt;
{| cellpadding=&amp;quot;4&amp;quot; cellspacing=&amp;quot;0&amp;quot; align=&amp;quot;right&amp;quot; style=&amp;quot;clear:right; background:#fafafa; font-size: 85%; border: 1px solid #CCCCCC;&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| [[File:CH Products Fighterstick USB.jpg|x199px|CH Products Fighterstick USB]]&lt;br /&gt;
| [[File:Saitek Pro Flight Cessna Yoke front.jpg|300px|Saitek Pro Flight Cessna Yoke]]&lt;br /&gt;
|-&lt;br /&gt;
| CH Products Fighterstick USB&lt;br /&gt;
| Saitek Pro Flight Cessna Yoke&lt;br /&gt;
|}&lt;br /&gt;
The two most common control devices on aircraft are the joystick (left picture) and yoke (right picture). Joysticks can be found on military fighters, helicopters and Airbus [[airliner]]s, while yokes are used on almost all other fixed wing aircraft, including Boeing airliners.&lt;br /&gt;
&lt;br /&gt;
Joysticks are generally a lot cheaper, starting at $10. Yokes start at $100. When you are new to flightsimming, buying a cheap (ca. $20) joystick might be a good way to find out if it's something for you.&lt;br /&gt;
&lt;br /&gt;
The following table should help you decide which one is best suited for you:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! Joystick&lt;br /&gt;
! Yoke&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Price&lt;br /&gt;
| $10+&lt;br /&gt;
| $100+&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | General aviation&lt;br /&gt;
| {{no}}&lt;br /&gt;
| {{yes}}&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Gliders&lt;br /&gt;
| {{yes}}&lt;br /&gt;
| {{no}}&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Helicopter&lt;br /&gt;
| {{yes}}&lt;br /&gt;
| {{no}}&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Fighters&lt;br /&gt;
| {{yes}}&lt;br /&gt;
| {{no}}&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Most airliners including Boeing&lt;br /&gt;
| {{no}}&lt;br /&gt;
| {{yes}}&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot; | Airbus&lt;br /&gt;
| {{yes}}&lt;br /&gt;
| {{no}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It should be noted that any type of input device (be it a joystick, a yoke or even a gamepad) will work with '''all''' aircraft in FlightGear and that the table above only suggests which ones are more suited to specific types of aircraft given how they are flown in real life.&lt;br /&gt;
&lt;br /&gt;
Some reviews of flight simulation hardware can be found in [[:Category:Hardware reviews]].&lt;br /&gt;
&lt;br /&gt;
== Built-in joystick support ==&lt;br /&gt;
In order for joystick auto-detection to work, a joystick bindings xml file must exist for each joystick. This file describes what axes and buttons are to be used to control which functions in FlightGear. The associations between functions and axes or buttons are called &amp;quot;bindings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
FlightGear includes a large number of such bindings files for a variety of manufacturers. Chances are high that your joystick will be recognised straight away, so let's try that first. You can confirm whether it was recognised by looking in the &amp;lt;tt&amp;gt;File &amp;gt; Joystick Configuration&amp;lt;/tt&amp;gt; dialog. &amp;quot;Used for&amp;quot; should contain a name/description of your joystick. It will contain &amp;quot;default&amp;quot; when FlightGear did not recognise your joystick.&lt;br /&gt;
&lt;br /&gt;
Most of the time when your joystick is not recognised, it is because of a missing name definition in the respective bindings file. Because FlightGear is used on all kind of operating systems, names vary a lot. You can find the files under &amp;lt;tt&amp;gt;[[$FG_ROOT]]/Input/Joysticks/&amp;lt;/tt&amp;gt; (despite the name, yokes and pedals are also found here!). For example, if you have a CH Products joystick, look in the folder &amp;lt;tt&amp;gt;[[$FG_ROOT]]/Input/Joysticks/CH&amp;lt;/tt&amp;gt; for a file that might work for your joystick. When such a file exists, do the following:&lt;br /&gt;
# Launch FlightGear with the joystick connected.&lt;br /&gt;
# Look under File &amp;gt; Joystick Configuration and check the name behind &amp;quot;Joystick #0:&amp;quot;. &lt;br /&gt;
# Open your joystick's bindings file in a XML editor and add the following code to the file, below the already-present &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; tags.&lt;br /&gt;
# &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;name&amp;gt;The EXACT name you found under step 2; including spaces, capitals etc.&amp;lt;/name&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Please report this name [http://forum.flightgear.org/viewforum.php?f=24 at our forum], so it can be added to the official file (and next releases).&lt;br /&gt;
&lt;br /&gt;
Please note that the latest config files are always to be found at {{fgdata file|Input/Joysticks}}. This link may contain additional binding files that were not included in the latest stable release.&lt;br /&gt;
&lt;br /&gt;
If there is no file for your joystick, you will have to create such a file. We will discuss that in [[Joystick#Writing or editing joystick binding xml files|a later section]], by cutting and pasting bindings from the examples that are included with FlightGear.&lt;br /&gt;
&lt;br /&gt;
=== Multiple devices on Windows ===&lt;br /&gt;
In case you have two USB devices (for instance a yoke plus pedals) to a Windows computer, there may be cases, where the same driver name is reported twice. In this case, you can get at least the yoke to work by assigning it number 0 (out of 0 and 1), by adding a line to &amp;lt;tt&amp;gt;[[$FG_ROOT]]/joystick.xml&amp;lt;/tt&amp;gt; like:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;js n=&amp;quot;0&amp;quot; include=&amp;quot;Input/Joysticks/Saitek/ST290-Pro.xml&amp;quot;/&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
if you also have pedals (or another joystick), just add more lines, similar to:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;js n=&amp;quot;1&amp;quot; include=&amp;quot;Input/Joysticks/Saitek/Pro-Flight-Rudder-Pedals.xml&amp;quot;/&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To make sure that the first input device is indeed the yoke, rotate the yoke ([[aileron]] control) and observe the Help &amp;gt; Joystick Configuration dialog. If the aileron value does not change, you have to make the yoke the preferred device first. For doing so, enter the Windows &amp;quot;Control panel&amp;quot;, open &amp;quot;Game controllers&amp;quot; and select the &amp;quot;Advanced&amp;quot; button. Here you can select the yoke as the &amp;quot;Preferred&amp;quot; device. Afterwards you can check that assignment by restarting FlightGear. The yoke should now control the aileron.&lt;br /&gt;
&lt;br /&gt;
== Adding support for your joystick ==&lt;br /&gt;
=== Verifying your joystick is working ===&lt;br /&gt;
==== Linux ====&lt;br /&gt;
Reboot your system and immediately enter on the [[command line]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
dmesg | grep Joystick &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which pipes the boot message to grep which then prints every line in the boot message that contains the string “Joystick”. When you do this with a Saitek joystick attached, you will see a line similar to this one: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
input0: USB HID v1.00 Joystick [SAITEK CYBORG 3D USB] on usb2:3.0 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This line tells us that a joystick has identified itself as SAITEK CYBORG 3D USB to the operating system. It does not tell us that the joystick driver sees your joystick.  You can verify that the joystick driver sees the joystick by entering:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
js_demo &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If your joystick is not recognized by the driver, you may have to manually load the joystick device module with the command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
modprobe joydev&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Windows ====&lt;br /&gt;
Go to &amp;lt;tt&amp;gt;Start &amp;gt; Control Panel &amp;gt; Game Controller&amp;lt;/tt&amp;gt; and see whether the dialog displays (and responses) on your joystick. &lt;br /&gt;
&lt;br /&gt;
=== Confirming that the driver recognizes your joystick ===&lt;br /&gt;
FlightGear ships with a utility called js_demo. It will report the number of joysticks attached to a system, their respective &amp;quot;names&amp;quot; and their capabilities. Under Linux, you can run js_demo from the folder /FlightGear/bin as follows: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /usr/local/FlightGear/bin &lt;br /&gt;
$ js_demo&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Under Windows, open a command shell (&amp;lt;tt&amp;gt;Start &amp;gt; All Programs &amp;gt; Accessories &amp;gt; Command Prompt&amp;lt;/tt&amp;gt;), go to the FlightGear binary folder and start the program as follows (given FlightGear is installed under &amp;lt;tt&amp;gt;C:/Program Files/Flightgear&amp;lt;/tt&amp;gt;) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
C:&lt;br /&gt;
cd /Program Files/FlightGear/bin/win32 &lt;br /&gt;
js_demo.exe &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--If js_demo.exe is not included in your version, download it [http://fgfs.beggabaur.de/forum/js_demo.exe here].--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On our system, the first few lines of output are (stop the program with C if it is quickly scrolling past your window!) as follows: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Joystick test program. &lt;br /&gt;
Joystick 0: “CH PRODUCTS CH FLIGHT SIM YOKE USB ” &lt;br /&gt;
Joystick 1: “CH PRODUCTS CH PRO PEDALS USB” &lt;br /&gt;
Joystick 2 not detected &lt;br /&gt;
Joystick 3 not detected &lt;br /&gt;
Joystick 4 not detected &lt;br /&gt;
Joystick 5 not detected &lt;br /&gt;
Joystick 6 not detected &lt;br /&gt;
Joystick 7 not detected &lt;br /&gt;
+——————–JS.0———————-+——————–JS.1———————-+ &lt;br /&gt;
| Btns Ax:0 Ax:1 Ax:2 Ax:3 Ax:4 Ax:5 Ax:6 | Btns Ax:0 Ax:1 Ax:2 | &lt;br /&gt;
+———————————————-+———————————————-+ &lt;br /&gt;
| 0000 +0.0 +0.0 +1.0 -1.0 -1.0 +0.0 +0.0 . | 0000 -1.0 -1.0 -1.0 . . . . . | &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First note that js demo reports which number is assigned to each joystick recognized by the driver. Also, note that the “name” each joystick reports is also included between quotes. We will need the names for each bindings file when we begin writing the binding xml files for each joystick.&lt;br /&gt;
&lt;br /&gt;
=== Identifying the numbering of axes and buttons ===&lt;br /&gt;
Axis and button numbers can be identified using js demo as follows. By observing the output of js demo while working your joystick axes and buttons you can determine what axis and button numbers are assigned to each joystick axis and button. It should be noted that numbering generally starts with zero. &lt;br /&gt;
&lt;br /&gt;
The buttons are handled internally as a binary number in which bit 0 (the least significant bit) represents button 0, bit 1 represents button 1, etc., but this number is displayed on the screen in hexadecimal notation, so: &lt;br /&gt;
&lt;br /&gt;
* 0001 ⇒ button 0 pressed &lt;br /&gt;
* 0002 ⇒ button 1 pressed &lt;br /&gt;
* 0004 ⇒ button 2 pressed &lt;br /&gt;
* 0008 ⇒ button 3 pressed &lt;br /&gt;
* 0010 ⇒ button 4 pressed &lt;br /&gt;
* 0020 ⇒ button 5 pressed &lt;br /&gt;
* 0040 ⇒ button 6 pressed &lt;br /&gt;
* ... etcp to ... &lt;br /&gt;
* 8000 ⇒ button 15 pressed &lt;br /&gt;
* ... and ... &lt;br /&gt;
* 0014 ⇒ buttons 2 and 4 pressed simultaneously &lt;br /&gt;
* ... etc. &lt;br /&gt;
&lt;br /&gt;
For Linux users, there is another option for identifying the “name” and the numbers assigned to each axis and button. Most Linux distributions include a very handy program, “jstest”. With a CH Product Yoke plugged into the system, the following output lines are displayed by jstest: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jstest /dev/js3 &lt;br /&gt;
Joystick (CH PRODUCTS CH FLIGHT SIM YOKE USB ) has 7 axes and 12 buttons. Driver version is 2.1.0 &lt;br /&gt;
Testing…(interrupt to exit) &lt;br /&gt;
Axes: 0: 0 1: 0 2: 0 3: 0 4: 0 5: 0 6: 0 Buttons: 0:off 1:off 2:off 3:on 4:off 5:off 6:off 7:off 8:off 9:off 10:off 11:off &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the “name” between parentheses. This is the name the system associates with your joystick. &lt;br /&gt;
&lt;br /&gt;
When you move any control, the numbers change after the axis number corresponding to that moving control and when you depress any button, the “off” after the button number corresponding to the button pressed changes to “on”. In this way, you can quickly write down the axes numbers and button numbers for each function without messing with binary.&lt;br /&gt;
&lt;br /&gt;
In most modern repositories, there is a graphical version of this program, called &amp;quot;jstest-gtk&amp;quot;, where you are given a list of attached devices to choose from, and then get a graphical representation of all axes and buttons.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
For many/most joysticks this step might not be necessary.  If either, the centre position of the joystick does not yield near-zero values for relevant axes, or if the maximum value for an axis cannot be reached, or is reached too early, you need to calibrate the joystick. (Note that some calibration problems can be fixed with the flighgear joystick configuration files, but not all, e.g. if the maximum value is reached too early)&lt;br /&gt;
&lt;br /&gt;
==== Linux ====&lt;br /&gt;
The program &amp;quot;jscal&amp;quot; (install from repositories if not already available on your system) provides a calibration routine for joysticks.  You need to know or find out the device name of your joystick (usually /dev/js0  or /dev/input/js0 - instead of 0 a different number might need to be used, look at output of js_demo to figure out which). For example to calibrate the joystick with device name /dev/input/js0, execute&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;jscal -c /dev/input/js0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
and follow instructions.&lt;br /&gt;
The calibration is retained until the joystick is unplugged, or the computer rebooted.  In order to save it for future use, execute &lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;jscal -p /dev/input/js0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which will print the command that needs to be entered to restore the calibration, for example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;jscal -s 6,1,0,8171,8171,65936,65374,1,0,8166,8166,65928,65494,1,0,128,128,4194176,4227201,1,0,128,128,4194176,4227201,1,0,0,0,536854528,536854528,1,0,0,0,536854528,536854528 /dev/input/js0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Copy the command you obtained into your startup script in order to restore calibration automatically.&lt;br /&gt;
&lt;br /&gt;
Recent versions of jscal have this simplified, you can store and restore your calibration settings.&lt;br /&gt;
After calibration store the settings:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;jscal-store /dev/input/js0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then in your startup script recall the settings for the given device:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;jscal-restore /dev/input/js0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However if you have multiple controllers connected, they might be renumbered on pluging/repluging. Best way to insure you get the correct calibration is to create a custom '''udev''' rule-set that you put into /etc/udev/rules.d/00-joystick.rules, similar to the following example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#joystick rules to make the names persistent and reload the stored calibration profile&lt;br /&gt;
ACTION!=&amp;quot;add|change&amp;quot;, GOTO=&amp;quot;joystick_end&amp;quot;&lt;br /&gt;
SUBSYSTEM!=&amp;quot;input&amp;quot;, GOTO=&amp;quot;joystick_end&amp;quot;&lt;br /&gt;
&lt;br /&gt;
KERNEL==&amp;quot;js*&amp;quot;, ATTRS{idProduct}==&amp;quot;a02f&amp;quot;, ATTRS{idVendor}==&amp;quot;12bd&amp;quot;, SYMLINK+=&amp;quot;input/joystick&amp;quot; RUN+=&amp;quot;/etc/udev/scripts/joycal.sh&amp;quot;&lt;br /&gt;
KERNEL==&amp;quot;js*&amp;quot;, ATTRS{name}==&amp;quot;Logitech Logitech Racing Wheel&amp;quot;, SYMLINK+=&amp;quot;input/logiwheel&amp;quot; RUN+=&amp;quot;/etc/udev/scripts/wheelcal.sh&amp;quot;&lt;br /&gt;
&lt;br /&gt;
LABEL=&amp;quot;joystick_end&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the scripts called would be as follows:&lt;br /&gt;
*/etc/udev/scripts/joycal.sh:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/sh&lt;br /&gt;
jscal-restore /dev/input/joystick&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*/etc/udev/scripts/wheelcal.sh&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/sh&lt;br /&gt;
jscal-restore /dev/input/logiwheel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now each time you plug/replug your joysticks/controllers they will get the persistent device names, and will get the correct calibration profile restored.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The calibration is even more comfortable using the program &amp;quot;jstest-gtk&amp;quot;, also available from most repositories. Starting this you are given a list of all attached joysticks with their device names. Pick the one you wish to inspect or calibrate and click 'Properties', then calibrate.  This calibration tool offers the possibility to fine-tune the calibration by editing the numbers. The program manipulates the same internals as jscal so you can use jscal to save the calibration information for later use, as before.&lt;br /&gt;
&lt;br /&gt;
=== Writing or editing joystick binding xml files ===&lt;br /&gt;
At this point, you have confirmed that the operating system and the joystick driver both recognize your joystick(s). You also know of several ways to identify the joystick “name” your joystick reports to the driver and operating system. You will need a written list of what control functions you wish to have assigned to which axis and button and the corresponding numbers. &lt;br /&gt;
&lt;br /&gt;
Make the following table from what you learned from js demo or jstest above (pencil and paper is fine). Here we assume there are 5 axes including 2 axes associated with the hat. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;prettytable&amp;quot;&lt;br /&gt;
! align=&amp;quot;center&amp;quot; bgcolor=&amp;quot;#EFEFEF&amp;quot; | Axis&lt;br /&gt;
! align=&amp;quot;center&amp;quot; bgcolor=&amp;quot;#EFEFEF&amp;quot; | Button&lt;br /&gt;
|- &lt;br /&gt;
|elevator = 0 &lt;br /&gt;
|view cycle = 0  &lt;br /&gt;
|- &lt;br /&gt;
|rudder = 1  &lt;br /&gt;
|all brakes = 1  &lt;br /&gt;
|- &lt;br /&gt;
|aileron = 2  &lt;br /&gt;
|up trim = 2  &lt;br /&gt;
|- &lt;br /&gt;
|throttle = 3  &lt;br /&gt;
|down trim = 3  &lt;br /&gt;
|-&lt;br /&gt;
|leftright hat = 4 &lt;br /&gt;
|extend flaps = 4 &lt;br /&gt;
|- &lt;br /&gt;
|foreaft hat = 5  &lt;br /&gt;
|retract flaps = 5 &lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|decrease RPM = 6 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|increase RPM = 7 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
We will assume that our hypothetical joystick supplies the “name” QUICK STICK 3D USB to the system and driver. With all the examples included with FlightGear, the easiest way to get a so far unsupported joystick to be auto detected, is to edit an existing binding xml file. Look at the xml files in the sub-folders of '''/FlightGear/Input/Joysticks/'''. After evaluating several of the xml binding files supplied with FlightGear, we decide to edit the file &amp;lt;tt&amp;gt;[[$FG_ROOT]]/Input/Joysticks/Saitek/Cyborg-Gold-3d-USB.xml&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This file has all the axes functions above assigned to axes and all the button functions above assigned to buttons. This makes our editing almost trivial. &lt;br /&gt;
&lt;br /&gt;
Before we begin to edit, we need to choose a name for our bindings xml file, create the folder for the QS joysticks, and copy the original xml file into this directory with this name. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /usr/local/FlightGear/Input/Joysticks &lt;br /&gt;
$ mkdir QS &lt;br /&gt;
$ cd QS &lt;br /&gt;
$ cp /usr/local/FlightGear/Input/Joysticks/Saitek/ &lt;br /&gt;
Cyborg-Gold-3d-USB.xml QuickStick.xml &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, we obviously have supposed a Linux/UNIX system with FlightGear being installed under '''/usr/local/FlightGear'''. For a similar procedure under Windows with FlightGear being installed under C:/Program Files/FlightGear, open a command shell and type &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
C: &lt;br /&gt;
cd /Program Files/FlightGear/Input/Joysticks &lt;br /&gt;
mkdir QS &lt;br /&gt;
cd QS &lt;br /&gt;
copy /Program Files/FlightGear/Input/Joysticks/Saitek/ &lt;br /&gt;
Cyborg-Gold-3d-USB.xml QuickStick.xml &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, open QuickStick.xml with your favorite editor. Before we forget to change the joystick name, search for the line containing &amp;lt;name&amp;gt;. You should find the line &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;name&amp;gt;SAITEK CYBORG 3D USB&amp;lt;/name&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and change it to &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;name&amp;gt;QUICK STICK 3D USB&amp;lt;/name&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This line illustrates a key feature of xml statements. They begin with a &amp;lt;tag&amp;gt; and end with a &amp;lt;/tag&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You can now compare your table to the comment table at the top of your file copy. Note that the comments tell us that the Saitek elevator was assigned to axis 1. Search for the string &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;axis n=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and change this to &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;axis n=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, note that the Saitek rudder was assigned to axis 2. Search for the string &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;axis n=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and change this to &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;axis n=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Continue comparing your table with the comment table for the Saitek and changing the axis numbers and button numbers accordingly. Since QUICKSTICK USB and the Saitek have the same number of axes but different number of buttons, you must delete the buttons left over. Just remember to double check that you have a closing tag for each opening tag or you will get an error using the file. &lt;br /&gt;
&lt;br /&gt;
Finally, be good to yourself (and others when you submit your new binding file to a FlightGear developers or users archive!), take the time to change the comment table in the edited file to match your changed axis and button assignments. The new comments should match the table you made from the js demo output. Save your edits. &lt;br /&gt;
&lt;br /&gt;
Several users have reported that the numbers of axes and buttons assigned to functions may be different with the same joystick under Windows and Linux. The above procedure should allow one to easily change a binding xml file created for a different operating system for use by their operating system.&lt;br /&gt;
&lt;br /&gt;
You can tell how FlightGear has interpretted your joystick setup by selecting &amp;lt;tt&amp;gt;Help &amp;gt; Joystick Configuration&amp;lt;/tt&amp;gt; from the menu.&lt;br /&gt;
&lt;br /&gt;
== Joystick support via .fgfsrc entries ==&lt;br /&gt;
Fortunately, there is a tool available now, which takes most of the burden from the average user who, maybe, is not that experienced with XML, the language which these files are written in. &lt;br /&gt;
&lt;br /&gt;
For configuring your joystick using this approach, open a command shell (command prompt under windows, to be found under Start|All programs|Accessories). Change to the directory &amp;lt;tt&amp;gt;[[$FG_ROOT]]/bin&amp;lt;/tt&amp;gt; via e.g. (modify to your path) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dos&amp;quot;&amp;gt;cd C:/Program Files/FlightGear/bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and invoke the tool fgjs via &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./fgjs --fg-root=$FG_ROOT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
on a UNIX/Linux machine, or via &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dos&amp;quot;&amp;gt;fgjs --fg-root=$FG_ROOT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
on a Windows machine. The program will tell you which joysticks, if any, were detected. Now follow the commands given on screen, i.eṁove the axis and press the buttons as required. Be careful, a minor touch already “counts” as a movement. Check the reports on screen. If you feel something went wrong, just re-start the program. &lt;br /&gt;
&lt;br /&gt;
After you are done with all the axis and switches, the directory above will hold a file called fgfsrc.js. If the FlightGear base directory FlightGear does not already contain an options file .fgfsrc (under UNIX)/system.fgfsrc (under Windows) mentioned above, just copy &lt;br /&gt;
&lt;br /&gt;
'''fgfsrc.js''' into '''.fgfsrc''' (UNIX)/'''system.fgfsrc''' (Windows) &lt;br /&gt;
&lt;br /&gt;
and place it into the directory FlightGear base directory FlightGear. In case you already wrote an options file, just open it as well as fgfsrc.js with an editor and copy the entries from fgfsrc.js into .fgfsrc/system.fgfsrc. One hint: The output of fgjs is UNIX formatted. As a result, Windows Editor may not display it the proper way. I suggest getting an editor being able to handle UNIX files as well, for example [https://notepad-plus-plus.org/ Notepad++]. &amp;lt;!-- My favorite freeware file editor for that purpose, although somewhat dated, is still PFE, to be obtained from http://www.lancs.ac.uk/people/cpaap/pfe/. --&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The the axis/button assignment of fgjs should, at least, get the axis assignments right, its output may need some tweaking. There may be axes moving the opposite way they should, the dead zones may be too small etc. For instance, I had to change &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
--prop:/input/joysticks/js[1]/axis[1]/binding/factor=-1.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
--prop:/input/joysticks/js[1]/axis[1]/binding/factor=1.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(USB CH Flightsim Yoke under Windows XP). Thus, here is a short introduction into the assignments of joystick properties. &lt;br /&gt;
&lt;br /&gt;
Basically, all axes settings are specified via lines having the following structure: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
--prop:/input/joysticks/js[n]/axis[m]/binding/command=property-scale &lt;br /&gt;
--prop:/input/joysticks/js[n]/axis[m]/binding/property=/controls/steering option&lt;br /&gt;
--prop:/input/joysticks/js[n]/axis[m]/binding/dead-band=db&lt;br /&gt;
--prop:/input/joysticks/js[n]/axis[m]/binding/offset=os&lt;br /&gt;
--prop:/input/joysticks/js[n]/axis[m]/binding/factor=fa&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;prettytable&amp;quot;&lt;br /&gt;
! align=&amp;quot;center&amp;quot; bgcolor=&amp;quot;#EFEFEF&amp;quot; | &lt;br /&gt;
! align=&amp;quot;center&amp;quot; bgcolor=&amp;quot;#EFEFEF&amp;quot; | &lt;br /&gt;
|- &lt;br /&gt;
|n&lt;br /&gt;
|number of device (usually starting with 0)  &lt;br /&gt;
|-&lt;br /&gt;
|m&lt;br /&gt;
|number of axis (usually starting with 0)&lt;br /&gt;
|-&lt;br /&gt;
|steering option&lt;br /&gt;
|elevator, aileron, rudder, throttle, mixture, pitch  &lt;br /&gt;
|-&lt;br /&gt;
|dead-band&lt;br /&gt;
|range, within which signals are discarded; useful to avoid jittering for minor yoke movements&lt;br /&gt;
|-&lt;br /&gt;
|offset&lt;br /&gt;
|specifies, if device not centered in its neutral position &lt;br /&gt;
|-&lt;br /&gt;
|factor&lt;br /&gt;
|controls sensitivity of that axis; defaults to +1, with a value of -1 reversing the behavior &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You should be able to at least get your joystick working along these lines. Concerning all the finer points, for instance, getting the joystick buttons working, John Check has written a very useful README being included in the base package to be found under '''FlightGear/Docs/Readme/Joystick.html'''. In case of any trouble with your input device, it is highly recommended to have a look into this document.&lt;br /&gt;
&lt;br /&gt;
== More about programming joystick XML files ==&lt;br /&gt;
=== General tips ===&lt;br /&gt;
* When testing a new xml file it is best to start FlightGear via a command window (rather than the GUI interface). Any error messages will then be displayed in the terminal. Error messages will give both a message and a line number, helping you pinpoint any errors.&lt;br /&gt;
* Errors can be detected on initial startup or at runtime. Both types of errors will be displayed in the terminal.&lt;br /&gt;
* One of the most common errors is including a character that makes XML choke. Such characters include&amp;lt;br&amp;gt;&amp;amp; &amp;lt; --&amp;lt;br&amp;gt;These characters will cause problems even if simply included in comments or within scripts.&lt;br /&gt;
* If your scripts contain any of these characters, you have to enclose the scripts in &amp;lt;script&amp;gt;&amp;lt;![CDATA[...]]&amp;gt;&amp;lt;/script&amp;gt;. Alternatively, you can 'escape' the characters, ie &amp;quot;&amp;lt;&amp;quot; becomes &amp;quot;&amp;amp;amp;lt;&amp;quot;.&lt;br /&gt;
* You can reload your edited joystick file without restarting FlightGear by selecting &amp;quot;Debug&amp;quot; &amp;amp;gt; &amp;quot;Reload Input&amp;quot; from the main simulator window.&lt;br /&gt;
* You can find many examples of different ways to program joysticks simply by examining the joystick xml files that are packaged with FlightGear. See the directory FlightGear/data/input/joysticks&lt;br /&gt;
* Many advanced functions can be programmed using the Nasal scripting language. These scripts are enclosed in &amp;lt;script&amp;gt;&amp;lt;/script&amp;gt; tags in the XML file. Helpful:&lt;br /&gt;
** A guide to the [[Nasal scripting language]] in FlightGear &lt;br /&gt;
** [[Nasal FAQ]]&lt;br /&gt;
** [[Howto: Write simple scripts in Nasal]]&lt;br /&gt;
* You can explore the internal property tree to see many variables that can be altered using joystick buttons or axes (File/Browse Internal Properties)&lt;br /&gt;
* You can test bits of Nasal code and do some other useful things using the Nasal Console (Debug/Nasal Console).&lt;br /&gt;
* All Nasal code shares a common namespace, so it's possible to set a variable in one nasal binding, and to read it in another.&lt;br /&gt;
&lt;br /&gt;
=== Useful hints for scripts ===&lt;br /&gt;
Some particularly useful ideas for programming scripts within joystick XML files:&lt;br /&gt;
* getprop and setprop can be used for getting &amp;amp; setting properties from the internal properties tree:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var brake = !getprop(&amp;quot;/controls/gear/brake-parking&amp;quot;);&lt;br /&gt;
setprop(&amp;quot;/controls/gear/brake-parking&amp;quot;, brake);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* You can also make your own values on the property tree:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
setprop(&amp;quot;/input/joysticks/js[0]/myjoystick-modifier&amp;quot;, 1);&lt;br /&gt;
var mod = getprop(&amp;quot;/input/joysticks/js[0]/myjoystick-modifier&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* You can print to terminal using the print function. This is very useful for debugging.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* You can display info in FlightGear via a popup. This is useful for giving the user feedback about changes that may not be obvious via the panel. It can also be useful for debugging. Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
gui.popupTip(&amp;quot;Parking Brake ON&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Arguments for gui.popupTip must be strings, so if you want to display other types of variables they should be formatted with something like sprintf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
gui.popupTip(sprintf(&amp;quot;Elevator trim: %d&amp;quot;, 100 * getprop(&amp;quot;/controls/flight/elevator-trim&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
thv = getprop(&amp;quot;/controls/engines/engine[0]/mixture&amp;quot;);&lt;br /&gt;
gui.popupTip(&amp;quot;Thrust vector &amp;quot; ~ int(thv * 120 - 20));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* You can just start using variables, ie,&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
x = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But [http://wiki.flightgear.org/index.php/Nasal_scripting_language#Variables for various reasons] it is generally better to declare variables with the &amp;quot;var&amp;quot; statement:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var x = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that &amp;quot;var&amp;quot; creates variables that are local in scope, but since all bindings for a joystick share the same scope, it will be seen across each script in the joystick file.&lt;br /&gt;
&lt;br /&gt;
* You can include a section of script that runs on startup to initialize variables, create functions, etc. Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
  &amp;lt;name type=&amp;quot;string&amp;quot;&amp;gt;My joystick name&amp;lt;/name&amp;gt;&lt;br /&gt;
  &amp;lt;name type=&amp;quot;string&amp;quot;&amp;gt;My joystick name #2&amp;lt;/name&amp;gt;&lt;br /&gt;
  &amp;lt;nasal&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;&lt;br /&gt;
       #initialize variables&lt;br /&gt;
       f1 = f2 = 0;&lt;br /&gt;
       left_brake = right_brake = 0;&lt;br /&gt;
       # create a function to be used with all buttons&lt;br /&gt;
       getmod = func { getprop(&amp;quot;/input/joysticks/js[0]/t-flight-hotas-x-modifier&amp;quot; ) }&lt;br /&gt;
    &amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Sample code for firing weapons with the joystick is found on the [[Gun Effects]] page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Multiple mice on Linux ==&lt;br /&gt;
&lt;br /&gt;
On Linux, it is possible to make specific mice control specific Flightgear properties instead of the default flight controls.&lt;br /&gt;
&lt;br /&gt;
You will need to know the Linux name and ID for the mouse. This can be done by running the command &amp;lt;code&amp;gt;xinput --list&amp;lt;/code&amp;gt; which  lists all connected input devices. (Extra information about a specific device can be found with &amp;lt;code&amp;gt;xinput --list-props &amp;lt;id&amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;xinput --list-props &amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
Then create a file in &amp;lt;code&amp;gt;fgdata/Input/Event/&amp;lt;/code&amp;gt;. The leafname doesn't matter, but for example it could be called &amp;lt;code&amp;gt;fgdata/Input/Event/MouseExtra.xml&amp;lt;/code&amp;gt;. The contents of this file determine the properties that the mouse will control. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
  &amp;lt;name&amp;gt;Logitech Logitech USB Optical Mouse&amp;lt;/name&amp;gt;&lt;br /&gt;
  &amp;lt;debug-events type=&amp;quot;bool&amp;quot;&amp;gt;false&amp;lt;/debug-events&amp;gt;&lt;br /&gt;
  &amp;lt;grab type=&amp;quot;bool&amp;quot;&amp;gt;true&amp;lt;/grab&amp;gt;&lt;br /&gt;
  &amp;lt;event&amp;gt;&lt;br /&gt;
    &amp;lt;desc&amp;gt;Y-Axis&amp;lt;/desc&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;rel-y-translate&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;binding&amp;gt;&lt;br /&gt;
     &amp;lt;command&amp;gt;property-adjust&amp;lt;/command&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/controls/flight/elevator&amp;lt;/property&amp;gt;&lt;br /&gt;
     &amp;lt;factor type=&amp;quot;double&amp;quot;&amp;gt;-.002&amp;lt;/factor&amp;gt;&lt;br /&gt;
     &amp;lt;min type=&amp;quot;double&amp;quot;&amp;gt;-1.0&amp;lt;/min&amp;gt;&lt;br /&gt;
     &amp;lt;max type=&amp;quot;double&amp;quot;&amp;gt;1.0&amp;lt;/max&amp;gt;&lt;br /&gt;
     &amp;lt;wrap type=&amp;quot;bool&amp;quot;&amp;gt;false&amp;lt;/wrap&amp;gt;&lt;br /&gt;
    &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;/event&amp;gt;&lt;br /&gt;
  &amp;lt;event&amp;gt;&lt;br /&gt;
    &amp;lt;desc&amp;gt;X-Axis&amp;lt;/desc&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;rel-x-translate&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;binding&amp;gt;&lt;br /&gt;
     &amp;lt;command&amp;gt;property-adjust&amp;lt;/command&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/controls/flight/aileron&amp;lt;/property&amp;gt;&lt;br /&gt;
     &amp;lt;factor type=&amp;quot;double&amp;quot;&amp;gt;.002&amp;lt;/factor&amp;gt;&lt;br /&gt;
     &amp;lt;min type=&amp;quot;double&amp;quot;&amp;gt;-1.0&amp;lt;/min&amp;gt;&lt;br /&gt;
     &amp;lt;max type=&amp;quot;double&amp;quot;&amp;gt;1.0&amp;lt;/max&amp;gt;&lt;br /&gt;
     &amp;lt;wrap type=&amp;quot;bool&amp;quot;&amp;gt;false&amp;lt;/wrap&amp;gt;&lt;br /&gt;
    &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;/event&amp;gt;&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Change &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;Logitech Logitech USB Optical Mouse&amp;lt;/name&amp;gt;&amp;lt;/code&amp;gt; to match your mouse.)&lt;br /&gt;
&lt;br /&gt;
It can be useful to tell X to ignore the mouse so that it doesn't affect the main X pointer. This can be done with: &amp;lt;code&amp;gt;xinput --set-prop &amp;lt;id&amp;gt; &amp;quot;Device Enabled&amp;quot; 0&amp;lt;/code&amp;gt;. Normal X handling of the mouse can be restored with &amp;lt;code&amp;gt;xinput --set-prop &amp;lt;id&amp;gt; &amp;quot;Device Enabled&amp;quot; 1&amp;lt;/code&amp;gt;. (One can also use the name of the mouse instead of &amp;lt;code&amp;gt;&amp;lt;id&amp;gt;&amp;lt;/code&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
On Devuan Linux, one can use a udev rule to ensure that the mouse can be read and written by flightgear (usually they are only accessible to root):&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;/etc/udev/rules.d/90-fgmouseextra.rules&amp;lt;/code&amp;gt; containing a single line &amp;lt;code&amp;gt;KERNEL==&amp;quot;hidraw*&amp;quot;, SUBSYSTEM==&amp;quot;hidraw&amp;quot;, MODE=&amp;quot;0666&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Reboot or run &amp;lt;code&amp;gt;udevadm control --reload-rules&amp;lt;/code&amp;gt;.&lt;br /&gt;
[Note that i know almost nothing about udev, and it's entirely possible that this represents a huge security flaw, so use with caution.]&lt;br /&gt;
&lt;br /&gt;
[The information above is based on the forum thread https://forum.flightgear.org/viewtopic.php?t=32750 and Torsten Dreyer's original email https://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg23171.html.]&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Bindings]]&lt;br /&gt;
* [[Force feedback]]&lt;br /&gt;
* [[Head tracking]]&lt;br /&gt;
* [[Joystick xml library]] &lt;br /&gt;
* [[Troubleshooting input devices]]&lt;br /&gt;
* [[Writing Joystick Code: Part 1]]&lt;br /&gt;
* [[Writing Joystick Code: Part 2]]&lt;br /&gt;
* [[Writing Joystick Code: Part 3]]&lt;br /&gt;
* [[Writing Joystick Code: Part 4]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|f=24|title=Hardware}}&lt;br /&gt;
* {{forum link|t=43132|title=&amp;lt;nowiki&amp;gt;[&amp;lt;/nowiki&amp;gt;HOWTO&amp;lt;nowiki&amp;gt;]&amp;lt;/nowiki&amp;gt; Use more than 32 buttons on Windows via HID}}&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [http://www.flightgear.org/Docs/getstart/getstartch3.html#x8-360003.6 The FlightGear Manual]&lt;br /&gt;
* [http://sourceforge.net/projects/hapticsforfg/ Force Feedback]&lt;br /&gt;
&lt;br /&gt;
[[Category:Hardware]]&lt;br /&gt;
[[Category:Joysticks and Yokes]]&lt;br /&gt;
&lt;br /&gt;
[[de:Joystick]]&lt;br /&gt;
[[es:Joystick]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Aircraft_properties_reference&amp;diff=141453</id>
		<title>Aircraft properties reference</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Aircraft_properties_reference&amp;diff=141453"/>
		<updated>2025-03-03T09:31:23Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* The serviceable and operable properties */ New section, based on &amp;lt;https://forum.flightgear.org/viewtopic.php?p=198298#p198298&amp;gt; and &amp;lt;flightgear/src/Instrumentation/AbstractInstrument.cxx&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{PropertyTree}}&lt;br /&gt;
&lt;br /&gt;
This is a generic '''aircraft properties reference'''.  There are many properties that are very common in aircraft, and many of them are present even in a very simple aircraft.  However, a complete description that matches all the properties is very unlikely to be written, as aircraft can be very different from each other.&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|2}}&lt;br /&gt;
&lt;br /&gt;
== What are properties? ==&lt;br /&gt;
{{main article|Property tree}}&lt;br /&gt;
=== The property tree ===&lt;br /&gt;
Most parts of FlightGear communicate with each other through key-value pair properties in the [[property tree]].  The properties represent both the input from the pilot, the values determining the position and velocity of the aircraft, the values used for animating the aircraft, and pretty much anything else.&lt;br /&gt;
&lt;br /&gt;
While many properties will be common between most aircraft, many properties will also be different between aircraft.  This becomes obvious if one consider the many different configurations (aircraft/helicopter/car, control surface and landing gear layout, number and locations of engines, etc) and propulsions systems (types of engines, types of fuels etc.) an aircraft can have.  There are also different flight dynamic models (FDMs) that have different needs.  In addition many properties will by necessity be aircraft specific, though developers should make a conscious effort to have properties map to more common ones if that is possible.&lt;br /&gt;
&lt;br /&gt;
=== Where are the properties defined? ===&lt;br /&gt;
There are a couple ways that the properties are set, but a fair amount of them just &amp;quot;appear&amp;quot; without being documented anywhere.  There are several places to look for properties.  One is in the aircraft files, starting from the aircraft specific [[aircraft-set.xml]] file, another is the [[Nasal]] files, and the last place (and often most useful!) is &amp;quot;grepping&amp;quot; (searching) through the C++ code.&lt;br /&gt;
&lt;br /&gt;
To determine how a property works and what it does often requires looking through any code that uses it.  This is a part of FlightGear that we could certainly document better&lt;br /&gt;
&lt;br /&gt;
== The serviceable and operable properties ==&lt;br /&gt;
Many systems, instruments, etc. have either or both of the properties &amp;lt;code&amp;gt;serviceable&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;operable&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;serviceable&amp;lt;/code&amp;gt; property is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; when something is functional and have not failed. Setting it to false would make it fail.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;operable&amp;lt;/code&amp;gt; property is set to &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; when something both is serviceable and have power. Unfortunately it is a tied property and you can not assign a [[Nasal library#setlistener()|Nasal listener]] to it.&lt;br /&gt;
&lt;br /&gt;
== Annunciators ==&lt;br /&gt;
The /instrumentation/annunciators section shall provide a central place for&lt;br /&gt;
&lt;br /&gt;
* aircrafts to write annunciator status data&lt;br /&gt;
* 3D aircraft models and external devices (like joysticks, yokes, throttle quadrants or custom made cockpit hardware) to read such data and drive status lights etc.&lt;br /&gt;
&lt;br /&gt;
Some of the props are automatically calculated by property rules in &amp;lt;code&amp;gt;$FGDATA/Aircraft/Generic/generic-annunciators.xml&amp;lt;/code&amp;gt; (available since FG 2024.1) other properties have to be calculated by the actual aircraft.&lt;br /&gt;
&lt;br /&gt;
The aircraft.nas module provides aircraft.light which allows easy implementation of blinking lights. Such lights have a bool property 'state' which tells if the light is on or off.&amp;lt;pre&amp;gt;&lt;br /&gt;
/instrumentation/annunciators/doors&lt;br /&gt;
/instrumentation/annunciators/master-caution/state  # aircraft.light&lt;br /&gt;
/instrumentation/annunciators/master-warning/state  # aircraft.light&lt;br /&gt;
&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/enabled&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/alt&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/apr&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/hdg&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/ias&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/nav&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/rev&lt;br /&gt;
/instrumentation/annunciators/autoflight/ap/mode/vs&lt;br /&gt;
/instrumentation/annunciators/autoflight/flightdirector/enabled&lt;br /&gt;
&lt;br /&gt;
/instrumentation/annunciators/engines/&lt;br /&gt;
/instrumentation/annunciators/engines/apu/enabled&lt;br /&gt;
/instrumentation/annunciators/engines/apu/fire&lt;br /&gt;
# summary props, 'sum' of all engines&lt;br /&gt;
/instrumentation/annunciators/engines/fire&lt;br /&gt;
/instrumentation/annunciators/engines/oil-pressure-low&lt;br /&gt;
/instrumentation/annunciators/engines/starter&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# for &amp;lt;gear-name&amp;gt; in {nose, left, right}&lt;br /&gt;
/instrumentation/annunciators/gear/&amp;lt;gear-name&amp;gt;/down&lt;br /&gt;
/instrumentation/annunciators/gear/&amp;lt;gear-name&amp;gt;/in-transition&lt;br /&gt;
/instrumentation/annunciators/gear/&amp;lt;gear-name&amp;gt;/unsafe&lt;br /&gt;
/instrumentation/annunciators/gear/&amp;lt;gear-name&amp;gt;/up&lt;br /&gt;
# gear summary&lt;br /&gt;
/instrumentation/annunciators/gear/down&lt;br /&gt;
/instrumentation/annunciators/gear/in-transition&lt;br /&gt;
/instrumentation/annunciators/gear/unsafe&lt;br /&gt;
/instrumentation/annunciators/gear/up &lt;br /&gt;
/instrumentation/annunciators/gear/parking-brake&lt;br /&gt;
&lt;br /&gt;
/instrumentation/annunciators/systems/anti-ice/enabled&lt;br /&gt;
/instrumentation/annunciators/systems/fuel/aux-pump&lt;br /&gt;
/instrumentation/annunciators/systems/fuel/pressure-low&lt;br /&gt;
/instrumentation/annunciators/systems/fuel/system[]/pressure-low&lt;br /&gt;
/instrumentation/annunciators/systems/hyd/pressure-low&lt;br /&gt;
/instrumentation/annunciators/systems/hyd/system[]/pressure-low&lt;br /&gt;
/instrumentation/annunciators/systems/vacuum&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Consumables ==&lt;br /&gt;
:''See also [[#Fuel]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/consumables/fuel/tank[%d]/level-lb&lt;br /&gt;
/consumables/fuel/tank[%d]/level-lbs&lt;br /&gt;
/consumables/fuel/tank[%d]/level-gal_us&lt;br /&gt;
/consumables/fuel/tank[%d]/capacity-gal_us&lt;br /&gt;
/consumables/fuel/tank[%d]/density-ppg&lt;br /&gt;
/consumables/fuel/total-fuel-lbs&lt;br /&gt;
/consumables/fuel/total-gal_us&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Controls ==&lt;br /&gt;
These properties are meant to represent the various cockpit controls (levers, switches, etc.)  Consider these the pilot input.&lt;br /&gt;
&lt;br /&gt;
=== Recommended usage ===&lt;br /&gt;
&lt;br /&gt;
* Animation of control elements (levers, switches, etc) themself in the 3D model of the cockpit&lt;br /&gt;
* As input to aircraft system simulation, e.g. hydraulics or electrical systems &lt;br /&gt;
&lt;br /&gt;
Avoid refering to control props directly for animating parts of the 3D model if there is an aircraft system (electric, hydraulic, ...) involved. &lt;br /&gt;
&lt;br /&gt;
Better use XML property rules (e.g. logic or filter) that calculate the component state from /controls/foo as well as &amp;quot;power available&amp;quot;, &amp;quot;component is serviceable&amp;quot; etc. &lt;br /&gt;
&lt;br /&gt;
=== Anti-ice ===&lt;br /&gt;
These properties control the various anti-ice properties that may be present in an aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/anti-ice/wing-heat&lt;br /&gt;
/controls/anti-ice/pitot-heat&lt;br /&gt;
/controls/anti-ice/wiper&lt;br /&gt;
/controls/anti-ice/window-heat&lt;br /&gt;
/controls/anti-ice/engine[%d]/carb-heat&lt;br /&gt;
/controls/anti-ice/engine[%d]/inlet-heat&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== APU ===&lt;br /&gt;
These properties control any auxiliary power unit, in essence a small turbine engine driving generators, hydraulic pumps etc. before and after the aircraft's engines are up an running.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/APU/off-start-run&lt;br /&gt;
/controls/APU/fire-switch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Armament ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/armament/master-arm&lt;br /&gt;
/controls/armament/station-select&lt;br /&gt;
/controls/armament/release-all&lt;br /&gt;
/controls/armament/station[%d]/stick-size&lt;br /&gt;
/controls/armament/station[%d]/release-stick&lt;br /&gt;
/controls/armament/station[%d]/release-all&lt;br /&gt;
/controls/armament/station[%d]/jettison-all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Autoflight ===&lt;br /&gt;
These properties control the autopilot on certain airplanes. For [[IT Autoflight]] based systems, see [[IT Autoflight#Interface_Reference|here]].&lt;br /&gt;
&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/autoflight/autopilot[%d]/engage&lt;br /&gt;
/controls/autoflight/autothrottle-arm&lt;br /&gt;
/controls/autoflight/autothrottle-engage&lt;br /&gt;
/controls/autoflight/heading-select&lt;br /&gt;
/controls/autoflight/altitude-select&lt;br /&gt;
/controls/autoflight/bank-angle-select&lt;br /&gt;
/controls/autoflight/vertical-speed-select&lt;br /&gt;
/controls/autoflight/speed-select&lt;br /&gt;
/controls/autoflight/mach-select&lt;br /&gt;
/controls/autoflight/vertical-mode&lt;br /&gt;
/controls/autoflight/lateral-mode&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Electric ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/electric/battery-switch&lt;br /&gt;
/controls/electric/external-power&lt;br /&gt;
/controls/electric/APU-generator&lt;br /&gt;
/controls/electric/engine[%d]/generator&lt;br /&gt;
/controls/electric/engine[%d]/bus-tie&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Engines ===&lt;br /&gt;
Engines are numbered engine[0] for a single engine to engine[0] to engine[3] for a 747 for example. (The model allows for up to 12 engines rumour has it ;-)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/engines/throttle_idle&lt;br /&gt;
/controls/engines/engine[%d]/throttle&lt;br /&gt;
/controls/engines/engine[%d]/starter&lt;br /&gt;
/controls/engines/engine[%d]/fuel-pump&lt;br /&gt;
/controls/engines/engine[%d]/fire-switch&lt;br /&gt;
/controls/engines/engine[%d]/fire-bottle-discharge&lt;br /&gt;
/controls/engines/engine[%d]/cutoff&lt;br /&gt;
/controls/engines/engine[%d]/mixture&lt;br /&gt;
/controls/engines/engine[%d]/propeller-pitch&lt;br /&gt;
/controls/engines/engine[%d]/magnetos&lt;br /&gt;
/controls/engines/engine[%d]/boost&lt;br /&gt;
/controls/engines/engine[%d]/WEP&lt;br /&gt;
/controls/engines/engine[%d]/cowl-flaps-norm&lt;br /&gt;
/controls/engines/engine[%d]/feather&lt;br /&gt;
/controls/engines/engine[%d]/ignition&lt;br /&gt;
/controls/engines/engine[%d]/augmentation&lt;br /&gt;
/controls/engines/engine[%d]/afterburner&lt;br /&gt;
/controls/engines/engine[%d]/reverser&lt;br /&gt;
/controls/engines/engine[%d]/water-injection&lt;br /&gt;
/controls/engines/engine[%d]/condition&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Flight controls ===&lt;br /&gt;
These properties control the flight controls surfaces, though often through a mechanical, analog or digital flight control system (FCS) that may or may not be modeled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/flight/aileron&lt;br /&gt;
/controls/flight/aileron-trim&lt;br /&gt;
/controls/flight/elevator&lt;br /&gt;
/controls/flight/elevator-trim&lt;br /&gt;
/controls/flight/rudder&lt;br /&gt;
/controls/flight/rudder-trim&lt;br /&gt;
/controls/flight/flaps&lt;br /&gt;
/controls/flight/slats&lt;br /&gt;
/controls/flight/BLC			// Boundary Layer Control&lt;br /&gt;
/controls/flight/spoilers&lt;br /&gt;
/controls/flight/speedbrake&lt;br /&gt;
/controls/flight/wing-sweep&lt;br /&gt;
/controls/flight/wing-fold&lt;br /&gt;
/controls/flight/drag-chute&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The positions of the controls are usually put in &amp;lt;code&amp;gt;/surface-positions/&amp;lt;/code&amp;gt; at the discretion of the aircraft designer. These usually drive the animations of the control surfaces. They are either normalized (like &amp;lt;code&amp;gt;/surface-positions/elevator-pos-norm&amp;lt;/code&amp;gt;) or in degrees, and sometimes the aileron is split left/right.&lt;br /&gt;
&lt;br /&gt;
=== Fuel ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/fuel/dump-valve&lt;br /&gt;
/controls/fuel/tank[%d]/fuel_selector&lt;br /&gt;
/controls/fuel/tank[%d]/to_engine&lt;br /&gt;
/controls/fuel/tank[%d]/to_tank&lt;br /&gt;
/controls/fuel/tank[%d]/boost-pump[%d]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Gear ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/gear/brake-left&lt;br /&gt;
/controls/gear/brake-right&lt;br /&gt;
/controls/gear/brake-parking&lt;br /&gt;
/controls/gear/steering // Used if rudder is not sufficient for control of steering&lt;br /&gt;
/controls/gear/gear-down&lt;br /&gt;
/controls/gear/antiskid // Deprecated?&lt;br /&gt;
/controls/gear/tailhook&lt;br /&gt;
/controls/gear/tailwheel-lock&lt;br /&gt;
/controls/gear/wheel[%d]/alternate-extension&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Hydraulics ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/hydraulic/system[%d]/engine-pump&lt;br /&gt;
/controls/hydraulic/system[%d]/electric-pump&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Lights ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/lighting/landing-lights&lt;br /&gt;
/controls/lighting/turn-off-lights&lt;br /&gt;
/controls/lighting/formation-lights&lt;br /&gt;
/controls/lighting/taxi-light&lt;br /&gt;
/controls/lighting/logo-lights&lt;br /&gt;
/controls/lighting/nav-lights&lt;br /&gt;
/controls/lighting/beacon&lt;br /&gt;
/controls/lighting/strobe&lt;br /&gt;
/controls/lighting/panel-norm&lt;br /&gt;
/controls/lighting/instruments-norm&lt;br /&gt;
/controls/lighting/dome-norm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Pneumatic ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/pneumatic/APU-bleed&lt;br /&gt;
/controls/pneumatic/engine[%d]/bleed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Pressurization ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/pressurization/mode&lt;br /&gt;
/controls/pressurization/dump&lt;br /&gt;
/controls/pressurization/outflow-valve&lt;br /&gt;
/controls/pressurization/pack[%d]/pack-on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Seat ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controls/seat/vertical-adjust&lt;br /&gt;
/controls/seat/fore-aft-adjust&lt;br /&gt;
/controls/seat/cmd_selector_valve&lt;br /&gt;
/controls/seat/eject[%d]/initiate&lt;br /&gt;
/controls/seat/eject[%d]/status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
== Engines ==&lt;br /&gt;
Status of engines. See also: /controls/engines/ &lt;br /&gt;
=== Common ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/engines/engine[%d]/fuel-flow-gph&lt;br /&gt;
/engines/engine[%d]/fuel-flow_pph&lt;br /&gt;
/engines/engine[%d]/thrust_lb&lt;br /&gt;
/engines/engine[%d]/running&lt;br /&gt;
/engines/engine[%d]/starter&lt;br /&gt;
/engines/engine[%d]/cranking&lt;br /&gt;
/engines/engine[%d]/fire&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Turbine ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/engines/engine[%d]/n1&lt;br /&gt;
/engines/engine[%d]/n2&lt;br /&gt;
/engines/engine[%d]/epr&lt;br /&gt;
/engines/engine[%d]/augmentation&lt;br /&gt;
/engines/engine[%d]/water-injection&lt;br /&gt;
/engines/engine[%d]/ignition&lt;br /&gt;
/engines/engine[%d]/nozzle-pos-norm&lt;br /&gt;
/engines/engine[%d]/inlet-pos-norm&lt;br /&gt;
/engines/engine[%d]/reversed&lt;br /&gt;
/engines/engine[%d]/cutoff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Piston ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/engines/engine[%d]/mp-osi&lt;br /&gt;
/engines/engine[%d]/egt-degf&lt;br /&gt;
/engines/engine[%d]/oil-temperature-degf&lt;br /&gt;
/engines/engine[%d]/oil-pressure-psi&lt;br /&gt;
/engines/engine[%d]/cht-degf&lt;br /&gt;
/engines/engine[%d]/rpm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Propeller ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/engines/engine[%d]/rpm&lt;br /&gt;
/engines/engine[%d]/pitch&lt;br /&gt;
/engines/engine[%d]/torque&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
== Flight Dynamics Model ==&lt;br /&gt;
=== Position ===&lt;br /&gt;
This will return the current position of the aircraft within FlightGear. This is also the stuff that is transmitted in [[Howto:Multiplayer|multiplayer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/position/&lt;br /&gt;
/position/altitiude-ft ()&lt;br /&gt;
/position/altitude-agl-ft (22.46983965)&lt;br /&gt;
/position/altitude-ft (28.24368289)&lt;br /&gt;
/position/ground-elev-ft (-0.43513529)&lt;br /&gt;
/position/ground-elev-m (-0.1326292364)&lt;br /&gt;
/position/latitude-deg (37.61371436)&lt;br /&gt;
/position/latitude-string (37*36 49.4N)&lt;br /&gt;
/position/longitude-deg (-122.3576508)&lt;br /&gt;
/position/longitude-string (-122*21 27.5W)&lt;br /&gt;
/position/sea-level-radius-ft (20899648.76)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Orientation ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/orientation/roll-deg&lt;br /&gt;
/orientation/pitch-deg&lt;br /&gt;
/orientation/heading-deg&lt;br /&gt;
&lt;br /&gt;
/orientation/roll-rate-degps&lt;br /&gt;
/orientation/pitch-rate-degps&lt;br /&gt;
/orientation/yaw-rate-degps&lt;br /&gt;
&lt;br /&gt;
/orientation/side-slip-rad&lt;br /&gt;
/orientation/side-slip-deg&lt;br /&gt;
/orientation/alpha-deg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Velocities ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/velocities/airspeed-kt&lt;br /&gt;
/velocities/mach&lt;br /&gt;
/velocities/speed-north-fps&lt;br /&gt;
/velocities/speed-east-fps&lt;br /&gt;
/velocities/speed-down-fps&lt;br /&gt;
&lt;br /&gt;
/velocities/uBody-fps&lt;br /&gt;
/velocities/vBody-fps&lt;br /&gt;
/velocities/wBody-fps&lt;br /&gt;
&lt;br /&gt;
/velocities/vertical-speed-fps&lt;br /&gt;
/velocities/glideslope&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
=== Acceleration ===&lt;br /&gt;
TODO - explain of section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/accelerations/nlf&lt;br /&gt;
&lt;br /&gt;
/accelerations/ned/north-accel-fps_sec&lt;br /&gt;
/accelerations/ned/east-accel-fps_sec&lt;br /&gt;
/accelerations/ned/down-accel-fps_sec&lt;br /&gt;
&lt;br /&gt;
/accelerations/pilot/x-accel-fps_sec&lt;br /&gt;
/accelerations/pilot/y-accel-fps_sec&lt;br /&gt;
/accelerations/pilot/z-accel-fps_sec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
TODO - section notes&lt;br /&gt;
&lt;br /&gt;
== Gear ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/gear/serviceable&lt;br /&gt;
/gear/gear[%d]/cast-angle-deg // The angle of the wheel where 0 is pointing straight forward&lt;br /&gt;
/gear/gear[%d]/compression-m&lt;br /&gt;
/gear/gear[%d]/compression-norm&lt;br /&gt;
/gear/gear[%d]/ground-friction-factor&lt;br /&gt;
/gear/gear[%d]/ground-is-solid&lt;br /&gt;
/gear/gear[%d]/has-brake&lt;br /&gt;
/gear/gear[%d]/rollspeed-ms // Speed of the wheel's rotation in meters per second&lt;br /&gt;
/gear/gear[%d]/wow // Weight-on-wheel&lt;br /&gt;
/gear/gear[%d]/xoffset-in&lt;br /&gt;
/gear/gear[%d]/yoffset-in&lt;br /&gt;
/gear/gear[%d]/zoffset-in&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Instrumentation ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/instrumentation/adf[%d]/&lt;br /&gt;
/instrumentation/airspeed-indicator/&lt;br /&gt;
/instrumentation/altimeter/&lt;br /&gt;
/instrumentation/annunciator/&lt;br /&gt;
/instrumentation/attitude-indicator/&lt;br /&gt;
/instrumentation/clock/&lt;br /&gt;
/instrumentation/comm[%d]/&lt;br /&gt;
/instrumentation/dme/&lt;br /&gt;
/instrumentation/efis/&lt;br /&gt;
/instrumentation/encoder/&lt;br /&gt;
/instrumentation/flightdirector/&lt;br /&gt;
/instrumentation/gps/&lt;br /&gt;
/instrumentation/gps-annunciator/&lt;br /&gt;
/instrumentation/heading-indicator/&lt;br /&gt;
/instrumentation/heading-indicator-fg/&lt;br /&gt;
/instrumentation/magnetic-compass/&lt;br /&gt;
/instrumentation/marker-beacon/&lt;br /&gt;
/instrumentation/nav[%d]/&lt;br /&gt;
/instrumentation/radar/&lt;br /&gt;
/instrumentation/slip-skid-ball/&lt;br /&gt;
/instrumentation/tacan[%d]/&lt;br /&gt;
/instrumentation/transponder/&lt;br /&gt;
/instrumentation/turn-indicator/&lt;br /&gt;
/instrumentation/vertical-speed-indicator/&lt;br /&gt;
/instrumentation/wxradar/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Instrumentation ADF ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/instrumentation/adf[%d]/adf-btn&lt;br /&gt;
/instrumentation/adf[%d]/bro-btn&lt;br /&gt;
/instrumentation/adf[%d]/display-mode&lt;br /&gt;
/instrumentation/adf[%d]/enroute-timer/running&lt;br /&gt;
/instrumentation/adf[%d]/enroute-timer/start-time&lt;br /&gt;
/instrumentation/adf[%d]/enroute-timer/time&lt;br /&gt;
/instrumentation/adf[%d]/error-deg&lt;br /&gt;
/instrumentation/adf[%d]/flight-timer/running&lt;br /&gt;
/instrumentation/adf[%d]/flight-timer/start-time&lt;br /&gt;
/instrumentation/adf[%d]/flight-timer/time&lt;br /&gt;
/instrumentation/adf[%d]/flt-btn&lt;br /&gt;
/instrumentation/adf[%d]/frequencies/dial-1-khz&lt;br /&gt;
/instrumentation/adf[%d]/frequencies/dial-100-khz&lt;br /&gt;
/instrumentation/adf[%d]/frequencies/selected-khz&lt;br /&gt;
/instrumentation/adf[%d]/frequencies/standby-khz&lt;br /&gt;
/instrumentation/adf[%d]/frq-btn&lt;br /&gt;
/instrumentation/adf[%d]/ident&lt;br /&gt;
/instrumentation/adf[[%d]/ident-audible&lt;br /&gt;
/instrumentation/adf[%d]/in-range&lt;br /&gt;
/instrumentation/adf[%d]/indicated-bearing-deg&lt;br /&gt;
/instrumentation/adf[%d]/mode&lt;br /&gt;
/instrumentation/adf[%d]/model&lt;br /&gt;
/instrumentation/adf[%d]/operable&lt;br /&gt;
/instrumentation/adf[%d]/power-btn&lt;br /&gt;
/instrumentation/adf[%d]/right-display&lt;br /&gt;
/instrumentation/adf[%d]/rotation-deg&lt;br /&gt;
/instrumentation/adf[%d]/serviceable&lt;br /&gt;
/instrumentation/adf[%d]/set-btn&lt;br /&gt;
/instrumentation/adf[%d]/volume&lt;br /&gt;
/instrumentation/adf[%d]/volume-norm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Instrumentation comm radio ===&lt;br /&gt;
See [[Aircraft_properties_reference/Instrumentation/COMM]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Instrumentation NAV radio ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/instrumentation/nav[%d]/audio-btn&lt;br /&gt;
/instrumentation/nav[%d]/back-course-btn&lt;br /&gt;
/instrumentation/nav[%d]/cdi/serviceable&lt;br /&gt;
/instrumentation/nav[%d]/crosstrack-error-m&lt;br /&gt;
/instrumentation/nav[%d]/crosstrack-heading-error-deg&lt;br /&gt;
/instrumentation/nav[%d]/data-is-valid&lt;br /&gt;
/instrumentation/nav[%d]/dme-in-range&lt;br /&gt;
/instrumentation/nav[%d]/filtered-cdiNAV0-deflection&lt;br /&gt;
/instrumentation/nav[%d]/filtered-gsNAV0-deflection&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/dial-khz&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/dial-mhz&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/is-localizer-frequency&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/selected-mhz&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/selected-mhz-fmt&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/standby-mhz&lt;br /&gt;
/instrumentation/nav[%d]/frequencies/standby-mhz-fmt&lt;br /&gt;
/instrumentation/nav[%d]/from-flag&lt;br /&gt;
/instrumentation/nav[%d]/frq-swap-btn&lt;br /&gt;
/instrumentation/nav[%d]/gs/serviceable&lt;br /&gt;
/instrumentation/nav[%d]/gs-direct-deg&lt;br /&gt;
/instrumentation/nav[%d]/gs-distance&lt;br /&gt;
/instrumentation/nav[%d]/gs-in-range&lt;br /&gt;
/instrumentation/nav[%d]/gs-needle-deflection&lt;br /&gt;
/instrumentation/nav[%d]/gs-needle-deflection-deg&lt;br /&gt;
/instrumentation/nav[%d]/gs-needle-deflection-norm&lt;br /&gt;
/instrumentation/nav[%d]/gs-rate-of-climb&lt;br /&gt;
/instrumentation/nav[%d]/gs-rate-of-climb-fpm&lt;br /&gt;
/instrumentation/nav[%d]/has-gs&lt;br /&gt;
/instrumentation/nav[%d]/heading-deg&lt;br /&gt;
/instrumentation/nav[%d]/heading-needle-deflection&lt;br /&gt;
/instrumentation/nav[%d]/heading-needle-deflection-norm&lt;br /&gt;
/instrumentation/nav[%d]/ident&lt;br /&gt;
/instrumentation/nav[%d]/ident-audible&lt;br /&gt;
/instrumentation/nav[%d]/in-range&lt;br /&gt;
/instrumentation/nav[%d]/nav-distance&lt;br /&gt;
/instrumentation/nav[%d]/nav-id&lt;br /&gt;
/instrumentation/nav[%d]/nav-id_asc1&lt;br /&gt;
/instrumentation/nav[%d]/nav-id_asc2&lt;br /&gt;
/instrumentation/nav[%d]/nav-id_asc3&lt;br /&gt;
/instrumentation/nav[%d]/nav-id_asc4&lt;br /&gt;
/instrumentation/nav[%d]/nav-loc&lt;br /&gt;
/instrumentation/nav[%d]/operable&lt;br /&gt;
/instrumentation/nav[%d]/power-btn&lt;br /&gt;
/instrumentation/nav[%d]/radials/actual-deg&lt;br /&gt;
/instrumentation/nav[%d]/radials/reciprocal-radial-deg&lt;br /&gt;
/instrumentation/nav[%d]/radials/selected-deg&lt;br /&gt;
/instrumentation/nav[%d]/radials/target-auto-hdg-deg&lt;br /&gt;
/instrumentation/nav[%d]/radials/arget-radial-deg&lt;br /&gt;
/instrumentation/nav[%d]/serviceable&lt;br /&gt;
/instrumentation/nav[%d]/signal-quality-norm&lt;br /&gt;
/instrumentation/nav[%d]/slaved-to-gps&lt;br /&gt;
/instrumentation/nav[%d]/time-to-intercept-sec&lt;br /&gt;
/instrumentation/nav[%d]/to-flag&lt;br /&gt;
/instrumentation/nav[%d]/to-from/serviceable&lt;br /&gt;
/instrumentation/nav[%d]/volume&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Instrumentation Tacan ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/instrumentation/tacan[%d]/display/channel&lt;br /&gt;
/instrumentation/tacan[%d]/display/x-shift&lt;br /&gt;
/instrumentation/tacan[%d]/display/y-shift&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-channel&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-channel[1]&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-channel[2]&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-channel[3]&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-channel[4]&lt;br /&gt;
/instrumentation/tacan[%d]/frequencies/selected-mhz&lt;br /&gt;
/instrumentation/tacan[%d]/ident&lt;br /&gt;
/instrumentation/tacan[%d]/in-range&lt;br /&gt;
/instrumentation/tacan[%d]/indicated-bearing-true-deg&lt;br /&gt;
/instrumentation/tacan[%d]/indicated-distance-nm&lt;br /&gt;
/instrumentation/tacan[%d]/indicated-ground-speed-kt&lt;br /&gt;
/instrumentation/tacan[%d]/indicated-time-min&lt;br /&gt;
/instrumentation/tacan[%d]/name&lt;br /&gt;
/instrumentation/tacan[%d]/serviceable&lt;br /&gt;
/instrumentation/tacan[%d]/switch-position&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rotors (YASim only) ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/rotors/gear/torque-sound-filtered // Unused?&lt;br /&gt;
/rotors/gear/total-torque&lt;br /&gt;
/rotors/{name}/balance&lt;br /&gt;
/rotors/{name}/blade[%d]/flap-deg&lt;br /&gt;
/rotors/{name}/blade[%d]/incidence-deg&lt;br /&gt;
/rotors/{name}/blade[%d]/position-deg // Position relative to model&lt;br /&gt;
/rotors/{name}/bladesvisible // Used for animations&lt;br /&gt;
/rotors/{name}/cone%d-deg //e.g. cone-deg or cone2-deg&lt;br /&gt;
/rotors/{name}/roll-deg&lt;br /&gt;
/rotors/{name}/rpm&lt;br /&gt;
/rotors/{name}/stall&lt;br /&gt;
/rotors/{name}/stall-filtered&lt;br /&gt;
/rotors/{name}/tilt&lt;br /&gt;
/rotors/{name}/torque&lt;br /&gt;
/rotors/{name}/yaw-deg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For how to animate rotors using these properties, see [[Howto:Animate helicopters]].&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Aircraft-set.xml]]&lt;br /&gt;
* [[Multiplayer protocol]]&lt;br /&gt;
* [[Property browser]]&lt;br /&gt;
* [[PropertyList XML files]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|properties}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== Consumables ====&lt;br /&gt;
* {{flightgear file|src/FDM/TankProperties.hxx}}&lt;br /&gt;
* {{flightgear file|src/FDM/TankProperties.cxx}}&lt;br /&gt;
&lt;br /&gt;
==== Controls ====&lt;br /&gt;
* {{flightgear file|src/Aircraft/controls.hxx}}&lt;br /&gt;
* {{flightgear file|src/Aircraft/controls.cxx}}&lt;br /&gt;
&lt;br /&gt;
==== Flight Dynamics Model ====&lt;br /&gt;
* {{flightgear file|src/FDM/flightProperties.hxx}}&lt;br /&gt;
* {{flightgear file|src/FDM/flightProperties.cxx}}&lt;br /&gt;
&lt;br /&gt;
==== Instrumentation ====&lt;br /&gt;
* {{flightgear file|src/Instrumentation}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Property Tree]]&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141414</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141414"/>
		<updated>2025-02-26T11:35:40Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Clarifications about multiplayer damage.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots when both have multiplayer damage enabled..}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Blacklist files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsign-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/model-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Taking multiplayer damage ===&lt;br /&gt;
{{tip| Do not feed the trolls. Ignore the trolls.}}&lt;br /&gt;
&lt;br /&gt;
The Emesary multiplayer damage system used in some aircraft do not take into account if an aircraft is ignored or not. If both of you have multiplayer damage enabled your aircraft will take damage, even though you can not see the other aircraft.&lt;br /&gt;
&lt;br /&gt;
There are basically two ways around this:&lt;br /&gt;
* Do not fly with multiplayer damage always on, or at least not if you are not interested in fighting trolls.&lt;br /&gt;
* Change to a private multiplayer server (and do not communicate the change over multiplayer chat or some other way the troll could see or hear).&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by {{usr|PINTO|pinto}}, which in turn was based on a snippet by {{usr|Warty}} in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|text=Blacklist add-on|t=43066}}&lt;br /&gt;
* {{forum link|text=Trolls|p=274977}} Warty's snippet.&lt;br /&gt;
&lt;br /&gt;
[[Category:Multiplayer]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141413</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141413"/>
		<updated>2025-02-26T11:23:48Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +links: Forum topic and Warty's snippet; +cat: Multiplayer&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Blacklist files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsign-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/model-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Taking multiplayer damage ===&lt;br /&gt;
{{tip| Do not feed the trolls. Ignore the trolls.}}&lt;br /&gt;
&lt;br /&gt;
The Emesary multiplayer damage system used in some aircraft do not take into account if an aircraft is ignored or not. My understanding is that this is an anti-cheat feature.&lt;br /&gt;
&lt;br /&gt;
There are basically two ways around this:&lt;br /&gt;
* Do not fly with multiplayer damage always on, or at least not if you are not interested in fighting trolls.&lt;br /&gt;
* Change to a private multiplayer server (and do not communicate the change over multiplayer chat or some other way the troll could see or hear).&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by {{usr|PINTO|pinto}}, which in turn was based on a snippet by {{usr|Warty}} in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|text=Blacklist add-on|t=43066}}&lt;br /&gt;
* {{forum link|text=Trolls|p=274977}} Warty's snippet.&lt;br /&gt;
&lt;br /&gt;
[[Category:Multiplayer]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141412</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141412"/>
		<updated>2025-02-26T10:58:42Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* History */ + Links to the user pages of the earlier authors&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Blacklist files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsign-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/model-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Taking multiplayer damage ===&lt;br /&gt;
{{tip| Do not feed the trolls. Ignore the trolls.}}&lt;br /&gt;
&lt;br /&gt;
The Emesary multiplayer damage system used in some aircraft do not take into account if an aircraft is ignored or not. My understanding is that this is an anti-cheat feature.&lt;br /&gt;
&lt;br /&gt;
There are basically two ways around this:&lt;br /&gt;
* Do not fly with multiplayer damage always on, or at least not if you are not interested in fighting trolls.&lt;br /&gt;
* Change to a private multiplayer server (and do not communicate the change over multiplayer chat or some other way the troll could see or hear).&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by {{usr|PINTO|pinto}}, which in turn was based on a snippet by {{usr|Warty}} in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141411</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141411"/>
		<updated>2025-02-26T10:47:36Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +- The blacklists have been renamed in the latest commit&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Blacklist files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsign-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/model-blacklist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Taking multiplayer damage ===&lt;br /&gt;
{{tip| Do not feed the trolls. Ignore the trolls.}}&lt;br /&gt;
&lt;br /&gt;
The Emesary multiplayer damage system used in some aircraft do not take into account if an aircraft is ignored or not. My understanding is that this is an anti-cheat feature.&lt;br /&gt;
&lt;br /&gt;
There are basically two ways around this:&lt;br /&gt;
* Do not fly with multiplayer damage always on, or at least not if you are not interested in fighting trolls.&lt;br /&gt;
* Change to a private multiplayer server (and do not communicate the change over multiplayer chat or some other way the troll could see or hear).&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by pinto, which in turn was based on a snippet by Warty in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141410</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141410"/>
		<updated>2025-02-26T10:01:36Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Known issues */ New section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsigns-to-ignore.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/models-to-ignore.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Taking multiplayer damage ===&lt;br /&gt;
{{tip| Do not feed the trolls. Ignore the trolls.}}&lt;br /&gt;
&lt;br /&gt;
The Emesary multiplayer damage system used in some aircraft do not take into account if an aircraft is ignored or not. My understanding is that this is an anti-cheat feature.&lt;br /&gt;
&lt;br /&gt;
There are basically two ways around this:&lt;br /&gt;
* Do not fly with multiplayer damage always on, or at least not if you are not interested in fighting trolls.&lt;br /&gt;
* Change to a private multiplayer server (and do not communicate the change over multiplayer chat or some other way the troll could see or hear).&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by pinto, which in turn was based on a snippet by Warty in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141409</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141409"/>
		<updated>2025-02-26T09:44:27Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* List of Addons */ + Blacklist add-on; alphasort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=User:Johan_G/Things_I_have_done&amp;diff=141408</id>
		<title>User:Johan G/Things I have done</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=User:Johan_G/Things_I_have_done&amp;diff=141408"/>
		<updated>2025-02-26T09:39:16Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* New pages */ + Blacklist add-on&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These are ''some'' of the '''things I have done''' on the wiki.&lt;br /&gt;
&lt;br /&gt;
''For a complete list see [[Special:Contributions/Johan_G]].''&lt;br /&gt;
&lt;br /&gt;
== New pages ==&lt;br /&gt;
* [[Howto:Add blackout and redout settings]]&lt;br /&gt;
* [[Howto:Add multi-key commands to an aircraft]]&lt;br /&gt;
* [[Aerial refueling improvement ideas and resources]]&lt;br /&gt;
* [[Aircraft interception]]&lt;br /&gt;
* [[Blacklist add-on]]&lt;br /&gt;
* [[Dassault Mirage F1]]&lt;br /&gt;
* [[FlightGear wiki:FlightGear screenshot categories]]&lt;br /&gt;
* [[Flightplan XML formats]]&lt;br /&gt;
* [[Hand camera HK 101B add-on]]&lt;br /&gt;
* [[Head tracking]]&lt;br /&gt;
* [[Help:Tables]]&lt;br /&gt;
* [[Log time-stamper add-on]]&lt;br /&gt;
* [[Pilotage and dead reckoning]]&lt;br /&gt;
* [[Pilot List]]&lt;br /&gt;
* [[TerraMaster]]&lt;br /&gt;
* [[Time in FlightGear]]&lt;br /&gt;
&lt;br /&gt;
== Larger edits ==&lt;br /&gt;
* Replaced the text in [[Howto: Edit a livery]] ([http://wiki.flightgear.org/index.php?title=Howto:_Edit_a_livery&amp;amp;oldid=36141 original text]) with my own draft ([http://wiki.flightgear.org/index.php?title=User:Johan_G/Howto:_Edit_a_livery&amp;amp;oldid=39121 Final draft]).&lt;br /&gt;
* [[Help:Templates]] – Rewritten it more or less completely ([http://wiki.flightgear.org/index.php?title=Help%3ATemplates&amp;amp;diff=64365&amp;amp;oldid=44878 diff])&lt;br /&gt;
* [[Help:Formatting]] – Complete rewrite of the former [http://wiki.flightgear.org/index.php?title=Help:Tutorial&amp;amp;oldid=42573 Help:Tutorial]&lt;br /&gt;
* [[Help:Your first article]] – Complete rewrite of [http://wiki.flightgear.org/index.php?title=Help:Your_first_article&amp;amp;oldid=64321 Help:Your first article]&lt;br /&gt;
&lt;br /&gt;
== Templates ==&lt;br /&gt;
* A lot of template documentation&lt;br /&gt;
* Unification of the template documentation style (manually editing all existing template documentation!)&lt;br /&gt;
* {{tl|Welcome to the wiki}}&lt;br /&gt;
* {{tl|alds}} – For use on the [[Airliner Development Status]] page&lt;br /&gt;
* {{tl|due date}} – When you want to show one text before and another one after a certain date.&lt;br /&gt;
* {{tl|issue}} – For referencing bug tracker issues (be that bugs or feature requests).&lt;br /&gt;
* {{tl|key press}} – For illustrating key presses and combinations.&lt;br /&gt;
* {{tl|LangSwitch}} – For showing different texts depending on the language of a page.&lt;br /&gt;
* {{tl|rule of thumb}} – For rules of thumb, like 2 kt is about 1 m/s.&lt;br /&gt;
* {{tl|wikipedia}} – For links to Wikipedia.&lt;br /&gt;
* {{tl|yes}}, {{tl|partial}}, {{tl|no}} and {{tl|n/a}} – For use in various tables.&lt;br /&gt;
* Split up [http://wiki.flightgear.org/index.php?title=Template:Issue_Tracker&amp;amp;oldid=83057 &amp;lt;nowiki&amp;gt;{{Issue Tracker}}&amp;lt;/nowiki&amp;gt;] into {{tl|register to sf}}, {{tl|create ticket}} and {{tl|tickets}}.&lt;br /&gt;
* {{tl|fg screenshot cat}} – To lessen the typing when adding descriptions to FlightGear screenshot categories.&lt;br /&gt;
* {{tl|screenshot cat}} - To add to pages about subjects there are a screenshot category for.&lt;br /&gt;
* {{tl|readme file}} – To link to readme files in [[$FG_DATA]]/Docs.&lt;br /&gt;
* {{tl|fgaddon revision}}, {{tl|fgdata commit}}, {{tl|simgear commit}} and {{tl|flightgear commit}} – For links to revision/commit summaries on the repositories.&lt;br /&gt;
&lt;br /&gt;
== Uploaded images ==&lt;br /&gt;
* [[Special:ListFiles/Johan_G]]&lt;br /&gt;
&lt;br /&gt;
[[Category:User pages]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141407</id>
		<title>Blacklist add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Blacklist_add-on&amp;diff=141407"/>
		<updated>2025-02-26T09:39:00Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Created page with &amp;quot;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}} {{infobox addon | name         = Blacklist | started      = 13 November 2024 | desc         =  | contributors = {{usr|Johan G}}, pinto, Warty | status       = mature | coderepo     = https://gitlab.com/JohanG/blacklist }}  The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text fi...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{warning|Aircraft using the Emesary multiplayer damage system '''will take damage''' from ignored pilots.}}&lt;br /&gt;
{{infobox addon&lt;br /&gt;
| name         = Blacklist&lt;br /&gt;
| started      = 13 November 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}, pinto, Warty&lt;br /&gt;
| status       = mature&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/blacklist&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''Blacklist''' add-on will every few seconds automatically ignore pilots that are listed either in a text file with callsigns or a text file with multiplayer models.&lt;br /&gt;
&lt;br /&gt;
This can be useful for example if there often are trolls using the same callsigns, or if there are heavy models, or models breaking things.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
For installation instructions, see [[Addon#Installing and using an addon]].&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
{{note|The files must end with an empty line. Otherwise the last callsign or model in the list will not be ignored.}}&lt;br /&gt;
&lt;br /&gt;
The files with callsigns and models to ignore are:&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/callsigns-to-ignore.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[$FG_HOME]]/Export/Addons/org.flightgear.addons.blacklist/models-to-ignore.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callsigns and multiplayer models should be as listed in the [[Pilot List]] dialog.&lt;br /&gt;
&lt;br /&gt;
When starting FlightGear with the add-on installed the files will be created it they do not exist.&lt;br /&gt;
&lt;br /&gt;
If you edit them during a FlightGear session you can reload them using ''Main Menu'' &amp;amp;gt; ''Blacklist'' &amp;amp;gt; ''Reload or create blacklists''.&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
The blacklist add-on is based on a Nasal module by pinto, which in turn was based on a snippet by Warty in a FlightGear forum topic, most likely {{forum link|text=this one|p=274977}}.&lt;br /&gt;
&lt;br /&gt;
Pinto had sometimes talked about making an add-on of the Nasal module, but after a couple of years I did this one.&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=TerraMaster&amp;diff=141187</id>
		<title>TerraMaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=TerraMaster&amp;diff=141187"/>
		<updated>2025-01-04T08:21:56Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +cat: Putting back Category:Scenery software, as it seems relevant and might help people find the article&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Software&lt;br /&gt;
|title             = TerraMaster&lt;br /&gt;
|logo              =&lt;br /&gt;
|image             = TerraMaster r32 - Global view.png&lt;br /&gt;
|developedby       = reeed, portree kid&lt;br /&gt;
|initialrelease    = July 18, 2011&lt;br /&gt;
|latestrelease     = January 3, 2025 (v3.02)&lt;br /&gt;
|writtenin         = Java&lt;br /&gt;
|os                =&lt;br /&gt;
|platform          = Cross-platform&lt;br /&gt;
|developmentstatus =&lt;br /&gt;
|type              = Scenery manager&lt;br /&gt;
|license           = [[GNU GPL]] v2&lt;br /&gt;
|website           = https://portree-kid.github.io/terramaster/&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
'''TerraMaster''' is a graphical [[scenery]] manager that gives you a quick overview of your scenery and makes it easier to maintain that scenery.  TerraMaster is a cross platform stand alone application written in Java that synchronises [[TerraSync]] scenery tiles you select with the [[FlightGear Scenery Database]]. You can delete scenery tiles if the TerraSync scenery directory takes too much space, and you can also search for airports.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
=== Map ===&lt;br /&gt;
When TerraMaster starts it will show a map of the Earth. The map can be zoomed and one or more 1x1 degree tiles can be selected before they are synced or deleted. All the airports within a tile can be shown, and airports can be searched for and be shown.&lt;br /&gt;
&lt;br /&gt;
=== Icons ===&lt;br /&gt;
The top row contains a status field, two groups of icon buttons, a search field and a sync progress bar.  The icons will be grayed out when they can not be used.  For more detailed directions, see the section [[#Using|Using]] below. From left to right:&lt;br /&gt;
* ''Status field''. Shows the name of the tile that was last selected.&lt;br /&gt;
&lt;br /&gt;
* ''Sync icon''. Syncs the tiles currently selected.&lt;br /&gt;
* ''Clock icon''. Syncs old tiles.&lt;br /&gt;
* ''Flight icon''. Opens a dialog to help selecting a route.&lt;br /&gt;
* ''Trash can icon''. Deletes the currently selected tiles.&lt;br /&gt;
* ''Eye icon''. Shows all airports in the first selected tile of the currently selected tiles.&lt;br /&gt;
* ''Stop button''. Currently does not work.&lt;br /&gt;
&lt;br /&gt;
* ''Empty paper''. Hides all shown airports.&lt;br /&gt;
* ''Globe icon''. Zooms out to the global view.&lt;br /&gt;
* ''Gear icon''. Opens a dialogue to select the location of the TerraSync scenery directory.&lt;br /&gt;
&lt;br /&gt;
* ''Search field''. Used to search for airports.&lt;br /&gt;
&lt;br /&gt;
* ''Sync progress bar''. Only visible during syncing.&lt;br /&gt;
&lt;br /&gt;
=== Tile border colours ===&lt;br /&gt;
The tiles are colour coded to represent the state of the tiles:&lt;br /&gt;
&lt;br /&gt;
* '''Green''' tiles have both terrain and objects.&lt;br /&gt;
* '''Amber/Yellow''' tiles has terrain but no objects. (And are possibly unsyncable - see &amp;quot;Bug&amp;quot;, below.)&lt;br /&gt;
* '''Red''' tiles are currently selected for synchronisation.&lt;br /&gt;
* '''Blue''' tiles are currently being synchronised.&lt;br /&gt;
&lt;br /&gt;
The blue tiles that are being synced will change colour to green or amber/yellow as they are synced. Tiles that get no border after they have been synced don't contain terrain or objects and are usually located in open seas.&lt;br /&gt;
&lt;br /&gt;
== Using ==&lt;br /&gt;
[[File:TerraMaster r32 - Mouse hint.png|thumb|Tile information pop-up when holding the mouse pointer over a tile.]]&lt;br /&gt;
[[File:TerraMaster r32 - Airports in selected tile.png|thumb|Showing all airports in a selected tile using the &amp;quot;eye&amp;quot; button.]]&lt;br /&gt;
[[File:TerraMaster r32 - Searching for an airport.png|thumb|Searching for an airport.]]&lt;br /&gt;
&lt;br /&gt;
=== Zooming and panning ===&lt;br /&gt;
TerraMaster with the global view.  Left-clicking will zoom you in on an area.&lt;br /&gt;
&lt;br /&gt;
'''Zoom''' using the scroll wheel.&lt;br /&gt;
&lt;br /&gt;
'''Pan''' around by dragging the map while pressing the right mouse button.&lt;br /&gt;
&lt;br /&gt;
Get '''back to global view''' by clicking the &amp;quot;globe&amp;quot; icon.&lt;br /&gt;
&lt;br /&gt;
=== Showing tile information ===&lt;br /&gt;
To get some information about a downloaded tile, hold the mouse pointer over it.&lt;br /&gt;
&lt;br /&gt;
A pop-up box will show the name of that tile, if there are terrain and objects in it and what airports it contains. This only works if a tile has been synced, either with Terrasync or TerraMaster.&lt;br /&gt;
&lt;br /&gt;
=== Syncing tiles ===&lt;br /&gt;
To '''select just one tile''' left-click it. The scenery for download is available in 1x1 degree tiles.  &lt;br /&gt;
&lt;br /&gt;
To '''select more tiles''' press the Ctrl key while left-clicking or left-dragging over the wanted tiles.  If you accidentally selected a tile to many, '''deselect tiles''' using it Ctrl+left-click.  You can also deselect several tiles using Ctrl+left-click and dragging.&lt;br /&gt;
&lt;br /&gt;
'''Start the update and/or download''' clicking the &amp;quot;sync&amp;quot; icon.  The tiles will be downloaded starting from the first tile you selected and proceed in order of distance from it.  If you are downloading tiles for a long flight you should then begin by selecting a tile close to the departure airport.&lt;br /&gt;
&lt;br /&gt;
=== Syncing old tiles ===&lt;br /&gt;
&lt;br /&gt;
'''Start the update and/or download''' clicking the 2nd &amp;quot;sync&amp;quot; icon. All previously downloaded tiles that are older than the age given in the settings, will be downloaded.&lt;br /&gt;
&lt;br /&gt;
=== Deleting tiles ===&lt;br /&gt;
You can delete files from the TerraSync scenery by selecting the tiles you want to delete and click the &amp;quot;trash can&amp;quot; icon.&lt;br /&gt;
&lt;br /&gt;
=== Showing all airports in a tile ===&lt;br /&gt;
To show all airports in a tile, select it and click on the &amp;quot;eye&amp;quot; icon.  To hide the airports again, click the &amp;quot;empty paper&amp;quot; icon.&lt;br /&gt;
&lt;br /&gt;
=== Searching for an airport ===&lt;br /&gt;
You can search for airports using the search field and pressing enter.  The airports location an ICAO code will be shown in white on the map.  If you don't see it, zoom out.  ICAO airport codes works best, but sometimes the airport's name also work.  To hide the airports again, click the &amp;quot;empty paper&amp;quot; icon.&lt;br /&gt;
&lt;br /&gt;
Be warned that the search function is rather slow as it uses a web query to mpmap.flightgear.org and that it also doesn't tell if there was no airports found.&lt;br /&gt;
&lt;br /&gt;
== Video tutorial ==&lt;br /&gt;
Here is a short video tutorial:&lt;br /&gt;
{{#ev:youtube|YxWV4wk_dHw}}&lt;br /&gt;
&lt;br /&gt;
== Tips ==&lt;br /&gt;
* '''Before [[multiplayer]] events''' it might be an idea to sync the related tiles, even more so if you know that someone has crated new buildings etc. and got them into the [[FlightGear Scenery Database|scenery database]].&lt;br /&gt;
* If you have very little hard drive space left you can delete tiles you rarely or never fly within.&lt;br /&gt;
* Starting TerraMaster in a console window will let you see the sync process, which can be useful if the sync stalls for unknown reasons.&lt;br /&gt;
* If you already have a synchronisation running and want to add more tiles to be synced, just mark more tiles as above with Ctrl + leftclick and drag to mark more tiles and press the sync button again.&lt;br /&gt;
&lt;br /&gt;
== Installing ==&lt;br /&gt;
Before installing, make sure you have Java 6 or higher, as it is needed. NB: On Ubuntu 22.04 (Jammy) OpenJDK 8+ works.&lt;br /&gt;
&lt;br /&gt;
# Download the newest version of TerraMaster from the [https://github.com/Portree-Kid/terramaster/releases releases page].&lt;br /&gt;
# Create a shortcut or script that runs &amp;lt;code&amp;gt;java -jar terramaster.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
# When running TerraMaster the first time, click the &amp;quot;gear&amp;quot; icon and select the folder where your TerraSync scenery is located.  On some machines you might have to manually edit the property file to have it pointing to the TerraSync directory.&lt;br /&gt;
# Done!&lt;br /&gt;
&lt;br /&gt;
== External link ==&lt;br /&gt;
* {{forum link|title=TerraMaster: a new scenery manager|t=12050}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Scenery software]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140266</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140266"/>
		<updated>2024-08-20T09:31:31Z</updated>

		<summary type="html">&lt;p&gt;Johan G: +- &amp;quot;with some issues&amp;quot; → &amp;quot;with some known issues&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print:&lt;br /&gt;
* Simulated UTC time&lt;br /&gt;
* Real local time&lt;br /&gt;
* Real UTC time (with some known issues)&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some properties exposed that control the behavior of the add-on. These can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file, or through properties in &amp;lt;code&amp;gt;/addons/by-id/org.flightgear.addons.log-time-stamper/config/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Property !! Type !! Default !! Use&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-sim-utc-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Simulated UTC time-stamps will be printed if true.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-real-local-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Real local time-stamps will be printed if true.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-real-utc-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Real UTC time-stamps will be printed if true (with some known issues).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;time-stamp-interval-sec&amp;lt;/code&amp;gt;&lt;br /&gt;
| int&lt;br /&gt;
| 60&lt;br /&gt;
| Interval in seconds between time-stamps.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Time]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140265</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140265"/>
		<updated>2024-08-20T09:28:41Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Exposed properties */ Listing the properties with some more data in a table instead of as bullet points&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print:&lt;br /&gt;
* Simulated UTC time&lt;br /&gt;
* Real local time&lt;br /&gt;
* Real UTC time (with some issues)&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some properties exposed that control the behavior of the add-on. These can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file, or through properties in &amp;lt;code&amp;gt;/addons/by-id/org.flightgear.addons.log-time-stamper/config/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Property !! Type !! Default !! Use&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-sim-utc-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Simulated UTC time-stamps will be printed if true.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-real-local-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Real local time-stamps will be printed if true.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;print-real-utc-time&amp;lt;/code&amp;gt;&lt;br /&gt;
| bool&lt;br /&gt;
| True&lt;br /&gt;
| Real UTC time-stamps will be printed if true (though with some issues).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;time-stamp-interval-sec&amp;lt;/code&amp;gt;&lt;br /&gt;
| int&lt;br /&gt;
| 60&lt;br /&gt;
| Interval in seconds between time-stamps.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Time]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140264</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140264"/>
		<updated>2024-08-20T09:03:52Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Exposed properties */ Spelling&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print:&lt;br /&gt;
* Simulated UTC time&lt;br /&gt;
* Real local time&lt;br /&gt;
* Real UTC time (with some issues)&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some exposed properties that can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file or through the property tree.&lt;br /&gt;
&lt;br /&gt;
These properties controls:&lt;br /&gt;
* The time-stamp interval&lt;br /&gt;
* If you want either of these time-stamps&lt;br /&gt;
** Simulated UTC time&lt;br /&gt;
** Real local time&lt;br /&gt;
** Real UTC time (though with some issues)&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Time]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140263</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140263"/>
		<updated>2024-08-20T08:59:51Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Features */ Listing time stamps as bullet points instead&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print:&lt;br /&gt;
* Simulated UTC time&lt;br /&gt;
* Real local time&lt;br /&gt;
* Real UTC time (with some issues)&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some exposed properties than can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file or through the property tree.&lt;br /&gt;
&lt;br /&gt;
These properties controls:&lt;br /&gt;
* The time-stamp interval&lt;br /&gt;
* If you want either of these time-stamps&lt;br /&gt;
** Simulated UTC time&lt;br /&gt;
** Real local time&lt;br /&gt;
** Real UTC time (though with some issues)&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Time]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Time&amp;diff=140262</id>
		<title>Time</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Time&amp;diff=140262"/>
		<updated>2024-08-20T08:51:03Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Wiki articles */ + Log time-stamper add-on&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:PropertyBrowser-showing-sim-time.png|right|thumb|The [[property browser]] showing the &amp;lt;code&amp;gt;/sim/time/&amp;lt;/code&amp;gt; subtree]]&lt;br /&gt;
[[File:Pui-time-dialog.png|right|thumb|The ''Time Settings'' dialog]]&lt;br /&gt;
[[File:Time.png|thumb|New Time dialog in FlightGear]]&lt;br /&gt;
[[File:Time-dialog-via-pui2canvas.png|thumb|approximation of the FlightGear [[Time in FlightGear|Time]] dialog via [[Howto:Processing legacy PUI dialogs using Canvas|pui2canvas]] parser]]&lt;br /&gt;
&lt;br /&gt;
The '''time in FlightGear''' and the properties affecting it are represented internally as properties in the &amp;lt;code&amp;gt;/sim/time/&amp;lt;/code&amp;gt; property subtree.  The time can be manipulated using ''simulation rate''/''speed-up'', to increase or decrease the speed of the simulation, and ''time warp''/''warp delta'', to speed up or reverse local and UTC time.&lt;br /&gt;
&lt;br /&gt;
The terminology used in FlightGear is a bit inconsistent even within the GUI.  For the one used in this article, see [[#Terms used in this article]].&lt;br /&gt;
&lt;br /&gt;
== Key bindings ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Keys !! Binding !! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| {{key press|A}} || {{key press|Shift|A}} || Increase / decrease simulation rate&lt;br /&gt;
| Affects &amp;lt;code&amp;gt;/sim/speed-up&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| {{key press|W}} || {{key press|Shift|W}} || Increase / decrease the warp offset&lt;br /&gt;
| Affects &amp;lt;code&amp;gt;/sim/time/warp&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| {{key press|T}} || {{key press|Shift|T}} || Increase / decrease time warp in steps&lt;br /&gt;
| Affects &amp;lt;code&amp;gt;/sim/time/warp-delta&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Simulation rate vs. time warp ==&lt;br /&gt;
Simulation rate and time warp manipulates the speed of the simulation and time in FlightGear.  The simulation rate affects how much faster or slower the FDM appears to run and the time warp affects how fast local and UTC time changes.  Neither simulation rate or time warp will affect for example the airspeed of the aircraft.&lt;br /&gt;
&lt;br /&gt;
=== Simulation rate ===&lt;br /&gt;
When the simulation is sped up and slowed down, the integer factor &amp;lt;code&amp;gt;/sim/speed-up&amp;lt;/code&amp;gt; will increase and decrease.  The aircraft will appear to fly &amp;lt;code&amp;gt;/sim/speed-up&amp;lt;/code&amp;gt; times faster over the terrain.  When running FlightGear at normal simulation rate &amp;lt;code&amp;gt;/sim/speed-up&amp;lt;/code&amp;gt; is one.&lt;br /&gt;
&lt;br /&gt;
This will only affect the speed of the simulation, not the local and UTC time.&lt;br /&gt;
&lt;br /&gt;
=== FDM rate ===&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= The FDM runs at 120 Hertz and with a fixed time step. However, we play one small trick to make that happen. We take the time that has elapsed since the last frame, compute how many whole iterations of the FDM will fit in that time slice (at 1/120th of a second per iteration.) Then we invoke the FDM that many times with a time step of 1/120th of a second. Finally we save out the remainder and add that into the next time slice. This can produce a small amount of temporal jitter between the graphics and the fdm if the graphics frame rates are not a diviser of 120. In the best case scenario, you've locked your graphics frame rate to 60 hz so the FDM runs exactly 2 iterations every time it is invoked and there is no temporal jitter at all, ever. One thing to keep in mind is that handing a different size time slice to the FDM every frame (and sometimes that time slice could be 1 second or more?) can lead to instabilities in the math. So our approach is intended to avoid that potential problem. As far as the FDM is concerned, it *is* running asyncronously, at a fixed time step. But, we are playing a little trick on the FDM (it doesn't care) in order to handle the unfortunate possibility of non-fixed and highly variable frame rates on PC hardware running consumer grade operating systems.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/15645370/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] FlightGear/Plib periodic stutter notes&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Curtis Olson&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Oct 24th, 2007&lt;br /&gt;
  | added   = Oct 24th, 2007&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= the autopilot in FG should be running at a fixed framrate (/sim/model-h) these days. That is, fixed in simulated time, which is all that should matter. (As an aside our time management is still pretty messy and lots of things that ought to use simulated time seems to be using real time instead - try increasing the simulation rate and see how many virtual things that do not speed up...)&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/28195667/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] autopilot frame rate&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Anders Gidenstam&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Oct 7th, 2011&lt;br /&gt;
  | added   = Oct 7th, 2011&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Time warp ===&lt;br /&gt;
When time of day is sped up or reversed using time warp, &amp;lt;code&amp;gt;/sim/time/warp-delta&amp;lt;/code&amp;gt; seconds are added or subtracted to &amp;lt;code&amp;gt;/sim/time/warp&amp;lt;/code&amp;gt; each second.  In essence, the former is not a factor and the latter is an offset.  When running FlightGear at normal time warp &amp;lt;code&amp;gt;/sim/time/warp-delta&amp;lt;/code&amp;gt; is zero, and when running in real time, &amp;lt;code&amp;gt;/sim/time/warp&amp;lt;/code&amp;gt; is also zero.&lt;br /&gt;
&lt;br /&gt;
This will only affect the local and UTC time, not the speed of the simulation.  The aircraft will fly just as fast over the terrain, but the sun and clock hands will speed up or reverse.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= /sim/speed-up is now included in dt, unless you use ‘real dt’ - which is only for user-interface and other things that should work while paused? (I have a long-standing task to make view direction animation use real-dt so you can look around at normal rate while paused or using sim/speed-up)&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34883671/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Time speedup&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 25th, 2016&lt;br /&gt;
  | added   = Feb 25th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= Note the /sim/time/warp value may not be completely accurate, but should be close enough for what it’s used for (time of day), but to cumulative rounding as that is updated each frame. But the actual dt values should be completely accurate. &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34883671/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Time speedup&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 25th, 2016&lt;br /&gt;
  | added   = Feb 25th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Warp vs. speedup ===&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= I find two separate concepts exposed in two different properties: sim/time/warp &amp;amp; sim/time/speed-up. The difference is also made in the user interface, where you can set time warping and speed-up independently. Apparently, speed-up affects the aircraft only (physics &amp;amp; instrumentation), and warp affects the environment only (ephemeris, for example). This effectively establishes two independent timelines inside and outside the aircraft, so there is no single value for &amp;quot;elapsed simulated time&amp;quot;, i.e. it depends whether we are talking in or out the plane! &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/31578793/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] Time warping&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Anton Gomez Alvedro&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Oct 30th, 2013&lt;br /&gt;
  | added   = Oct 30th, 2013&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= speed-up is primarily related to the rate the FDM sees; instruments and autopilot also see the increase since I moved them to run in lock-step with the FDM (to avoid AP instability when using speed-ups which otherwise break the PID parameters). Speed-up is used to speed-up or slow-down how fast the simulation engine is running the local aircraft, in effect, but anything written in Nasal would need to handle it explicitly. We probably *could* factor sim/speed-up into simDt, but this would need to be done with great care since there's probably code using it which does not expect to see a speed-up. None of the above cares about 'wall clock times' or 'dates', only about dt intervals. - warp is about how fast we're updating our simulate 'time+date of the world'. Hence it affects things that refer to time of day such as ephemeris. The goal, I guess, was interactive adjustment of the calendar time to fly in the evening/morning, and indeed that's what the dialog suggests. Warp is really a warp-rate and a warp-offset - the offset is exactly the delta from system clock time to simulated world time, and we adjust it by the warp-rate each frame/update.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/31578949/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Time warping&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Oct 30th, 2013&lt;br /&gt;
  | added   = Oct 30th, 2013&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Framerate throttling ===&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= There is also a frame rate throttling option, but it's pretty buried /sim/frame-rate-throttle-h Also consider setting your &amp;quot;sync to vblank&amp;quot; option in your video hardware. That can help limit FlightGear to run at your display's refresh rate.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/28197283/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] autopilot frame rate&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Curtis Olson&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Oct 7th, 2011&lt;br /&gt;
  | added   = Oct 7th, 2011&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= If you sync to the vblank signal in FlightGear (and have enough cpu/graphics hp) you can run at a very solid 60h (or whatever rate your display refreshes at.) If you don't quite have that amount of hp consistently for all situations, there is a throttle-hz property you can set to force a slower update rate (maybe 30 or 20 fps ... ideally you want an even divider into your display update rate.) If consistent frame rates are your goal, there are ways to achieve that. However, because of the variability of systems and personal preferences, we don't turn a lot of this on by default.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/23257288/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Multithreading support&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Curtis Olson&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Aug 5th, 2009&lt;br /&gt;
  | added   = Aug 5th, 2009&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== News ===&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= First the good news : I have managed to generalise the existing ‘speed up’ function (accessed via a/A keys) to work for many more parts of the simulator than before. (This is the feature which allows time to run faster or slower than normal) This should make flying with speed up on autopilot more reliable, and solves my specific use-case of the testing the AI traffic code easier, since I can speed up how fast the AI aircraft move and update. This also allowed various special cases in the instruments and Nasal to be removed. For those who care about details, the ‘dt’ value passed to subsystems is now scaled by the speed-up factor. This is consistent with the existing behaviour when paused (dt = 0, no sim time is passing). If you are working code which needs the ‘real’ dt, typically because you want to animate something while the sim is paused, the ‘real dt’ is available as it always way. However, any such code in this category would already have been incorrect when froen / paused, so I’m not too worried about such cases. Now the question: as well as sim/speed-up, we have the /sim/time/warp property which defines the offset in seconds from system time to simulator time. This is how we implement the various time of day options, and with Richard’s change, adjusting the date as well.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34745068/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] Speed-up vs time-warp&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 6th, 2016&lt;br /&gt;
  | added   = Jan 6th, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Precision ==&lt;br /&gt;
As of October 2015 there is only one floating point property, &amp;lt;code&amp;gt;/sim/time/elapsed-sec&amp;lt;/code&amp;gt;, which tells how long FlightGear has been running. This property is not affected by simulation rate or time warp.  Most of the other properties are integers, thus only able to show full seconds.&lt;br /&gt;
&lt;br /&gt;
=== Getting sub-second local and UTC time ===&lt;br /&gt;
{{note|Yet to be tested}}&lt;br /&gt;
&lt;br /&gt;
Sometimes you could want sub-second precision time that is affected by time warp.  This could for example be if you want a cockpit clock to have a sweeping second hand or if you are logging test data in smaller intervals than one second while wanting those times to correlate to other data.&lt;br /&gt;
&lt;br /&gt;
As clock hands often only use elapsed seconds to animate the hands, the following example will produce two such properties.&lt;br /&gt;
&lt;br /&gt;
One way to get sub-second precision UTC and local time could be by declaring two floating point properties and a property rule in the [[aircraft-set.xml]] file for example like below.  Using property rules will update the values at frame rate, while using autopilot functions would update them at FDM rate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
 &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
 &amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
  &amp;lt;time&amp;gt;&lt;br /&gt;
   &amp;lt;!-- type=&amp;quot;double&amp;quot; will make the properties non-integer.  The initial zero values will change when FlightGear is running. --&amp;gt;&lt;br /&gt;
   &amp;lt;utc-sec type=&amp;quot;double&amp;quot;&amp;gt;0&amp;lt;/local-sec&amp;gt;&lt;br /&gt;
   &amp;lt;local-sec type=&amp;quot;double&amp;quot;&amp;gt;0&amp;lt;/local-sec&amp;gt;&lt;br /&gt;
  &amp;lt;time&amp;gt;&lt;br /&gt;
  &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
  &amp;lt;systems&amp;gt;&lt;br /&gt;
   &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
   &amp;lt;property-rule n=&amp;quot;100&amp;quot;&amp;gt;  &amp;lt;!-- &amp;quot;n&amp;quot; needs to be unique and &amp;gt;= 100 to avoid overwriting other predefined global rules (in particular the environment ones) --&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Sub-second precision UTC and local time&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;path&amp;gt;Systems/precision-time.xml&amp;lt;/path&amp;gt;&lt;br /&gt;
   &amp;lt;/property-rule&amp;gt;&lt;br /&gt;
   &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
  &amp;lt;/systems&amp;gt;&lt;br /&gt;
 &amp;lt;/sim&amp;gt;&lt;br /&gt;
 &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add property rules/autopilot filters adding &amp;lt;code&amp;gt;/sim/time/elapsed-sec&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/sim/time/warp&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/sim/time/offset-local&amp;lt;/code&amp;gt; like below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;filter&amp;gt;&lt;br /&gt;
  &amp;lt;name type=&amp;quot;string&amp;quot;&amp;gt;Sub-second precision UTC time&amp;lt;/name&amp;gt;&lt;br /&gt;
  &amp;lt;type&amp;gt;gain&amp;lt;/type&amp;gt;&lt;br /&gt;
  &amp;lt;gain&amp;gt;1&amp;lt;/gain&amp;gt;&lt;br /&gt;
  &amp;lt;input&amp;gt;&lt;br /&gt;
   &amp;lt;expression&amp;gt;&lt;br /&gt;
    &amp;lt;sum&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/sim/time/elapsed-sec&amp;lt;/property&amp;gt;  &amp;lt;!-- The only non-integer time --&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/sim/time/warp&amp;lt;/property&amp;gt;  &amp;lt;!-- The offset between local and clock time --&amp;gt;&lt;br /&gt;
    &amp;lt;/sum&amp;gt;&lt;br /&gt;
   &amp;lt;/expression&amp;gt;&lt;br /&gt;
  &amp;lt;/input&amp;gt;&lt;br /&gt;
  &amp;lt;output&amp;gt;/sim/time/utc-sec&amp;lt;/output&amp;gt;&lt;br /&gt;
 &amp;lt;/filter&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;filter&amp;gt;&lt;br /&gt;
  &amp;lt;name type=&amp;quot;string&amp;quot;&amp;gt;Sub-second precision local time&amp;lt;/name&amp;gt;&lt;br /&gt;
  &amp;lt;type&amp;gt;gain&amp;lt;/type&amp;gt;&lt;br /&gt;
  &amp;lt;gain&amp;gt;1&amp;lt;/gain&amp;gt;&lt;br /&gt;
  &amp;lt;input&amp;gt;&lt;br /&gt;
   &amp;lt;expression&amp;gt;&lt;br /&gt;
    &amp;lt;sum&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/sim/time/utc-sec&amp;lt;/property&amp;gt;  &amp;lt;!-- Using output from the above filter --&amp;gt;&lt;br /&gt;
     &amp;lt;property&amp;gt;/sim/time/local-offset&amp;lt;/property&amp;gt;  &amp;lt;!-- The offset between UTC and local time --&amp;gt;&lt;br /&gt;
    &amp;lt;/sum&amp;gt;&lt;br /&gt;
   &amp;lt;/expression&amp;gt;&lt;br /&gt;
  &amp;lt;/input&amp;gt;&lt;br /&gt;
  &amp;lt;output&amp;gt;/sim/time/local-sec&amp;lt;/output&amp;gt;&lt;br /&gt;
 &amp;lt;/filter&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Terms used in this article ==&lt;br /&gt;
The terms used in the source code, keyboard binding descriptions and ''Time Settings'' dialog labels are a bit inconsistent.  Therefore, in this article these terms (mostly from the ''Time Settings'' dialog) will be used:&lt;br /&gt;
&lt;br /&gt;
; clock time&lt;br /&gt;
: The local system time as represented by the &amp;lt;code&amp;gt;/sim/time/real/&amp;lt;/code&amp;gt; property subtree.&lt;br /&gt;
; local time&lt;br /&gt;
: The in-sim local time as represented by &amp;lt;code&amp;gt;/sim/time/gmt&amp;lt;/code&amp;gt; + the offset represented by &amp;lt;code&amp;gt;/sim/time/local-offset&amp;lt;/code&amp;gt;.&lt;br /&gt;
; real time&lt;br /&gt;
: When running FlightGear with zero warp, so that local time is equal to clock time, and zero time warp so that local time is at the same speed as clock time.&lt;br /&gt;
; simulation rate&lt;br /&gt;
: Simulation rate speed-up factor as represented by &amp;lt;code&amp;gt;/sim/speed-up&amp;lt;/code&amp;gt;. This is not related to the FDM rate, the speed at which the flight dynamics model is run.&lt;br /&gt;
; time warp&lt;br /&gt;
: Local and UTC time change rate as represented by &amp;lt;code&amp;gt;/sim/time/warp-delta&amp;lt;/code&amp;gt;.&lt;br /&gt;
; UTC time&lt;br /&gt;
: The in-sim UTC time as represented by &amp;lt;code&amp;gt;/sim/time/gmt&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;/sim/time/utc&amp;lt;/code&amp;gt; property subtree.  In essence the time shown on many cockpit clocks.&lt;br /&gt;
; warp&lt;br /&gt;
: The offset between local time and clock time as represented by &amp;lt;code&amp;gt;/sim/time/warp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Nasal scripting ==&lt;br /&gt;
* {{func link|maketimer()}}&lt;br /&gt;
* {{func link|settimer()}}&lt;br /&gt;
* FDM coupled&lt;br /&gt;
&lt;br /&gt;
== Ongoing Developments ==&lt;br /&gt;
=== Date selector ===&lt;br /&gt;
{{Note|See also {{Merge-request|project=fgdata|id=42}}}}&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= sad that it is pretty difficult to set another date while in-simulator (via property menu), so I took the time selector and changed it a bit to have a date selector. &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=269399#p269399&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Date selector&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;D-ECHO&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Dec 20th, 2015&lt;br /&gt;
  | added   = Dec 20th, 2015&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= What do you all think about adding a date control to the Time Settings dialog in the Environment menu (and then maybe rename it to &amp;quot;Time and Date Settings&amp;quot;)? Currently the only way to set the date while in sim is via the property editor using &amp;quot;/sim/time/gmt&amp;quot;. This would as useful as having time controls to play with the amount of daylight since the date influences the position and maximal height of the sun, particularly in very northern or southern places. Personally, I think it would be great to give users the chance to set it in sim without fussing with the debug tools, just like we currently do with time. &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34735814/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] Time settings dialog&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Gilberto Agostinho&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 3rd, 2016&lt;br /&gt;
  | added   = Jan 3rd, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= I ran into a problem: the property that needs to be altered to change the year, month and day, as well as time, is &amp;quot;/sim/time/gmt&amp;quot;, and its value is a single string. Unfortunately my knowledge of XML as well as of the capabilities of our dialogs isn't very fancy (I just did some simple things for the c172p so far), and I really don't know how could I create several buttons to change the date parameters individually and then have them concatenated into a single string in the format required by the mentioned property... If we had a single property for each parameter this would be easy peasy, but I am kind of stuck right now.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34736360/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Time settings dialog&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Gilberto Agostinho&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 3rd, 2016&lt;br /&gt;
  | added   = Jan 3rd, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= I've just added some text input boxes to the dialog for year,month,day to the dialog https://sourceforge.net/u/r-harrison/fgdata/ci/3e667c0b443a06d167d63c9447b6d8442f6aca57/ Merge request created. https://sourceforge.net/p/flightgear/fgdata/merge-requests/39/ (Not quite sure why it says 0 commits on the request - maybe I was a bit quick creating the merge request as possibly it hadn't finished pushing). I can redo the merge request if needed. Also added a checkbox for the warp easing as I'm always having to do this with the property tree.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34737165/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Time settings dialog&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Richard Harrison&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 4th, 2016&lt;br /&gt;
  | added   = Jan 4th, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= I've been working on the time dialog. At first it was a quick fix just to add date entry, but it's grown from that. This is what it is currently: http://i.imgur.com/QDuyDLD.png I wasn't sure about sliders on year,month,date at first, but the sliders actually work really effectively - which surprised me. IMHO this approach works better than the dropdown calendar that we've all seen before. The day of month is limited to the maximum number of days for the currently selected month and it takes leap years into account. I prefer to run with easing turned off which is another reason I added a checkbox for easing. I rather suspect I'm not alone in this view. The time slider partially removes the need for the warping. It's also neat adjusting the date with the sliders and seeing the effect changes can be found here: https://sourceforge.net/u/r-harrison/fgdata/ci/435b81bf2464b4c64b6e69ec966406ea45fea797/tree/gui/dialogs/timeofday.xml I've limited the range for the year from 1971 to 2037 (i.e. within completely safe 32bit time_t range). &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34747721/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] Speed-up vs time-warp&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Richard Harrison&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 7th, 2016&lt;br /&gt;
  | added   = Jan 7th, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= [we] are debating his proposed changes to the time dialog: https://sourceforge.net/p/flightgear/fgdata/merge-requests/42/ I’m fine with functionality (mostly) but don’t like his choice of widgets, although as ever he is constrained by what PUI does and doesn’t offer. Can some other people, ideally with UX experience, review and comment? I don’t want to make a unilateral decision since I’m aware I’m usually at one extreme end of the ‘less is more’ Johnny Ive school of interface design :D As ever I would ask people reviewing to apply the UI learnability measure of ‘if I’d never seen this screen before or used FlightGear for more than five minutes, would the choices and controls in this dialog make sense to me? If not, what could be added / changed so they do?'&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34747748/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] Additional perspectives needed on time dialog&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Jan 7th, 2016&lt;br /&gt;
  | added   = Jan 7th, 2016&lt;br /&gt;
  | script_version = 0.23&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Autopilot configuration reference]]&lt;br /&gt;
* [[Log time-stamper add-on]]&lt;br /&gt;
* [[Property browser]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* [http://forum.flightgear.org/viewtopic.php?f=25&amp;amp;t=20756 Link Simulation Rate and Time Warp together?]&lt;br /&gt;
&lt;br /&gt;
=== Issues ===&lt;br /&gt;
* {{Ticket|421}}&lt;br /&gt;
&lt;br /&gt;
=== Source files ===&lt;br /&gt;
* {{fgdata file|keyboard.xml}}&lt;br /&gt;
* {{fgdata file|Nasal/controls.nas}}&lt;br /&gt;
* {{fgdata file|Timezone/}}&lt;br /&gt;
&lt;br /&gt;
* {{simgear file|simgear/timing/}}&lt;br /&gt;
&lt;br /&gt;
* {{flightgear file|src/Time/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140261</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140261"/>
		<updated>2024-08-20T08:50:18Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Wiki articles */ + Time&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print the simulated UTC time, real local time, and, with some issues, real UTC time.&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some exposed properties than can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file or through the property tree.&lt;br /&gt;
&lt;br /&gt;
These properties controls:&lt;br /&gt;
* The time-stamp interval&lt;br /&gt;
* If you want either of these time-stamps&lt;br /&gt;
** Simulated UTC time&lt;br /&gt;
** Real local time&lt;br /&gt;
** Real UTC time (though with some issues)&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Time]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Instant_Replay&amp;diff=140254</id>
		<title>Instant Replay</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Instant_Replay&amp;diff=140254"/>
		<updated>2024-08-19T15:39:29Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Saving and replaying saved replay tapes */ New section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:FG-instant-replay-new.png|thumb|500px|The instant replay dialog.]]&lt;br /&gt;
'''Instant Replay''' allows you to view for example your last landing from different views and perspectives afterwards.&lt;br /&gt;
&lt;br /&gt;
Additionally the instant replay feature allows saving and replaying past flights.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
FlightGear maintains an in-memory recording of the current session. To avoid running our of memory, only the last minute or so is recorded in full detail, and older data is gradually pruned.&lt;br /&gt;
&lt;br /&gt;
The in-memory recording can be saved to a file.&lt;br /&gt;
&lt;br /&gt;
=== Starting and controlling replay of normal recording ===&lt;br /&gt;
* Press '''Ctrl-r''' or select '''Instant Replay''' from the '''View''' menu to start the replay. Replay starts immediately.&lt;br /&gt;
* Press '''p''' or click '''pause''' to pause/resume the replay.&lt;br /&gt;
* Use the '''left/right''' arrow keys, or the '''&amp;amp;lt;&amp;amp;lt;''', '''&amp;amp;gt;&amp;amp;gt;''' buttons to skip. You can also use the mouse and drag the time slider.&lt;br /&gt;
* Use the '''up/down''' arrow keys, or the '''+''', '''-''' buttons to change replay speed. You can replay at slow-motion, real-time or fast-forward speed.&lt;br /&gt;
* Enable the '''Loop''' checkbox to continuously repeat the playback. When you configure a duration (in seconds) then only the last few seconds are continuously replayed.&lt;br /&gt;
&lt;br /&gt;
=== Stopping replay ===&lt;br /&gt;
* Press '''Escape''' or the '''End Replay''' button to stop replay and return to the position prior to starting replay.&lt;br /&gt;
* Alternatively, click the '''My Controls!''' button to take over control at any point. With this feature you can use the replay system to go back in time, regain control and then continue the flight from a past position. This is useful to train particular flight phases, such as flying the same approach again and again, maybe using different weather/wind conditions.&lt;br /&gt;
&lt;br /&gt;
=== Saving and loading replay tapes ===&lt;br /&gt;
Though ''Main Menu'' &amp;amp;gt; ''File'' &amp;amp;gt; ''Save Flight Recorder Tape'' and ''Load Flight Recorder Tape'' you can save and load flight recorder tapes.&lt;br /&gt;
&lt;br /&gt;
When saving the tapes you can add a description of the flight.&lt;br /&gt;
&lt;br /&gt;
== Record/replay on next ==&lt;br /&gt;
[[File:Flight-recorder-control.png|thumb|The extended Flight Recorder Control dialog on next.]]&lt;br /&gt;
There are various additions to record/replay on next:&lt;br /&gt;
&lt;br /&gt;
* Extended '''File/Flight Recorder Control''' dialogue.&lt;br /&gt;
* Periodic generation of a recovery snapshot, allowing resumption after a flightgear crash.&lt;br /&gt;
* Continuous recording system:&lt;br /&gt;
** Continuous recording to file without any loss of detail.&lt;br /&gt;
** Optionally record/replay extra information:&lt;br /&gt;
*** Multiplayer aircraft.&lt;br /&gt;
*** Main window size.&lt;br /&gt;
*** Main window position.&lt;br /&gt;
*** Main window view.&lt;br /&gt;
* Aircraft override:&lt;br /&gt;
** When loading a Continuous recording at startup, we override the aircraft and starting airport to match what is in the recording.&lt;br /&gt;
* Replay of Continuous recordings from URL:&lt;br /&gt;
** Extends the '''--load-tape command-line''' option, for example: '''--load-tape=&amp;lt;nowiki&amp;gt;http://foo.com/bar.fgtape&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
=== Details ===&lt;br /&gt;
* A recovery snapshot is actually a single-frame continuous recording, and will be called '''&amp;lt;aircraft-type&amp;gt;-recovery.fgtape'''.&lt;br /&gt;
** Load on the command line with '''--load-tape=&amp;lt;aircraft-type&amp;gt;-recovery'''.&lt;br /&gt;
** Then do '''Ctrl-R''' to see the replay dialogue.&lt;br /&gt;
** And click on '''My Controls'''.&lt;br /&gt;
** Notes:&lt;br /&gt;
*** Some aircraft do not support '''My Controls'''.&lt;br /&gt;
*** Loading a recovery file from within Flightgear via the '''Flight Recorder Control''' dialogue is usually not useful because the recovery file will have been overwritten by the current Flightgear session.&lt;br /&gt;
* Continuous recording to file uncompressed is e.g. 100MB for an hour's recording near EDDF.&lt;br /&gt;
* Support for compression of Continuous recordings was pushed to next on 2021-7-31 ({{flightgear commit|7d414886e00e}}).&lt;br /&gt;
* Replay of Continuous recordings from URL:&lt;br /&gt;
** Downloads to local file in the background while replaying.&lt;br /&gt;
** Reuses local file if present, appending to it in the background if it was a partial download.&lt;br /&gt;
** Local file is in directory specified by property '''/sim/replay/tape-directory'''.&lt;br /&gt;
&lt;br /&gt;
For details on recording file formats, see: {{flightgear file|docs-mini/README-recordings.md}}&lt;br /&gt;
&lt;br /&gt;
== FG 2.6.0 and later ==&lt;br /&gt;
{{Note|&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |the &amp;quot;my controls&amp;quot; button is currently intentionally disabled for JSBSim and for YASim helicopters. Only works with YASim aircraft so far.&lt;br /&gt;
  |{{cite web |url=http://sourceforge.net/p/flightgear/mailman/message/30084554/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder / replay system&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;ThorstenB&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2012-11-11&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
[[File:FG-instant-replay-new.png|thumb|500px|The instant replay dialog.]]&lt;br /&gt;
FG 2.6.0 introduced a new replay system which can be adapted to individual aircraft and provides better recordings/playbacks. It also introduces a new GUI as well as a dedicated file format for serializing flights to disk: [[Fgtape]].&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |the flight recorder tapes are only 1 or 2 hours max... at one time i wanted to do one for a cross-country flight but when i saved it i only got the last hour or two&lt;br /&gt;
  |{{cite web |url=https://sourceforge.net/p/flightgear/mailman/message/34868768/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;[Flightgear-devel] memory leaks - long term testing - pre-2016.2.0&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Unknown&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2016-02-20&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== FG 2.4.0 and earlier ==&lt;br /&gt;
[[File:FG-instant-replay.gif|thumb|220px|The instant replay dialog for FG2.4.0 and earlier.]]&lt;br /&gt;
For FG 2.4.0 and earlier, a simple dialog box is presented with a couple of options (duration of replay and view type). &lt;br /&gt;
* A value of 0 for duration will replay the entire flight. The default value is 90 seconds (similar to MSFS). If you want something different, just type the number of seconds desired in the text box. &lt;br /&gt;
* The view option is a drop-down box with three options corresponding to Cockpit, Chase and Tower views. No matter what this is initially set to, the view type can be changed as normal with v/V and ctrl-v to cycle forwards or backwards through all the available views or return to default Cockpit view.&lt;br /&gt;
&lt;br /&gt;
To return to normal flight, press 'p' twice (pause/unpause). This returns you to the point where you selected Instant Replay. You cannot back up 30 seconds or so and try that landing again. Also, it is currently not possible to save the replay buffer to a file to load and replay a flight later on.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' only data directly related to your aircraft is saved and played back, AI objects, multiplayer aircraft or any random features will not be replayed.&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |The replay system uses three buffer levels: short term memory records 60 &amp;lt;br/&amp;gt;&lt;br /&gt;
seconds at full frame rate, mid term buffer records another 10 minutes &amp;lt;br/&amp;gt;&lt;br /&gt;
at 2fps, and the long term buffer holds 1 hour at 1/5fps. As I stated &amp;lt;br/&amp;gt;&lt;br /&gt;
earlier, I'm not changing the buffering scheme itself. However, the &amp;lt;br/&amp;gt;&lt;br /&gt;
buffer durations and rates are exposed by properties now. So, if you had &amp;lt;br/&amp;gt;&lt;br /&gt;
enough memory, you could increase the buffer sizes or change their rates.&lt;br /&gt;
  |{{cite web |url=http://sourceforge.net/p/flightgear/mailman/message/28045468/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder / replay system&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;ThorstenB&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2011-09-05&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= You should be able to extend the recording limit by changing the /sim/reply/duration property (e.g. by clicking on View -&amp;gt; Instant Replay and changing the duration in the textbox next to the &amp;quot;Loop&amp;quot; checkbox). &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34868828/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Alessandro Menti&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 20th, 2016&lt;br /&gt;
  | added   = Feb 20th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Feature requests ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= If the F/R data was written to disk immediately, so it doesn't have to take up valueable RAM, would make it possible to record nearly indefinitely (depending on disk space). And delete this data like a tmp-file on shutdown, if it isn't stored somewhere else...&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34868775/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;chris&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 20th, 2016&lt;br /&gt;
  | added   = Feb 20th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= This should be quite doable, the FlightRecorder code is quite self-contained. Also it uses thining of the history buckets so actually I think for multi-hour data, maybe it just needs a coarser (maybe one sample per 5 seconds?) bucket of larger sie. If you want to look at the code I’m happy provide guidance on enhancing it.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34869713/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[Something similar to this is on next - see above.]&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Howto:Record, analyze and replay multiplayer flights with network tools]]&lt;br /&gt;
* [[Redesigning the Replay System]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|p=373973|title=Re: How to start an A320 on approach with engines running?}} - Starting FlightGear with the &amp;lt;code&amp;gt;--load-tape&amp;lt;/code&amp;gt; option, for example for approach training.&lt;br /&gt;
&lt;br /&gt;
=== Readme file ===&lt;br /&gt;
* {{flightgear source |path=docs-mini/README-recordings.md}} (Description of Normal and Continuous recordings, up to date as of 2021-10-10.)&lt;br /&gt;
* {{readme file|flightrecorder}} (Detailed description of Normal recordings, last modified 2013-06-20.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{fgdata source|path=Aircraft/Generic/flightrecorder/}} - Generic flight recorder configuration files.&lt;br /&gt;
&lt;br /&gt;
* {{flightgear source|path=src/Aircraft/replay.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Aircraft/replay.cxx}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear feature]]&lt;br /&gt;
[[Category:Menubar]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Instant_Replay&amp;diff=140253</id>
		<title>Instant Replay</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Instant_Replay&amp;diff=140253"/>
		<updated>2024-08-19T15:34:21Z</updated>

		<summary type="html">&lt;p&gt;Johan G: Some well needed cleanup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:FG-instant-replay-new.png|thumb|500px|The instant replay dialog.]]&lt;br /&gt;
'''Instant Replay''' allows you to view for example your last landing from different views and perspectives afterwards.&lt;br /&gt;
&lt;br /&gt;
Additionally the instant replay feature allows saving and replaying past flights.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
FlightGear maintains an in-memory recording of the current session. To avoid running our of memory, only the last minute or so is recorded in full detail, and older data is gradually pruned.&lt;br /&gt;
&lt;br /&gt;
The in-memory recording can be saved to a file.&lt;br /&gt;
&lt;br /&gt;
=== Starting and controlling replay of normal recording ===&lt;br /&gt;
* Press '''Ctrl-r''' or select '''Instant Replay''' from the '''View''' menu to start the replay. Replay starts immediately.&lt;br /&gt;
* Press '''p''' or click '''pause''' to pause/resume the replay.&lt;br /&gt;
* Use the '''left/right''' arrow keys, or the '''&amp;amp;lt;&amp;amp;lt;''', '''&amp;amp;gt;&amp;amp;gt;''' buttons to skip. You can also use the mouse and drag the time slider.&lt;br /&gt;
* Use the '''up/down''' arrow keys, or the '''+''', '''-''' buttons to change replay speed. You can replay at slow-motion, real-time or fast-forward speed.&lt;br /&gt;
* Enable the '''Loop''' checkbox to continuously repeat the playback. When you configure a duration (in seconds) then only the last few seconds are continuously replayed.&lt;br /&gt;
&lt;br /&gt;
=== Stopping replay ===&lt;br /&gt;
* Press '''Escape''' or the '''End Replay''' button to stop replay and return to the position prior to starting replay.&lt;br /&gt;
* Alternatively, click the '''My Controls!''' button to take over control at any point. With this feature you can use the replay system to go back in time, regain control and then continue the flight from a past position. This is useful to train particular flight phases, such as flying the same approach again and again, maybe using different weather/wind conditions.&lt;br /&gt;
&lt;br /&gt;
== Record/replay on next ==&lt;br /&gt;
[[File:Flight-recorder-control.png|thumb|The extended Flight Recorder Control dialog on next.]]&lt;br /&gt;
There are various additions to record/replay on next:&lt;br /&gt;
&lt;br /&gt;
* Extended '''File/Flight Recorder Control''' dialogue.&lt;br /&gt;
* Periodic generation of a recovery snapshot, allowing resumption after a flightgear crash.&lt;br /&gt;
* Continuous recording system:&lt;br /&gt;
** Continuous recording to file without any loss of detail.&lt;br /&gt;
** Optionally record/replay extra information:&lt;br /&gt;
*** Multiplayer aircraft.&lt;br /&gt;
*** Main window size.&lt;br /&gt;
*** Main window position.&lt;br /&gt;
*** Main window view.&lt;br /&gt;
* Aircraft override:&lt;br /&gt;
** When loading a Continuous recording at startup, we override the aircraft and starting airport to match what is in the recording.&lt;br /&gt;
* Replay of Continuous recordings from URL:&lt;br /&gt;
** Extends the '''--load-tape command-line''' option, for example: '''--load-tape=&amp;lt;nowiki&amp;gt;http://foo.com/bar.fgtape&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
=== Details ===&lt;br /&gt;
* A recovery snapshot is actually a single-frame continuous recording, and will be called '''&amp;lt;aircraft-type&amp;gt;-recovery.fgtape'''.&lt;br /&gt;
** Load on the command line with '''--load-tape=&amp;lt;aircraft-type&amp;gt;-recovery'''.&lt;br /&gt;
** Then do '''Ctrl-R''' to see the replay dialogue.&lt;br /&gt;
** And click on '''My Controls'''.&lt;br /&gt;
** Notes:&lt;br /&gt;
*** Some aircraft do not support '''My Controls'''.&lt;br /&gt;
*** Loading a recovery file from within Flightgear via the '''Flight Recorder Control''' dialogue is usually not useful because the recovery file will have been overwritten by the current Flightgear session.&lt;br /&gt;
* Continuous recording to file uncompressed is e.g. 100MB for an hour's recording near EDDF.&lt;br /&gt;
* Support for compression of Continuous recordings was pushed to next on 2021-7-31 ({{flightgear commit|7d414886e00e}}).&lt;br /&gt;
* Replay of Continuous recordings from URL:&lt;br /&gt;
** Downloads to local file in the background while replaying.&lt;br /&gt;
** Reuses local file if present, appending to it in the background if it was a partial download.&lt;br /&gt;
** Local file is in directory specified by property '''/sim/replay/tape-directory'''.&lt;br /&gt;
&lt;br /&gt;
For details on recording file formats, see: {{flightgear file|docs-mini/README-recordings.md}}&lt;br /&gt;
&lt;br /&gt;
== FG 2.6.0 and later ==&lt;br /&gt;
{{Note|&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |the &amp;quot;my controls&amp;quot; button is currently intentionally disabled for JSBSim and for YASim helicopters. Only works with YASim aircraft so far.&lt;br /&gt;
  |{{cite web |url=http://sourceforge.net/p/flightgear/mailman/message/30084554/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder / replay system&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;ThorstenB&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2012-11-11&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
[[File:FG-instant-replay-new.png|thumb|500px|The instant replay dialog.]]&lt;br /&gt;
FG 2.6.0 introduced a new replay system which can be adapted to individual aircraft and provides better recordings/playbacks. It also introduces a new GUI as well as a dedicated file format for serializing flights to disk: [[Fgtape]].&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |the flight recorder tapes are only 1 or 2 hours max... at one time i wanted to do one for a cross-country flight but when i saved it i only got the last hour or two&lt;br /&gt;
  |{{cite web |url=https://sourceforge.net/p/flightgear/mailman/message/34868768/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;[Flightgear-devel] memory leaks - long term testing - pre-2016.2.0&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Unknown&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2016-02-20&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== FG 2.4.0 and earlier ==&lt;br /&gt;
[[File:FG-instant-replay.gif|thumb|220px|The instant replay dialog for FG2.4.0 and earlier.]]&lt;br /&gt;
For FG 2.4.0 and earlier, a simple dialog box is presented with a couple of options (duration of replay and view type). &lt;br /&gt;
* A value of 0 for duration will replay the entire flight. The default value is 90 seconds (similar to MSFS). If you want something different, just type the number of seconds desired in the text box. &lt;br /&gt;
* The view option is a drop-down box with three options corresponding to Cockpit, Chase and Tower views. No matter what this is initially set to, the view type can be changed as normal with v/V and ctrl-v to cycle forwards or backwards through all the available views or return to default Cockpit view.&lt;br /&gt;
&lt;br /&gt;
To return to normal flight, press 'p' twice (pause/unpause). This returns you to the point where you selected Instant Replay. You cannot back up 30 seconds or so and try that landing again. Also, it is currently not possible to save the replay buffer to a file to load and replay a flight later on.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' only data directly related to your aircraft is saved and played back, AI objects, multiplayer aircraft or any random features will not be replayed.&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |The replay system uses three buffer levels: short term memory records 60 &amp;lt;br/&amp;gt;&lt;br /&gt;
seconds at full frame rate, mid term buffer records another 10 minutes &amp;lt;br/&amp;gt;&lt;br /&gt;
at 2fps, and the long term buffer holds 1 hour at 1/5fps. As I stated &amp;lt;br/&amp;gt;&lt;br /&gt;
earlier, I'm not changing the buffering scheme itself. However, the &amp;lt;br/&amp;gt;&lt;br /&gt;
buffer durations and rates are exposed by properties now. So, if you had &amp;lt;br/&amp;gt;&lt;br /&gt;
enough memory, you could increase the buffer sizes or change their rates.&lt;br /&gt;
  |{{cite web |url=http://sourceforge.net/p/flightgear/mailman/message/28045468/&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder / replay system&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;ThorstenB&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;2011-09-05&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= You should be able to extend the recording limit by changing the /sim/reply/duration property (e.g. by clicking on View -&amp;gt; Instant Replay and changing the duration in the textbox next to the &amp;quot;Loop&amp;quot; checkbox). &lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34868828/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Alessandro Menti&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 20th, 2016&lt;br /&gt;
  | added   = Feb 20th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Feature requests ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= If the F/R data was written to disk immediately, so it doesn't have to take up valueable RAM, would make it possible to record nearly indefinitely (depending on disk space). And delete this data like a tmp-file on shutdown, if it isn't stored somewhere else...&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34868775/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;[Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;chris&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 20th, 2016&lt;br /&gt;
  | added   = Feb 20th, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= This should be quite doable, the FlightRecorder code is quite self-contained. Also it uses thining of the history buckets so actually I think for multi-hour data, maybe it just needs a coarser (maybe one sample per 5 seconds?) bucket of larger sie. If you want to look at the code I’m happy provide guidance on enhancing it.&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://sourceforge.net/p/flightgear/mailman/message/34869713/&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: [Flightgear-devel] flight recorder time limit&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;James Turner&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[Something similar to this is on next - see above.]&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Howto:Record, analyze and replay multiplayer flights with network tools]]&lt;br /&gt;
* [[Redesigning the Replay System]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|p=373973|title=Re: How to start an A320 on approach with engines running?}} - Starting FlightGear with the &amp;lt;code&amp;gt;--load-tape&amp;lt;/code&amp;gt; option, for example for approach training.&lt;br /&gt;
&lt;br /&gt;
=== Readme file ===&lt;br /&gt;
* {{flightgear source |path=docs-mini/README-recordings.md}} (Description of Normal and Continuous recordings, up to date as of 2021-10-10.)&lt;br /&gt;
* {{readme file|flightrecorder}} (Detailed description of Normal recordings, last modified 2013-06-20.)&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{fgdata source|path=Aircraft/Generic/flightrecorder/}} - Generic flight recorder configuration files.&lt;br /&gt;
&lt;br /&gt;
* {{flightgear source|path=src/Aircraft/replay.hxx}}&lt;br /&gt;
* {{flightgear source|path=src/Aircraft/replay.cxx}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear feature]]&lt;br /&gt;
[[Category:Menubar]]&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140243</id>
		<title>Log time-stamper add-on</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Log_time-stamper_add-on&amp;diff=140243"/>
		<updated>2024-08-12T09:04:32Z</updated>

		<summary type="html">&lt;p&gt;Johan G: /* Known issues */ The Time Setter add-on by Colin must be loaded before this add-on for the status of it to be printed.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{infobox addon&lt;br /&gt;
| name         = Log time-stamper add-on&lt;br /&gt;
| started      = 6 August 2024&lt;br /&gt;
| desc         = &lt;br /&gt;
| contributors = {{usr|Johan G}}&lt;br /&gt;
| status       = Under development&lt;br /&gt;
| coderepo     = https://gitlab.com/JohanG/log-time-stamper&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The '''log time-stamper add-on''' will print time-stamps to the console and log, which can be useful when comparing timings of events with other resources.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
FlightGear v. 2020.3.18 prints times-stamps each on line of the console output and log, but only with elapsed time of the FlightGear session.&lt;br /&gt;
&lt;br /&gt;
This add-on prints timestamps to the console at configurable intervals.  As of early August 2024 it can print the simulated UTC time, real local time, and, with some issues, real UTC time.&lt;br /&gt;
&lt;br /&gt;
Additionally the add-on prints changes in&lt;br /&gt;
* Simulation rate, how fast the simulation is running.&lt;br /&gt;
* Time warp, if the time of day is running at at a different speed than the simulation rate.&lt;br /&gt;
* If FlightGear is paused or unpaused.&lt;br /&gt;
* If the Time Setter add-on by Colin&amp;lt;ref&amp;gt;See https://gitlab.com/colingeniet/flightgear-time-offset for his add-on.&amp;lt;/ref&amp;gt; is enabled or disabled.  It synchronizes the simulated time with real time, optionally with a number of hours offset.&lt;br /&gt;
&lt;br /&gt;
Timestamps are printed in ISO 8601 / RFC 3339 format, in essence &amp;lt;code&amp;gt;YYYY-MM-DD&amp;quot;T&amp;quot;HH:MM:SS(&amp;quot;Z&amp;quot;|(&amp;quot;+&amp;quot;|&amp;quot;-&amp;quot;)hh:mm:ss)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Exposed properties ==&lt;br /&gt;
There are some exposed properties than can be configured through the &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; file or through the property tree.&lt;br /&gt;
&lt;br /&gt;
These properties controls:&lt;br /&gt;
* The time-stamp interval&lt;br /&gt;
* If you want either of these time-stamps&lt;br /&gt;
** Simulated UTC time&lt;br /&gt;
** Real local time&lt;br /&gt;
** Real UTC time (though with some issues)&lt;br /&gt;
&lt;br /&gt;
== Known issues ==&lt;br /&gt;
=== Real UTC time time-stamps ===&lt;br /&gt;
As of early August 2024 the real UTC time time-stamps ignores day rollovers, in essence if the offset and time makes it so that the UTC time would be on another day than the local real time day.&lt;br /&gt;
&lt;br /&gt;
This means that days, months and years can be off by one.&lt;br /&gt;
&lt;br /&gt;
=== The Time Setter add-on ===&lt;br /&gt;
The Time Setter add-on must be loaded before the time-stamper add-on for the time-stamper add-on to be able to print status changes of the Time Setter add-on.&lt;br /&gt;
&lt;br /&gt;
This is due that to register a listener to the the relevant property, &amp;lt;code&amp;gt;/sim/time/time-setter/enable&amp;lt;/code&amp;gt;, that property must exist. That property get initialized when the Time Setter add-on is loaded.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Forum topic ===&lt;br /&gt;
* {{forum link |title=Log time-stamper add-on |t=42533}}&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
* {{gitlab url |user=JohanG |repo=log-time-stamper}}&lt;/div&gt;</summary>
		<author><name>Johan G</name></author>
	</entry>
</feed>