RPN Scripting
Contents |
|
Related Links |
Overview
In addition to the newer LUA Scripting, Prepar3D supports Reverse Polish Notation (RPN) scripting. The evaluation of RPN scripts is done using a stack and reverse polish notation. For example, if you want a gauge to show altitude in thousands of feet, divide the value of INDICATED ALTITUDE by 1000. In RPN this would be:
To execute an arithmetic operation, put both operands first in the script and then include the symbol of operation (the divide symbol in this case). This is how some programmable calculators performed arithmetic operations in the past.
This convention works the following way: When the script parser comes across a value, it pushes it onto the top of the stack. When the script parser comes across an operator, it pops from the stack the number of operands that the operator works on (usually one or two values).
Whatever value is left on top of the stack at the end of the execution is the result of the calculated expression. The Examples section provides a list of simple and more complex script examples.
The Variable Overview article describes all of the systems that support variable access.
Operators
The operators that are available in RPN are as follows:
Operator | Operation | Arguments | Example | Result |
---|---|---|---|---|
Common Operator |
|
|||
+ | addition | 2 | 3 5 + | 8 |
- | subtraction. If the stack contains A B -, then the calculation is A - B. | 2 | (L:Value) 90 - | The local value minus 90. |
/ | division. If the stack contains A B /, then the calculation is A / B. | 2 | 5 2 / | 2.5 |
* | multiplication | 2 | pi 2 * | 2 pi |
% | taking modulo | 2 | 5 3 % | 2 |
++ | increment | 1 | 4 ++ | 5 |
-- | decrement | 1 | 4 -- | 3 |
/-/ neg |
negates a number | 1 | 4 /-/ | -4 |
Comparison Operators |
|
|||
== | true if equal | 2 | (L:Value) 0 == if{ A } | Operation A is carried out if Value is 0. |
!= | true if not equal | 2 | (L:Value) 0 != if{ A } | Operation A is carried out if Value is not 0. |
> | true if greater than | 2 | (L:Value1) (L:Value2) > if{ A } els{ B } | If Value1 is greater than Value2, operation A is carried out, otherwise operation B is carried out. |
< | true if less | 2 | (L:Value1) (L:Value2) < if{ A } els{ B } | If Value1 is less than Value2, operation A is carried out, otherwise operation B is carried out. |
>= | true greater than or equal | 2 | (L:Value1) (L:Value2) >= if{ A } els{ B } | If Value1 is greater than or equal to Value2, operation A is carried out, otherwise operation B is carried out. |
<= | true if less than or equal | 2 | (L:Value1) (L:Value2) <= if{ A } els{ B } | If Value1 is less than or equal to Value2, operation A is carried out, otherwise operation B is carried out. |
? | The third operand determines whether the first (True) or second (False) is selected. | 3 | A B True ? | This evaluates to A. |
Bit Operators |
|
|||
& | bitwise AND | 2 | 5 3 & | 1 |
| | bitwise OR | 2 | 5 3 | | 7 |
^ | bitwise XOR | 2 | 5 3 ^ | 6 |
~ | bitwise NOT | 1 | 5 ~ | -6 |
>> | shift right operand number of bits | 2 | 5 3 >> | 0 |
<< | shift left operand number of bits | 2 | 5 3 << | 40 |
Logical Operators |
|
|||
!, NOT | NOT | 1 | (L:Local) ! (>L:Local) | Toggles the variable Local |
&&, AND | AND | 2 | (L:Local) 0xFF00 && (>L:Local) | The variable Local is ANDed with hex 0xFF00 |
||, OR | OR | 2 | (L:Local) 07777 OR (>L:Local) | The variable Local is ORed with octal 7777. |
Numerical Operators |
|
|||
abs | Absolute value | 1 | -5 abs | 5 |
int flr |
Calculates nearest integer number which is less than the source number | 1 | 5.98 flr | 5 |
rng | Range; returns True if the third operand lies between values one and two. | 3 | 4 7 6 rng | True |
cos | Cosine (input in radians) | 1 | pi cos | -1 |
lg | Logarithm to base 10 | 1 | 10 lg | 1 |
min | Minimum | 2 | 5 2 min | 2 |
sin | Sine (input in radians) | 1 | pi sin | 0 |
acos | Arc cosine (returns radians) | 1 | pi acos | |
ctg | cotangent (input in radians) | 1 | pi ctg | |
ln | Natural logarithm | 1 | 2.718282 ln | 1 |
sqr | Square | 1 | 5 sqr | 25 |
asin | arc sine | 1 | pi asin | |
eps | Floating-point relative accuracy | 1 | 1 eps | 2^(-52) |
log | Logarithm of operand one, to the base of operand two. | 2 | 8 2 log | 3 |
pi | Pi = 3.14159; puts pi on the stack | 0 | pi | 3.14159 |
sqrt | Square root | 1 | 25 sqrt | 5 |
atg2 | arc tangent with two inputs (input in radians) | 2 | ||
exp | Exponent; e to the power of the operand | 1 | 1 exp | 2.718282 |
max | Maximum | 2 | 5 2 max | 5 |
pow | Power of; the first value to the power of the second | 2 | 2 5 pow | 32 |
tg | Tangent (input in radians) | 1 | pi tg | 0 |
atg | arc tangent with one input | 1 | pi atg | |
Special Operators |
|
|||
div | Divides integers; its result is always an integer | 2 | 5 3 div | 1 |
ceil | Calculates nearest integer number which is bigger than the source | 1 | 4.3 ceil | 5 |
near | Calculates the nearest integer number, rounding .5 up. | 1 | 4.5 near | 5 |
dnor d360 rdeg |
Normalizes an angle expressed in degrees. The result is a value between 0 and 360. | 1 | -15 dnor | 345 |
rddg | Converts radians to degrees | 1 | pi rddg | 180 |
dgrd | Converts degrees to radians | 1 | 180 dgrd | pi |
rnor | Normalizes an angle expressed in radians, the result of this operation is between 0 and 2 pi | 1 | 5 pi | 1.8584 |
if{ .... } | If statement, note there is no space between the if and the { | 1 | (L:Value) 0 == if{ A } | Operation A is carried out if Value is 0. |
els{ .... } | Else statement, note there is no space between the els and the { | 1 | (L:Value1) (L:Value2) <= if{ A } els{ B } | If Value1 is less than or equal to Value2, operation A is carried out, otherwise operation B is carried out. |
quit | The quit statement allows expression evaluation to stop completely, and avoid the use of nesting if{ statements. | 0 | pi quit (L:Value1) (L:Value2) <= if{ A } els{ B } | pi. The rest of the script is ignored. |
g0...gn | Goto label. Execution will jump to the specified label. Labels are set by entering a colon followed by the label number. | 0 | g4 | Execution jump to :4 |
case | Case statement | 50 40 30 20 10 5 (L:value) case |
The "5" indicates there are five case values, which are selected
depending on the evaluation of (L:value). If the evaluation is equal to or greater than 0, but less than 1, the result is 10. If the evaluation is equal to or greater than 1, but less than 2, the result is 20, and so on. |
|
String Operators |
|
|||
lc | Converts a string to lowercase | 1 | 'ABcd10' lc | 'abcd10' |
uc cap |
Converts a string to uppercase | 1 | 'ABcd10' uc | 'ABCD10' |
chr | Converts a number to a symbol | 1 | 65 chr | 'A' |
ord | Converts a symbol to an integer | 1 | 'A' ord | 65 |
scat | Concatenates strings | 2 | 'abc' 'red' scat | 'abcred' |
schr | Finds a symbol in a string | |||
scmp | Compares strings, case sensitive | 2 | (M:Event) 'LeftSingle' scmp 0 == if{ A }els{ B } | Performs A if the left mouse button has been pressed, otherwise performs B |
scmi | Compares strings, ignoring case | 2 | 'left' 'Left' scmi 0 == if{ 'yes' } | 'yes' |
sstr | Finds a substring | 2 | 'cd' 'abcde' sstr | 2 |
ssub | Extracts a substring | 2 | 'ab' 'abcde' ssub | 'cde' |
symb | Extracts a single character | 2 | 'abc' 1 symb | 'b' |
Stack Operators |
|
|||
b | Backup the stack | 0 | ||
c | Clears the stack | 0 | stack: 1 2 3 c | stack: |
d | Duplicates the value that is on the top of the stack | 1 | stack: 5 d | stack: 5 5 |
p | Pops and discards the top value on the stack | 1 | stack: 1 2 3 p | stack: 1 2 |
r | Reverses the top and second values on the stack | 2 | stack: 1 2 3 r | stack: 1 3 2 |
s0, s1, … s49 | Stores the top value in an internal register, but does not pop it from the stack. | 1 | stack: 1 2 3 s0 |
stack: 1 2 3 s0: 3 |
l0, l1, … l49 | Loads a value from a register to the top of the stack | 1 | stack: 1 2 3 s0 l0 | stack: 1 2 3 3 |
sp0, sp1, … sp49 | Stores the top value and pops it from the stack | 1 | stack: 1 2 3 sp0 |
stack: 1 2 sp0: 3 |
Notes
- Formatted strings use a similar but slightly different syntax, see the example.
- Strings should be entered using single quotes, for example: 'abcd'.
- Hexadecimal numbers can be entered using the 0xf or 0xF convention (for example, 0xff or 0xFF00FF00)
- Octal numbers can be entered by using a leading zero. For example, 022 is octal 18. Be careful not to enter leading zeros on decimal numbers.
- Scientific notation can be used: 5E2 represents 5 x 10 squared (500), 5E-2 is 0.005
Examples
The table below gives a range of script examples that can be used for aircraft. See also the examples for the GaugeText object, and a few examples in the Gauge Color Scripts section.
Aircraft Script Examples
# | Expression | Gauge | Description |
---|---|---|---|
1 | (A:LIGHT NAV, bool) | Navigation Lights | Returns True (1) if the navigation light switch is on, False (0) otherwise. |
2 | (A:TRAILING EDGE FLAPS LEFT ANGLE, radians) 1.1 * | Flaps | Returns the left flaps angle in radians, multiplied by 1.1. Multiplications like this can help reduce rounding errors when making comparisons with integer settings. |
3 | (A:PARTIAL PANEL ELECTRICAL,enum) ! | Electrical gauges | The enum for this variable is (0 = OK, 1= fail, 2 = blank), so in this case the result is inverted by the "!" operator to give 1 = OK and 0 = fail. |
4 | (A:PLANE HEADING DEGREES GYRO, degrees) /-/ dgrd | HSI Compass ring | Returns the aircraft heading negated and converted to radians. |
5 | (A:NAV GSI:1,percent) 250 / | HSI Glideslope deflection | Returns the percentage for the GSI (Glideslope deviation indicator) for Nav1, divided by 250. |
6 | (P:Units of measure, enum) 2 == if{ (A:RADIO HEIGHT, meters) } els{ (RADIO HEIGHT, feet) } | Radio Height (or Radar Altitude) needle |
The Units of measure enum is: 0 = English 1 = Metric (with altitude in feet) 2 = Metric (with altitude in meters) So if this enum value is 2, the If statement is evaluated, otherwise the Els statement is evaluated. |
7 | (A:PARTIAL PANEL ELECTRICAL,enum)! if{ 25 (A:GENERAL ENG FUEL PRESSURE:1, psi) - } els{ 25.5 } | Fuel Pressure | If the Partial Panel Electrical is OK (see Example 3 above) then 25 minus the fuel reading for engine 1 is returned, if not the fixed value of 25.5 is returned. |
8 |
(A:FUEL TANK SELECTOR 1, enum) 0 ==
(A:FUEL TANK SELECTOR 1, enum) 3 == or if{ pi 2 / } els{ 0 } |
Right Valve of Fuel Gauge | If all the fuel tanks are selected (FUEL TANK SELECTOR enum value is 0) or the right fuel tank is selected (the enum value is 3) then the fuel valve is set at pi/2, otherwise it is set at zero. |
9 |
(A:NAV1 OBS, degrees) d (A:PARTIAL PANEL HEADING, bool) (A:PARTIAL PANEL ELECTRICAL,
bool) or 0 ==
if{ (A:PLANE HEADING DEGREES GYRO, degrees) 90 - - } dgrd |
GPS element of HSI | If the PARTIAL PANEL HEADING is false and the PARTIAL PANEL ELECTIRCAL is false, then this expression returns the NAV1 OBS reading minus the (PLANE HEADING DEGREES GYRO reading minus 90), converted to radians. |
10 |
(P:Units of measure, enum) 2 ==
if{ (A:INDICATED ALTITUDE, meters) } els{ (A:INDICATED ALTITUDE, feet) } 100000 / 360 * dgrd |
Altimeter ten thousand foot needle | Divides the altitude by one hundred thousand, then multiplies by 360, and converts the result from degrees to radians. |
11 |
(L:Show Volts 1,bool)
if{ (A:ELECTRICAL GENALT BUS VOLTAGE:1, volts) 2 * } els{ (A:ELECTRICAL GENALT BUS AMPS:1, amps) } |
Amps/Volts | If the local variable Show Volts 1 is true, the bus voltage for engine 1, multiplied by 2, is returned. Otherwise the bus amps for engine 1 is returned. |
12 |
(A:ADF Radial:1,degrees) 360 +
d360 dgrd |
ADF | The ADF radial value in degrees is added to 360 and then normalized to a value between 0 and 360. It is then converted to radians. |
Notes
- Simulation variables are not case-sensitive, they can be all upper, all lower or a mix of upper and lower case in your scripts.
String Formatting
Certain XML Gauge Elements, such as the GaugeText Objects, and Scenario Objects, such as the On Screen Text, support using scripting to more intricately format the text that is to be displayed. While GaugeText Objects support the full list of options to follow, On Screen Text only supports the text based formatting options.
NOTE: This syntax is NOT the same as generic RPN script syntax and care must be taken not to confuse the two.
Formatting Numbers
The format for numbers is contained within the !...! marks.
The last letter is required and is case-sensitive, is the formatting of the variable, where:
- s = string.
- d = decimal number (integer). If the number is not an integer, it is rounded to the nearest integer. Note that rounding, not truncation occurs.
- f = number (floating point)
The formatting letter can be preceded by a number, which is the minimum number of digits to display, and is optional. For decimal numbers the following rules apply:
- If d is preceded by the digit "0", then leading zeros are added if necessary.
- If d is preceded by "-", text is left-aligned.
- If d is preceded by "+", a "+" symbol is indicated in front of the number when the number is greater than 0 (a "-" is always used to indicate numbers less than 0).
- If d is preceded by " " (space), leading spaces are added if necessary.
For floating point numbers, the following rule applies:
- If a decimal point is used in the formatting number, the digit after the decimal point specifies the number of digits to display after the decimal point.
Examples of Number Formatting
String | Result | Description |
---|---|---|
%( 12.34 )%!4.3f! | 12.340 | The 4 in 4.3 is ignored. |
%( 12.34 )%!04.3f! | 12.340 | Leading "0"s are not added to floating point numbers. |
%( 12345.6789 )%!4.3f! | 12345.679 | The number before decimal point does not limit the number of digits displayed before decimal point. |
%( 34.56 )%!+d! | +35 | Rounding, not truncation, has occurred. |
%(234)%!5d! | 234 | Two leading spaces have been prefixed to the number. |
%( 'foo' )%!5s! | foo | Two leading spaces have been prefixed to the string |
%( 234 )%!3s! | 234 | The number is output as a string, with a minimum of three digits. |
Conditional Formatting
The format of conditions (if, then, else, and case statements) in gauge strings is different from that in other scripts. In gauge strings use the %{if}, %{else}, and %{end} constructs to choose which text to display. Note that these keywords are case-sensitive and must be typed in lowercase. Also, there must not be a space between the "%" and the "{". An if statement can be used without a corresponding else, in which case nothing is displayed if the result of the condition is false. The syntax for usage is one of the following:
- %(CONDITIONAL)%{if}TEXT TO DISPLAY IF TRUE%{else}TEXT TO DISPLAY IF FALSE%{end}
- %(CONDITIONAL)%{if}TEXT TO DISPLAY IF TRUE%{end}
For example: %( 1 )%{if}ON%{else}OFF%{end} would give the output ON. Whereas %( 0 )%{if}The value is true%{else}The value is false%{end} would give the output :The value is false.
In the Example of Gauge Strings below, note also the case and loop statements.
Escape Code Formatting
It is also possible to insert escape code sequences into gauge strings.
Escape code | Translation |
---|---|
\{tabs=50R,60C, 244L} | Set 3 tab stops; the first is right-aligned, the second is centered, and last is left-aligned. |
\{fnt1} | Switch to the first alternate font specified as a child of the gauge text element |
\{fnt} | Return to the default font |
\{up} | Superscript |
\{dn} | Subscript |
\{md} | Normal (neither superscript nor subscript) |
\{bo} | Bold |
\{ul} | Underline |
\{itl} | Italic |
\{strk} | Strikeout |
\{blnk} | Blink |
\{rev} | Reverse background/foreground color for text |
\{nr} | Normal -- clear all properties previously set. |
\{lcl} | Line color |
\{blc} | Background line color |
\{clr} | Color |
\{bck} | Background color |
\{dplo=X} | Put a degrees symbol above the next character after the ?=? |
\{dpl=XY} | Make X superscript and Y subscript |
\{lsp=23} | Set line spacing to 23 |
\{lsp} | Set line spacing to default |
\{ladj=L} | Set horizontal text alignment to left. (use ?C? for center or ?R? for right) |
\{line=240} | Draw a horizontal line with width 240 |
\{lmrg=20} | Set the left margin to 20 |
\{rmrg=30} | Set the right margin to 30 |
\{img1} | Insert image #1 (a text element can have image children) |
Examples
Gauge string | Description |
---|---|
Fuel Pressure | The text will appear exactly as entered: Fuel Pressure |
Fuel Capacity: %((A:FUEL TOTAL CAPACITY))%!1.2f! |
The fuel capacity of the aircraft will be given as a floating point number accurate
to two decimal places, following the initial string, such as: Fuel Capacity: 80.55 |
%((A:ENG ON FIRE:1) (A:ENG ON FIRE:2) ! if{ 'Warning: Engine Fire' } ) | The text string "Warning: Engine Fire" will appear if either or both of the engines are on fire. |
%( 1 )%{if}ON%{else}OFF%{end} | The text ON would be rendered. If there is no {else} statement, then no text will be displayed if the condition evaluates to false. |
%( 3 )%{case}%{ :0 }AIRPORT%{ :1 }INTERSECTION%{ :2 }NDB%{ :3 }VOR%{ :4 }MARKER%{end} |
A case statement can be used to select a text string from a group of strings. The
case numbers do not have to be sequential. The example would produce the result: VOR |
%((C:Mission:OnScreenTimerValue) 60 / 60 / flr )%!02d!: %((C:Mission:OnScreenTimerValue) 60 / flr 60 %)%!02d!: %((C:Mission:OnScreenTimerValue) flr 60 %)%!02d!. %((C:Mission:OnScreenTimerValue) 10 * flr 10 % )%!01d! |
Takes the custom on-screen timer value and displays the time in hours, minutes, seconds
and tenths of a second. The !02d! indicates that the text output should be displayed with two digits (for example, 06). The % signs inside the string refer to the modulus operator, and the colons and period between the statements will appear on the screen as text, for example: 01 : 14: 08 . 2 |
85 %% |
To output the percent character, use two percent signs in the script: 85 % |
%(10 s2 1 s1)%{loop}%( l1 )%!s! %( l1 ++ s1 l2 <)%{next} |
This statement sets up two registers s1 and s2, with the numbers 1 and 10, then
loops to print out: 1 2 3 4 5 6 7 8 9 Whatever value is on top of the stack when the %(next) statement is reached is evaluated as a boolean to determine if execution of the loop should continue. |
\b Indented Text | The parser will strip leading whitespace. A backspace '\b' can be used to override this behavior if indented text is required. |
The Infix2Postfix Tool
The Infix2Postfix tool makes it easier to both understand the logic of existing postfix (often called reverse Polish) notation, and enables the writing of scripts in the more normal infix notation familiar to all programmers. The Infix2Posfix tool is in the following folder:
The following screen shots show a conversion from infix to postfix, and vice versa. You can use the Paste button to take infix notation from the clipboard, and the Copy button to copy the postfix notation to the clipboard, so that it can be pasted into a gauge script. Alternatively use the File menu to select an existing XML gauge file, and you can then step through the scripts in the file, and examine the conversion to infix. The syntax for the infix notation is similar to C# or C++, and is available in a text file through an option in the Help menu.
Note
This tool is for educational purposes only, there are some scripts that it will not convert correctly.
Converting from infix to postfix |
---|
Converting from postfix to infix |