Hi fellow wiki editors!

To help newly registered users get more familiar with the wiki (and maybe older users too) there is now a {{Welcome to the wiki}} template. Have a look at it and feel free to add it to new users discussion pages (and perhaps your own).

I have tried to keep the template short, but meaningful. /Johan G

Difference between revisions of "Writing Joystick Code: Part 3"

From FlightGear wiki
Jump to: navigation, search
Line 9: Line 9:
  
 
-----------------------------------------------------------------------------
 
-----------------------------------------------------------------------------
  To toggle the Parking brake, use this. Entire code-block shown. Non-repeatable button. (You have to specifically set <repeatable> to true if you want repeatable. It is assumed to be false.
+
  To toggle the Parking brake, use this. Entire code-block shown. Non-repeatable button. (You have to specifically set <repeatable> to true
 +
if you want repeatable. It is assumed to be false.
 
   <button n="1">
 
   <button n="1">
 
     <desc>Toggle parking break</desc>
 
     <desc>Toggle parking break</desc>
Line 18: Line 19:
 
   </button>
 
   </button>
 
   
 
   
  This method can be used to toggle any property that is on/off. Use Debug - Browse Internal Properties to find suitable candidates. Also see getProp, setProp and setAll below.
+
  This method can be used to toggle any property that is on/off. Use Debug - Browse Internal Properties to find suitable candidates.  
 +
Also see getProp, setProp and setAll below.
 
   
 
   
 
  Remember, it ''toggles'' the property, you can't use it to set the property into a specific state.
 
  Remember, it ''toggles'' the property, you can't use it to set the property into a specific state.
Line 101: Line 103:
 
   }
 
   }
  
Now lets pretend that we still want to increase x by 2 if it less than 5, and if it is 5 or more we want to add 1 to it. We use else. It looks like this
+
Now lets pretend that we still want to increase x by 2 if it less than 5, and if it is 5 or more we want to add 1 to it. We use else.
 +
It looks like this
 
   if (x < 5) {
 
   if (x < 5) {
 
     x = x + 2
 
     x = x + 2
Line 117: Line 120:
 
   If (x < 5) then (x = x + 2) else if (x < 10) then (x = x + 1). If none of these are true then (x = x + 7). End of if clause.
 
   If (x < 5) then (x = x + 2) else if (x < 10) then (x = x + 1). If none of these are true then (x = x + 7). End of if clause.
  
We test for x < 5 first. If it fails we test for x < 10. If x , 5 succeeds we do (x = x + 2) and then move on past the end of if clause. So we can test for x < 5 and x < 10 in this way.
+
We test for x < 5 first. If it fails we test for x < 10. If x , 5 succeeds we do (x = x + 2) and then move on past the end of if clause.
 +
So we can test for x < 5 and x < 10 in this way.
  
 
Writing our code properly we get
 
Writing our code properly we get
Line 130: Line 134:
 
   }
 
   }
  
You can use more than one elseif, but it gets messy. If you have a number of tests then it is better to test for each case individually. Easier to understand and much less chance of an error. Our above example would become
+
You can use more than one elseif, but it gets messy. If you have a number of tests then it is better to test for each case individually.
 +
Easier to understand and much less chance of an error. Our above example would become
 
   if (x < 5) {
 
   if (x < 5) {
 
     x = x + 2
 
     x = x + 2
Line 164: Line 169:
 
To use a property you need its value. The best way to find out the path to a property is with Debug - Browse Internal Proprties.
 
To use a property you need its value. The best way to find out the path to a property is with Debug - Browse Internal Proprties.
  
Let us say we want to the current value of elevator-trim. From the menu select Debug and then Browse Internal Properties. Use the scroll-bar to go up and down the list and look at the options. One of them is controls. This is a good place to start, the elevator-trim is a control. Click on controls and inspect the new list of options.
+
Let us say we want to the current value of elevator-trim. From the menu select Debug and then Browse Internal Properties.
 +
Use the scroll-bar to go up and down the list and look at the options. One of them is controls. This is a good place to start,
 +
the elevator-trim is a control. Click on controls and inspect the new list of options.
  
One of them is flight. Good choice, the elevator-trim controls how the aircraft flies. Click on flight. Look at the list of variables displayed.
+
One of them is flight. Good choice, the elevator-trim controls how the aircraft flies. Click on flight.
 +
Look at the list of variables displayed.
  
You will see elevator-trim = '0' (double) (It might not say 0.) The (double) means that it is a double-precision floating point number. This means that it can change in small amounts and has a decimal point with numbers after the decimal point. Make a note that it is a floating point number.
+
You will see elevator-trim = '0' (double) (It might not say 0.) The (double) means that it is a double-precision floating point number.
 +
This means that it can change in small amounts and has a decimal point with numbers after the decimal point.
 +
Make a note that it is a floating point number.
  
Look at the top of the dialog-box and you will see that it says /controls/flight. We can see that the variable is elevator-trim, so make a note that the path to the variable is /controls/flight/elevator-trim.
+
Look at the top of the dialog-box and you will see that it says /controls/flight. We can see that the variable is elevator-trim,
 +
so make a note that the path to the variable is /controls/flight/elevator-trim.
  
 
You can close the dialog-box.
 
You can close the dialog-box.
Line 177: Line 188:
 
   getProp("/controls/flight/elevator-trim")
 
   getProp("/controls/flight/elevator-trim")
  
Having got the current value we probably want to change it. To do that we use setProp. Lets pretend that the current value is 0.175 and we want to make it 0.200. We use
+
Having got the current value we probably want to change it. To do that we use setProp. Lets pretend that the current value is 0.175
 +
and we want to make it 0.200. We use
 
   setProp("/controls/flight/elevator-trim", 0.200)
 
   setProp("/controls/flight/elevator-trim", 0.200)
  
 
Elevator trim is a double (floating point) number. The range of values will be -1.000 through 0 to +1.000.
 
Elevator trim is a double (floating point) number. The range of values will be -1.000 through 0 to +1.000.
  
If the value is an integer (int) the range of values is ... -3, -2, -1, 0, 1, +2, +3 ... This doesn't mean that -3 is a suitable value for that specific property, it just means that you can pass -3 as a value without crashing the system.
+
If the value is an integer (int) the range of values is ... -3, -2, -1, 0, 1, +2, +3 ... This doesn't mean that -3 is a suitable value
 +
for that specific property, it just means that you can pass -3 as a value without crashing the system.
  
 
Sometimes the value is a boolen (bool). This means that it is either true or false. Then you would use
 
Sometimes the value is a boolen (bool). This means that it is either true or false. Then you would use
Line 190: Line 203:
 
You can also use 1 instead of "true" and 0 instead of "false". If you use getProp, you will get 0 and 1 as your value, not "true" and "false."
 
You can also use 1 instead of "true" and 0 instead of "false". If you use getProp, you will get 0 and 1 as your value, not "true" and "false."
  
In multi-engined aircraft you can use throttleAxis() when you are adjusting the throttle with an axis. This will do all engines. But if you are using a button you need to set the throttle for all engines at the same time. To do this you need props.setAll(). It is a waste of time to try to take the throttles past maximum, so you first get the throttle setting, check if it is less than maximum, and if it is, increase it a bit. You end up with
+
In multi-engined aircraft you can use throttleAxis() when you are adjusting the throttle with an axis. This will do all engines.
 +
But if you are using a button you need to set the throttle for all engines at the same time. To do this you need props.setAll().
 +
It is a waste of time to try to take the throttles past maximum, so you first get the throttle setting, check if it is less than maximum,
 +
and if it is, increase it a bit. You end up with
 
   <![CDATA[
 
   <![CDATA[
 
     if(getprop("controls/engines/engine/throttle") < 1 ) {
 
     if(getprop("controls/engines/engine/throttle") < 1 ) {
Line 214: Line 230:
 
If you are happy with a 1 second display, you can leave out the Time-to-display (and the comma before it.
 
If you are happy with a 1 second display, you can leave out the Time-to-display (and the comma before it.
  
The String-to-display can get hairy. To start off, choose one of your buttons (not the hat.) Let's say it is button number 3. Just before </script> add this line:
+
The String-to-display can get hairy. To start off, choose one of your buttons (not the hat.) Let's say it is button number 3.
 +
Just before </script> add this line:
 
   gui.popupTip(sprintf("Button 3 pressed"),3)
 
   gui.popupTip(sprintf("Button 3 pressed"),3)
 
Don't forget to add a ; at the end of the previous line.
 
Don't forget to add a ; at the end of the previous line.
Line 222: Line 239:
 
Delete the line and the ; from your xml file.
 
Delete the line and the ; from your xml file.
  
This was not very useful in itself. What we normally want to display is some property. A good mexample is when adjusting a trim with a button - you have no idea where you are. You can use the HUD, but when you are transitioning to straight and level flight, it is much easier to remember a number from the last time, than the position of the trim on the HUD.
+
This was not very useful in itself. What we normally want to display is some property. A good example is when adjusting a trim
 +
with a button - you have no idea where you are. You can use the HUD, but when you are transitioning to straight and level flight,
 +
it is much easier to remember a number from the last time, than the position of the trim on the HUD.
  
 
We are going to display the value of elevator-trim. See getProp, setProp and setAll above to learn how to get the current value.
 
We are going to display the value of elevator-trim. See getProp, setProp and setAll above to learn how to get the current value.
Line 229: Line 248:
 
   getProp("/controls/flight/elevator-trim")
 
   getProp("/controls/flight/elevator-trim")
  
Now we want to display it. For a start we will have "Elevator-trim: " followed by the value that we got. But it is a number so we need to tell sprintf that there is a number coming, so we change our string to "Elevator-trim: %". We still need to tell sprintf that it is a floating point number, so we use "Elevator-trim: %f". But we would like it accurate to 3 decimal places, so we use "Elevator-trim" %.3f". Now we will have "Elevator-trim: %.3f", getProp("/controls/flight/elevator-trim").
+
Now we want to display it. For a start we will have "Elevator-trim: " followed by the value that we got. But it is a number,
 +
so we need to tell sprintf that there is a number coming, so we change our string to "Elevator-trim: %".
 +
We still need to tell sprintf that it is a floating point number, so we use "Elevator-trim: %f". But we would like it accurate
 +
to 3 decimal places, so we use "Elevator-trim" %.3f". Now we will have "Elevator-trim: %.3f", getProp("/controls/flight/elevator-trim").
  
 
The line of code ends up as
 
The line of code ends up as
Line 238: Line 260:
 
Add the line to aileron-trim and rudder-trim (if you have it), not forgetting to change all occurrences of elevator to aileron and rudder.
 
Add the line to aileron-trim and rudder-trim (if you have it), not forgetting to change all occurrences of elevator to aileron and rudder.
  
We can make the display for elevator-trim a bit better. The elevator-trim control is usually a largish wheel that you turn. In the C172 it has a range of 9 turns. -4.5 to +4.5. Let's adjust our code to be similar. Our getProp returns -1.0 to +1.0, so if we multiply it by 4.5 we will get -4.5 to + 4.5. We will only need 2 decimal places, so the code becomes
+
We can make the display for elevator-trim a bit better. The elevator-trim control is usually a largish wheel that you turn.
 +
In the C172 it has a range of 9 turns. -4.5 to +4.5. Let's adjust our code to be similar. Our getProp returns -1.0 to +1.0,
 +
so if we multiply it by 4.5 we will get -4.5 to + 4.5. We will only need 2 decimal places, so the code becomes
 
   gui.popupTip(sprintf("Elevator-trim: %.2f", 4.5 * getprop("/controls/flight/elevator-trim")))
 
   gui.popupTip(sprintf("Elevator-trim: %.2f", 4.5 * getprop("/controls/flight/elevator-trim")))
  
Line 254: Line 278:
 
   interpolate(property-to-adjust, target-value, time-to-get-there-in-seconds)
 
   interpolate(property-to-adjust, target-value, time-to-get-there-in-seconds)
  
An example of its use would be in using a non-repeatable button to adjust aileron-trim. You could make repetitive pushes of the button, but that is not satisfactory. Much easier to use interpolate. You want the aileron trim to go from center to max (1.0) in 15 seconds. the code is
+
An example of its use would be in using a non-repeatable button to adjust aileron-trim. You could make repetitive pushes of the button,
 +
but that is not satisfactory. Much easier to use interpolate. You want the aileron trim to go from center to max (1.0) in 15 seconds. the code is
 
   interpolate("controls/flight/aileron-trim", 1, 15)
 
   interpolate("controls/flight/aileron-trim", 1, 15)
 
You give the button a tap and in 15 seconds time the aileron-trim is at 1.0.
 
You give the button a tap and in 15 seconds time the aileron-trim is at 1.0.
  
But that was starting at 0. If the trim is at 0.5 when you start, you want it to get to max in half the time, 7.5 seconds. The trick is to get the current aileron-trim, subtract it from the max, thus giving you the change still needed, and use that to factor the time-to-get-there. This gives you
+
But that was starting at 0. If the trim is at 0.5 when you start, you want it to get to max in half the time, 7.5 seconds.
 +
The trick is to get the current aileron-trim, subtract it from the max, thus giving you the change still needed,
 +
and use that to factor the time-to-get-there. This gives you
 
   interpolate("controls/flight/aileron-trim", 1, (1 - getProp("controls/flight/aileron-trim") * 15)
 
   interpolate("controls/flight/aileron-trim", 1, (1 - getProp("controls/flight/aileron-trim") * 15)
  
  
All good and well, but if we are really using a non-repeatable button to adjust aileron-trim, we want to be able to stop the adjustment when it gets to a suitable value. The secret is to use <mod-up> to stop the action. To stop the action we use interpolate again, but this time we set the target to the current value, and the time-to-get-there as 0.
+
All good and well, but if we are really using a non-repeatable button to adjust aileron-trim, we want to be able to stop the adjustment
 +
when it gets to a suitable value. The secret is to use <mod-up> to stop the action. To stop the action we use interpolate again,
 +
but this time we set the target to the current value, and the time-to-get-there as 0.
 
   <mod-up>
 
   <mod-up>
 
     interpolate("controls/flight/aileron-trim", getProp("controls/flight/aileron-trim"), 0)
 
     interpolate("controls/flight/aileron-trim", getProp("controls/flight/aileron-trim"), 0)
 
   </mod-up>
 
   </mod-up>
Now, when the button is pressed, interpolation starts. Just once, because it is a non-repeatable button. When we are happy with the aileron-trim we release the button and interpolation stops.
+
Now, when the button is pressed, interpolation starts. Just once, because it is a non-repeatable button.
 +
When we are happy with the aileron-trim we release the button and interpolation stops.
  
 
Nice.
 
Nice.
Line 274: Line 304:
 
=== Variables ===
 
=== Variables ===
  
Lets say we want our buttons to behave in different ways, depending on whether the aircraft is on the ground, aircraft is in the air, aircraft is an helicopter. To do this we have a variable called mode, which has 3 values
+
Lets say we want our buttons to behave in different ways, depending on whether the aircraft is on the ground, aircraft is in the air,
 +
aircraft is an helicopter. To do this we have a variable called mode, which has 3 values
 
   mode = 1 Aircraft on ground
 
   mode = 1 Aircraft on ground
 
   mode = 2 Aircraft in air
 
   mode = 2 Aircraft in air
Line 280: Line 311:
 
Now, each time a button is pressed, we get the value of mode and use if to decide which action the button must carry out.
 
Now, each time a button is pressed, we get the value of mode and use if to decide which action the button must carry out.
  
Of course, we need some way to set the value of mode, say one of the buttons. Non-repeatable. Each time the button is clicked mode goes up 1 in value. If it is 3, then clicking takes it round to 1 again.
+
Of course, we need some way to set the value of mode, say one of the buttons. Non-repeatable. Each time the button is clicked
 +
mode goes up 1 in value. If it is 3, then clicking takes it round to 1 again.
  
 
There are two ways of defining variables.
 
There are two ways of defining variables.

Revision as of 23:10, 14 October 2012

WIP.png Work in progress
This article or section will be worked on in the upcoming hours or days.
See history for the latest developments.


Special snippets

Once again, the examples may use concepts that you need to explore in the Advanced Programming section.



To toggle the Parking brake, use this. Entire code-block shown. Non-repeatable button. (You have to specifically set <repeatable> to true
if you want repeatable. It is assumed to be false.
 <button n="1">
   <desc>Toggle parking break</desc>
   <binding>
     <command>property-toggle</command>
     <property>/controls/gear/brake-parking</property>
    </binding>
 </button>

This method can be used to toggle any property that is on/off. Use Debug - Browse Internal Properties to find suitable candidates. 
Also see getProp, setProp and setAll below.

Remember, it toggles the property, you can't use it to set the property into a specific state.

To make condition work with mixture on a lever (axis). This is pertinent to turboprops.
Change
   controls.mixtureAxis()
to
   controls.mixtureAxis();
   props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))

To make condition work with mixture on a non-repeatable button.

Make mixture richer NR
  <![CDATA[ 
    if(getprop("controls/engines/engine/mixture") < 1 ) {
      setprop("controls/engines/engine/mixture", getprop ("controls/engines/engine/mixture") + 0.05);
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
 
Make mixture leaner NR
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") > 0 ) {
       setprop("controls/engines/engine/mixture", getprop("controls/engines/engine/mixture") - 0.05)
     props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>


Make mixture richer R
  <![CDATA[ 
    if(getprop("controls/engines/engine/mixture") < 1 ) {
      setprop("controls/engines/engine/mixture", getprop ("controls/engines/engine/mixture") + 0.001);
      props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>
 
Make mixture leaner R
  <![CDATA[
    if(getprop("controls/engines/engine/mixture") > 0 ) {
       setprop("controls/engines/engine/mixture", getprop("controls/engines/engine/mixture") - 0.001)
     props.setAll("controls/engines/engine", "condition", getprop("controls/engines/engine/mixture"))
    }
  ]]>


This is an alternative method to adjust a property

  <repeatable>true</repeatable>
  <binding>
    <command>property-adjust</command>
    <property>/sim/current-view/goal-pitch-offset-deg</property>
    <step type="double">-2.0</step>
  </binding>

This will continously reduce /sim/current-view/goal-pitch-offset-deg in steps of 2 while the button is held in.


Advanced Programming

if ... then ... else

Sometimes you only want to do something if a certain condition is true (or false.) In this case you use an if clause. The general form is

 if (condition) then action endif

We write then as {, and endif as }.

Lets say that if x is less than 5 we want to add 2 to it. We write

 if (x < 5) { x = x + 2 }

The more readable way of writing it is

 if (x < 5) {
   x = x + 2
 }

If we also want to add 1 to y if the condition is true, we write

 if (x < 5) {
   x = x + 2;
   y + y + 1
 }

Now lets pretend that we still want to increase x by 2 if it less than 5, and if it is 5 or more we want to add 1 to it. We use else. It looks like this

 if (x < 5) {
   x = x + 2
 }
 else {
   x = x + 1
 }

Now lets say we have the following rules:

 If x < 5 add 2 to it
 If x < 10 add 1 to it
 If x is 10 or more add 7 to it. In this case elseif becomes useful.

In English our code would be

 If (x < 5) then (x = x + 2) else if (x < 10) then (x = x + 1). If none of these are true then (x = x + 7). End of if clause.

We test for x < 5 first. If it fails we test for x < 10. If x , 5 succeeds we do (x = x + 2) and then move on past the end of if clause. So we can test for x < 5 and x < 10 in this way.

Writing our code properly we get

 if (x < 5) {
   x = x + 2
 }
 elseif (x < 10) {
   x = x + 1
 }
 else {
   x = x + 7
 }

You can use more than one elseif, but it gets messy. If you have a number of tests then it is better to test for each case individually. Easier to understand and much less chance of an error. Our above example would become

 if (x < 5) {
   x = x + 2
 }
 if (x >= 5) and (x < 10) {
   x = x + 1
 }
 if (x >= 10) {
   x = x + 7
 }

Because each if is tested we need to have the (x >= 5) otherwise if x = 1 both the first and second if conditions would be satisfied.

But the advantage is that we are forced to write out each condition exactly as it should be tested. Easier to understand and easier to maintain.


Lets say that you want something to happen only if the gear is up. But the only property you can read is Gear-Down. Then you write

 if (!Gear-Down) {
    action
 }

The ! means not. So it translates as: If the gear is not down perform action.


Beacuse we use < and > we will need to enclose it all with CDATA.

See the discussion of variables below for an example of the use of if.


getProp, setProp and setAll

To use a property you need its value. The best way to find out the path to a property is with Debug - Browse Internal Proprties.

Let us say we want to the current value of elevator-trim. From the menu select Debug and then Browse Internal Properties. Use the scroll-bar to go up and down the list and look at the options. One of them is controls. This is a good place to start, the elevator-trim is a control. Click on controls and inspect the new list of options.

One of them is flight. Good choice, the elevator-trim controls how the aircraft flies. Click on flight. Look at the list of variables displayed.

You will see elevator-trim = '0' (double) (It might not say 0.) The (double) means that it is a double-precision floating point number. This means that it can change in small amounts and has a decimal point with numbers after the decimal point. Make a note that it is a floating point number.

Look at the top of the dialog-box and you will see that it says /controls/flight. We can see that the variable is elevator-trim, so make a note that the path to the variable is /controls/flight/elevator-trim.

You can close the dialog-box.

To get the current value of elevator-trim in your xml file you will use

  getProp("/controls/flight/elevator-trim")

Having got the current value we probably want to change it. To do that we use setProp. Lets pretend that the current value is 0.175 and we want to make it 0.200. We use

  setProp("/controls/flight/elevator-trim", 0.200)

Elevator trim is a double (floating point) number. The range of values will be -1.000 through 0 to +1.000.

If the value is an integer (int) the range of values is ... -3, -2, -1, 0, 1, +2, +3 ... This doesn't mean that -3 is a suitable value for that specific property, it just means that you can pass -3 as a value without crashing the system.

Sometimes the value is a boolen (bool). This means that it is either true or false. Then you would use

 setProp("path-to-property", "true")
 setProp("path-to-property", "false")

You can also use 1 instead of "true" and 0 instead of "false". If you use getProp, you will get 0 and 1 as your value, not "true" and "false."

In multi-engined aircraft you can use throttleAxis() when you are adjusting the throttle with an axis. This will do all engines. But if you are using a button you need to set the throttle for all engines at the same time. To do this you need props.setAll(). It is a waste of time to try to take the throttles past maximum, so you first get the throttle setting, check if it is less than maximum, and if it is, increase it a bit. You end up with

  <![CDATA[
    if(getprop("controls/engines/engine/throttle") < 1 ) {
     props.setAll("controls/engines/engine", "throttle", getprop("controls/engines/engine/mixture") + 0.001)
    }
  ]]>

You need CDATa because of the less-than sign.

You can use this for throttle, propeller, mixture and condition.

BTW Here the props in props.setAll is short for property, not propellers.



gui.popupTip

When you zoom in or out, a little window pops up giving you the field of view. You can do this with any adjustment you make.

 gui.popupTip(sprintf(String-to-display),Time-to-display-in-seconds)

If you are happy with a 1 second display, you can leave out the Time-to-display (and the comma before it.

The String-to-display can get hairy. To start off, choose one of your buttons (not the hat.) Let's say it is button number 3. Just before </script> add this line:

 gui.popupTip(sprintf("Button 3 pressed"),3)

Don't forget to add a ; at the end of the previous line.

Start (or restart) FG and press that button. You will see the message "Button 3 pressed" for 3 seconds.

Delete the line and the ; from your xml file.

This was not very useful in itself. What we normally want to display is some property. A good example is when adjusting a trim with a button - you have no idea where you are. You can use the HUD, but when you are transitioning to straight and level flight, it is much easier to remember a number from the last time, than the position of the trim on the HUD.

We are going to display the value of elevator-trim. See getProp, setProp and setAll above to learn how to get the current value.

To get the value of elevator-trim in your xml file you will use

  getProp("/controls/flight/elevator-trim")

Now we want to display it. For a start we will have "Elevator-trim: " followed by the value that we got. But it is a number, so we need to tell sprintf that there is a number coming, so we change our string to "Elevator-trim: %". We still need to tell sprintf that it is a floating point number, so we use "Elevator-trim: %f". But we would like it accurate to 3 decimal places, so we use "Elevator-trim" %.3f". Now we will have "Elevator-trim: %.3f", getProp("/controls/flight/elevator-trim").

The line of code ends up as

 gui.popupTip(sprintf("Elevator-trim: %.3f", getprop("/controls/flight/elevator-trim")))

Add it to both trim up and trim down, not forgetting the ; at the end of the previous line. Then play a bit, you'll like it.

Add the line to aileron-trim and rudder-trim (if you have it), not forgetting to change all occurrences of elevator to aileron and rudder.

We can make the display for elevator-trim a bit better. The elevator-trim control is usually a largish wheel that you turn. In the C172 it has a range of 9 turns. -4.5 to +4.5. Let's adjust our code to be similar. Our getProp returns -1.0 to +1.0, so if we multiply it by 4.5 we will get -4.5 to + 4.5. We will only need 2 decimal places, so the code becomes

  gui.popupTip(sprintf("Elevator-trim: %.2f", 4.5 * getprop("/controls/flight/elevator-trim")))

If you get a property which is on or off, you can use this to display it

  var i = getprop("controls/engines/engine/reverser");
  gui.popupTip("Thrust Reverser " ~ ["OFF", "ON"][i]);

If the message is the wrong way round you must swap the "OFF" and "ON" around.


Interpolate

Sometimes we need a property's value to change over a set period of time. For this we use interpolate. It takes the form

 interpolate(property-to-adjust, target-value, time-to-get-there-in-seconds)

An example of its use would be in using a non-repeatable button to adjust aileron-trim. You could make repetitive pushes of the button, but that is not satisfactory. Much easier to use interpolate. You want the aileron trim to go from center to max (1.0) in 15 seconds. the code is

 interpolate("controls/flight/aileron-trim", 1, 15)

You give the button a tap and in 15 seconds time the aileron-trim is at 1.0.

But that was starting at 0. If the trim is at 0.5 when you start, you want it to get to max in half the time, 7.5 seconds. The trick is to get the current aileron-trim, subtract it from the max, thus giving you the change still needed, and use that to factor the time-to-get-there. This gives you

 interpolate("controls/flight/aileron-trim", 1, (1 - getProp("controls/flight/aileron-trim") * 15)


All good and well, but if we are really using a non-repeatable button to adjust aileron-trim, we want to be able to stop the adjustment when it gets to a suitable value. The secret is to use <mod-up> to stop the action. To stop the action we use interpolate again, but this time we set the target to the current value, and the time-to-get-there as 0.

 <mod-up>
   interpolate("controls/flight/aileron-trim", getProp("controls/flight/aileron-trim"), 0)
 </mod-up>

Now, when the button is pressed, interpolation starts. Just once, because it is a non-repeatable button. When we are happy with the aileron-trim we release the button and interpolation stops.

Nice.


Variables

Lets say we want our buttons to behave in different ways, depending on whether the aircraft is on the ground, aircraft is in the air, aircraft is an helicopter. To do this we have a variable called mode, which has 3 values

 mode = 1 Aircraft on ground
 mode = 2 Aircraft in air
 mode = 3 Aircraft is an helicopter

Now, each time a button is pressed, we get the value of mode and use if to decide which action the button must carry out.

Of course, we need some way to set the value of mode, say one of the buttons. Non-repeatable. Each time the button is clicked mode goes up 1 in value. If it is 3, then clicking takes it round to 1 again.

There are two ways of defining variables.

Method 1

 
   <mode type="int">1</mode>  # Initialise mode to 1
 
 
 <nasal>
   <script>
     <![CDATA[
	   var self = cmdarg().getParent();
	   var data = self.getNode("data");
	   var mode = data.getNode("mode");
        get_mode = func { mode.getValue() }
      ]]></script>
  </nasal>
 
  var m   # Used whenever mode is accessed

This goes right at the top of your xml file, just after </name>.

The code for the button (non-repeatable)that changes mode looks like this

 m = get_mode();
 m = m + 1;
 if (m == 4) {
   m = 1
 }
 mode.setIntValue(m)  

When a button is pressed, the code looks like this

 m = get_mode();
 if (m == 1) {
   ground-action
 }
 if (m == 2) {
   air-action
 }
 if (m == 3) {
   helicopter-action
 }

Advantages: Mode is defined within the xml file.

Disadvantages: Complicated code needed to declare the variable.


Method 2

Define your mode variable with --prop:mode=1 in the command line.

You also need var mode just after </name> at the top of the xml file.

The activator for changing mode has this code

 mode = getprop("mode");
 mode = mode +1;
 if (mode == 4) {
   mode = 1
 }
 setProp("mode", mode)

Then the button pressed code looks like this

 mode = getprop("mode");
 if (m == 1) {
   ground-action
 }
 if (m == 2) {
   air-action
 }
 if (m == 3) {
   helicopter-action
 }


Advantages: Variable is easy to create.

Disadvantages: You have to have at --prop: in the command line. You can't type --prop: in the comments section at the top of your file, because of the consecutive minus signs. You have to type minusminusprop: and say that "minus" must be substituted with "-"


Whichever method you use, choose a better name than mode for your variable. It is too common a word and may well one day be used somewhere in the system files. In the xml file for the Saitek Yoke, where I use a variable to control the assignments of the levers on the quadrant, I call the variable SaitekMultiMode. Unlikely to be used by the FG programmers, or anybody else.




peraxisindexhandler



Go back to Writing Joystick Code: Part 2

Go forward to Writing Joystick Code: Part 4




Any complaints/suggestions/questions/kudos can be posted here.