Joystick autopilot bindings

From FlightGear wiki
(Redirected from Joystick Autopilot Bindings)
Jump to navigation Jump to search

This page describes joystick bindings, which can be used to control the (simulated) autopilot with a set of joystick buttons.

Prerequisites

Before you start, read Input device and probably Writing Joystick Code: Part 1.

Introduction

Autopilots are often very useful devices, but unfortunately the standard key bindings of Flightgear control the `magic autopilot' rather than the simulated real ones, and the only way to control popular GA autopilots is by clicking the corresponding buttons in the 3D-cockpit. This is awkward if there is a need to change mode or turn off the autopilot quickly (at final stage of ILS landing, or if the autopilot does something silly).

Every type of autopilot is operated quite differently, such that these keys only work for specific models. Here, key bindings are provided for the Bendix/King KAP140 Autopilot (in the Cessna 172) and the CENTURY III Autopilot POH (Seneca II). Those were prioritised, as the more complex autopilots in jets usually have a dedicated GUI and therefore maybe less in need of joystick binding. However, it is planned to provide bindings for at least basic functions like AUTOPILOT OFF for other autopilot types in the future.

Implementation

The example code provides a setup for six buttons arranged like this:

4 5 6
9 8 7

(this happens to be suitable for the Thrustmaster T16000M but it can be adapted trivially to other button numbers by simply changing the button numbers in the snippets below). In the following snippets for each functionality are provided - usually one with the actual binding and some corresponding initialisation code (placed between nasal tags at the beginning of the script). You need to copy/paste both into your joystick bindings xml file. In the summary the code of all snippets is provided in one code box for convenience but you learn more if you consider each snippet in turn. Generally, it was attempted to have largely consistent bindings for the different autopilot types, but because they are operated quite differently, this was not entirely possible, and it was attempted to stay close to the mode of operation of the actual instruments.

Generic initialisation code

On startup, we need to figure out whether we are dealing with a Bendix KAP 140 or CENTURY III autopilot.

<nasal>
    <!-- set autopilot type:
          1: KAP140     (e.g. c172p)
          2: CENTURYIII (e.g. senecaII) -->
   <script><![CDATA[
      var autopilottype=0;
      var horizontalmode=0;
     if (getprop("sim/aircraft")=="c172p") {
        print("JOYSTICK INIT: Found KAP140 autopilot");
        autopilottype=1;
     } elsif (getprop("autopilot/CENTURYIII/power")>-1) {
        print("JOYSTICK INIT: Found CENTURYIII autopilot");
        autopilottype=2; 
     }
    ....
   ]]></script>
</nasal>

As you can see it assumes only the Cessna 172 has the KAP-140. If there is another plane with this autopilot the condition should be adjusted.

Major mode selection

Button 4 turns the autopilot on or off (toggle switch). With modifiers:
(SHIFT) HDG mode toggle (the details of this mode are controlled by button 4)
(CTRL) PITCH mode toggle (CENTURYIII only!)
(ALT) ALT mode toggle (KAP140: switches between VS and ALT vertical modes. CENTURYIII: turns on and off ALT mode)

Button settings:

<button n="4">
    <repeatable type="bool">false</repeatable>
    <desc>Autopilot Toggle / HDG mode toggle / PITCH mode / ALT mode </desc>
   <binding>
      <condition><property>/autopilot/CENTURYIII/controls/roll/button-state</property></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/disconnect</property>
      <value>1</value>
     </binding>
    <binding>
      <condition><not><property>/autopilot/CENTURYIII/controls/roll/button-state</property></not></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/roll/button-on</property>
      <value>1</value>
     </binding>
      <binding>
      <command>nasal</command>
      <script>
       if ( autopilottype == 1 ) { 
         kap140.apButton();
         if ( getprop("autopilot/KAP140/locks/roll-mode") == 0 ) {
           gui.popupTip("AP off");
         } else {
           gui.popupTip("AP on");
         }
       } 
      </script>
    </binding>
    <mod-up>
      <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/disconnect</property>
        <value>0</value>
      </binding>
     <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/roll/button-on</property>
        <value>0</value>
      </binding>
        <binding>
        <command>nasal</command><script>
        if ( autopilottype == 2 ) { 
          <!-- CENTURYIII -->
          if ( getprop("autopilot/CENTURYIII/controls/roll/button-state") ) {
             gui.popupTip("AP ROLL on");
           } else {
             gui.popupTip("AP ROLL off");
           } 
         }
       </script>
       </binding>
    </mod-up> 
  <mod-shift> 
   <binding>
      <condition><property>/autopilot/CENTURYIII/controls/hdg/button-state</property></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/hdg/button-off</property>
      <value>1</value>
     </binding>
    <binding>
      <condition><not><property>/autopilot/CENTURYIII/controls/hdg/button-state</property></not></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/hdg/button-on</property>
      <value>1</value>
     </binding>
    <binding>
      <command>nasal</command>
      <script>
       if ( autopilottype == 1 ) { 
         kap140.hdgButton();
         if ( getprop("autopilot/KAP140/locks/roll-mode") == 2 ) {
           gui.popupTip("AP HDG");
         } elsif ( getprop("autopilot/KAP140/locks/roll-mode") == 1 )   {
           gui.popupTip("AP ROL");
         }
       } 
      </script>
    </binding>
   <mod-up>
      <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/hdg/button-off</property>
        <value>0</value>
      </binding>
     <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/hdg/button-on</property>
        <value>0</value>
      </binding>
      <binding>
      <command>nasal</command><script>
       if ( autopilottype == 2 ) { 
          <!-- CENTURYIII -->
         if ( getprop("autopilot/CENTURYIII/controls/hdg/button-state") ) {
           gui.popupTip("AP HDG on");
         } else {
           gui.popupTip("AP HDG off");
         }
        }
      </script>
     </binding>
    </mod-up> 
  </mod-shift>
  <mod-ctrl> 
   <binding>
      <condition><property>/autopilot/CENTURYIII/controls/pitch/button-state</property></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/pitch/button-off</property>
      <value>1</value>
     </binding>
    <binding>
      <condition><not><property>/autopilot/CENTURYIII/controls/pitch/button-state</property></not></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/pitch/button-on</property>
      <value>1</value>
     </binding>
   <mod-up>
      <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/pitch/button-off</property>
        <value>0</value>
      </binding>
     <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/pitch/button-on</property>
        <value>0</value>
      </binding>
        <binding>
        <command>nasal</command><script>
        if ( autopilottype == 2 ) { 
          <!-- CENTURYIII -->
          if ( getprop("autopilot/CENTURYIII/controls/pitch/button-state") ) {
             gui.popupTip("AP PITCH on");
           } else {
             gui.popupTip("AP PITCH off");
           } 
         }
        </script>
      </binding>
    </mod-up> 
  </mod-ctrl>
  <mod-alt> 
    <binding>
      <command>nasal</command>
      <script>if (autopilottype==1 ) {
       kap140.altButton();
       if ( getprop("autopilot/KAP140/locks/pitch-mode") == 2 ) {
         gui.popupTip("AP ALT");
       } elsif ( getprop("autopilot/KAP140/locks/pitch-mode") == 1 )   {
         gui.popupTip("AP VS");
       }
       }
    </script>
    </binding>
   <binding>
      <condition><property>/autopilot/CENTURYIII/controls/alt/button-state</property></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/alt/button-off</property>
      <value>true</value>
     </binding>
    <binding>
      <condition><not><property>/autopilot/CENTURYIII/controls/alt/button-state</property></not></condition>
       <command>property-assign</command>
      <property>autopilot/CENTURYIII/controls/alt/button-on</property>
      <value>true</value>
     </binding>
   <mod-up>
      <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/alt/button-off</property>
        <value>false</value>
      </binding>
     <binding>
        <command>property-assign</command>
        <property>autopilot/CENTURYIII/controls/alt/button-on</property>
        <value>false</value>
      </binding>
      <binding>
        <command>nasal</command>
        <script>
           if(autopilottype==2) {
              if ( getprop("autopilot/CENTURYIII/controls/alt/button-state")) {
                  gui.popupTip("AP ALT on");
              } else {
                  gui.popupTip("AP ALT off");
              }
           }
        </script>
     </binding>
    </mod-up> 
  </mod-alt>
</button>

Horizontal mode selection

Button 5 controls the source of course selection in HDG mode (for the LOC and APR modes, the vertical mode is also affected, of course). It works quite differently in the two autopilots:

  • KAP140: (no modifier) NAV mode: track radial of VOR1 beacon. Press again to go back to follow set heading

(SHIFT) APR mode: track ILS beacon (set radial to runway orientation). If glideslope is approached from below in ALT mode, lock on to localiser
(CTRL) REV mode: track reverse ILS direction

  • CENTURYIII: Move mode selector button to the right (clockwise)

(SHIFT) Move mode selector button to the left (anti-clockwise)
Both directions are wrapped, i.e. you can keep pressing the button until you arrive at the selected mode. In turn, the modes selected are: NAV, OMNI, HDG, LOC NORM, LOC REV. Please refer to the CENTURYIII manual for further explanation.

Initialisation:

     var showhorizontalmode=func() {
     if ( autopilottype == 1 ) {
       if ( getprop("autopilot/KAP140/locks/roll-arm") == 1 ) {
         gui.popupTip("AP NAV");
       } elsif ( getprop("autopilot/KAP140/locks/roll-arm") == 2 ) {
         gui.popupTip("AP APR");
       } elsif ( getprop("autopilot/KAP140/locks/roll-arm") == 3 ) {
         gui.popupTip("AP REV");
       }
     }
     if (autopilottype == 2) {
        horizontalmode=getprop("autopilot/CENTURYIII/controls/mode");
        if ( horizontalmode==0) {
          gui.popupTip("APmode NAV");
        } elsif ( horizontalmode==1) {
          gui.popupTip("APmode OMNI");
        } elsif ( horizontalmode==2) {
          gui.popupTip("APmode HDG");
        } elsif ( horizontalmode==3) {
          gui.popupTip("APmode LOC");
        } elsif ( horizontalmode==4) {
          gui.popupTip("APmode REV");
        } 
     }
    }

Button settings:

<button n="5">
    <repeatable type="bool">false</repeatable>
    <desc>KAP140: NAV / APR / REV ; CENTURYIII:
Move Hdg select button to the right (wrap)</desc>
    <binding>
           <command>property-adjust</command>
            <property>autopilot/CENTURYIII/controls/mode</property>
            <step type="double">1</step>
     <min>0</min>
      <max>5</max>    
      <wrap>true</wrap> 
    </binding>
    <binding>
      <command>nasal</command>
      <script>
       if (autopilottype==1) { 
         kap140.navButton(); }
       showhorizontalmode();
     </script>
    </binding>
  <mod-shift> 
    <binding>
           <command>property-adjust</command>
            <property>autopilot/CENTURYIII/controls/mode</property>
            <step type="double">-1</step>
     <min>0</min>
      <max>5</max>    
      <wrap>true</wrap> 
    </binding>
    <binding>
      <command>nasal</command>
      <script>       
        if (autopilottype==1) { kap140.aprButton();}
       showhorizontalmode();
      </script>
    </binding>
  </mod-shift>
  <mod-ctrl> 
    <binding>
      <command>nasal</command>
      <script>        if (autopilottype==1) { kap140.revButton();}
       showhorizontalmode();
     </script>
    </binding>
  </mod-ctrl>
</button>

Vertical mode pitch or vertical speed selector

Control the vertical mode.

  • KAP140: Button 6 increases target VS by 100 ft/min, button 7 decreases target VS by 100 ft/min
  • CENTURYIII: Button 6 smoothly lowers pitch (nose down), button 7 increases pitch (nose up)

The opposite way these buttons work for the two types of autopilot arises from the arrangement in the cockpit. For the CENTURYIII, the pitch is controlled by a wheel, which needs to be moved up to lower the nose (similar to elevator trim wheels), whereas for the KAP140 two buttons are arranged such that the top button increases target VS.

Initialisation:

     var showvertical=func()  {if ( autopilottype==1 ) {
            if ( getprop("autopilot/KAP140/locks/pitch-mode") == 1 )  {
               gui.popupTip(sprintf("AP VS
%d",getprop("autopilot/KAP140/settings/target-pressure-rate")*(-58000)));
            }
         } elsif ( autopilottype==2 ) {
            gui.popupTip(sprintf("AP Pitch:%4.1f",getprop("autopilot/CENTURYIII/settings/pitch-wheel-deg")));
         }}

Button settings:

  <button n="6">
<repeatable type="bool">false</repeatable>
    <desc>AP-VS (KAP140:increase VS, CENTURYII: pitch down</desc>
<!-- KAP140 -->
    <binding>
      <command>nasal</command>
      <script>if (autopilottype==1) {
         kap140.upButton();
      }
      showvertical(); 
     </script>
    </binding>
<!-- CENTURYIII -->
    <binding>
      <command>property-interpolate</command>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <value type="double">-10</value>
       <rate>4</rate>
     </binding>
     <mod-up>
    <binding>
      <command>property-interpolate</command>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <rate>1</rate>
     </binding>
    <binding>
      <command>nasal</command>
      <script>
        if(autopilottype==2) { showvertical(); } 
     </script>
    </binding>
     </mod-up>
  </button>


  #7: Autopilot VS down
  <button n="7">
<repeatable type="bool">false</repeatable>
    <desc>AP-dn</desc>
<!-- KAP140 -->
    <binding>
      <command>nasal</command>
      <script>if (autopilottype==1) {
         kap140.downButton();
      }
      showvertical(); 
     </script>
    </binding>
<!-- CENTURYIII -->
    <binding>
      <command>property-interpolate</command>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <value type="double">10</value>
       <rate>4</rate>
     </binding>
     <mod-up>
    <binding>
      <command>property-interpolate</command>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <property>/autopilot/CENTURYIII/settings/pitch-wheel-deg</property>
       <rate>1</rate>
     </binding>
    <binding>
      <command>nasal</command>
      <script>
        if(autopilottype==2) { showvertical(); } 
     </script>
    </binding>
     </mod-up>
  </button>

Horizontal mode direction selector

Adjust heading bug (button 8 clockwise and button 9 anti-clockwise).

With modifiers:
(SHIFT) coarse heading bug adjustment
(CTRL) adjust bank angle (CENTURY III only!)
(ALT) adjust radial of NAV1 (can also be used without autopilot)


Initialisation:

    var showhdgbug=func() {if ( autopilottype==1 ) {
                 gui.popupTip(sprintf("Heading Bug:%3d",getprop("autopilot/settings/heading-bug-deg"))); 
              } elsif ( autopilottype==2 ) {
                gui.popupTip(sprintf("Hdg Bug:%3d",getprop("instrumentation/kcs55/ki525/selected-heading-deg")));
              }}
     var showroll=func() { if ( autopilottype==2 ) {
                gui.popupTip(sprintf("AP Roll:%2d",getprop("autopilot/CENTURYIII/settings/roll-knob-deg")));}}
     var showcourseselect=func() {
                if ( autopilottype==1 ) {
                  gui.popupTip(sprintf("Course select:%2d",getprop("instrumentation/nav/radials/selected-deg")));
                } elsif ( autopilottype==2 ) {
                  gui.popupTip(sprintf("Course select:%2d",getprop("instrumentation/kcs55/ki525/selected-course-deg")));}}

Button definition:

<button n="8">
    <desc>Heading bug CW / Heading bug CW large steps /
(CENTURYIII:)Roll knob turn right / Course select CW</desc>
      <repeatable>true</repeatable>
    <binding>
            <command>property-adjust</command>
            <property>/autopilot/settings/heading-bug-deg</property>
            <step type="double">0.25</step>
    <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-heading-deg</property>
            <step type="double">0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
         <binding>
            <command>nasal</command>
             <script>
             showhdgbug();
          </script>
         </binding>
    <mod-shift>
        <binding>
            <command>nasal</command>
             <script>
             showhdgbug();
             </script>
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>/autopilot/settings/heading-bug-deg</property>
            <step type="double">2.5</step>
      <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-heading-deg</property>
            <step type="double">2.5</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
         <binding>
            <command>nasal</command>
             <script>
              showhdgbug();
          </script>
    </binding>
    </mod-shift>
   <mod-ctrl>
    <binding>
      <command>property-adjust</command>
       <property>/autopilot/CENTURYIII/settings/roll-knob-deg</property>
       <step type="double">0.5</step>
       <max>30</max>
     </binding>
     <binding>
            <command>nasal</command>
             <script>
             showroll();
          </script>
     </binding>
   </mod-ctrl>
 <mod-alt>
   <binding>
            <command>property-adjust</command>
            <property>instrumentation/nav/radials/selected-deg</property>
            <step type="double">0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-course-deg</property>
            <step type="double">0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
     <binding>
            <command>nasal</command>
             <script>
             showcourseselect();
          </script>
     </binding>
 </mod-alt>
  </button>


 <button n="9">
    <desc>Heading bug CCW / Heading bug CCW large steps</desc>
      <repeatable>true</repeatable>
    <binding>
            <command>property-adjust</command>
            <property>/autopilot/settings/heading-bug-deg</property>
            <step type="double">-0.25</step>
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-heading-deg</property>
            <step type="double">-0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
         <binding>
            <command>nasal</command>
             <script>
                 showhdgbug();
          </script>
         </binding>
    <mod-shift>
    <binding>
            <command>property-adjust</command>
            <property>/autopilot/settings/heading-bug-deg</property>
            <step type="double">-2.5</step>
      <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-heading-deg</property>
            <step type="double">-2.5</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
         <binding>
            <command>nasal</command>
             <script>
              showhdgbug();
          </script>
    </binding>
    </mod-shift>
   <mod-ctrl>
    <binding>
      <command>property-adjust</command>
       <property>/autopilot/CENTURYIII/settings/roll-knob-deg</property>
       <step type="double">-0.5</step>
       <min>-30</min>
     </binding>
     <binding>
            <command>nasal</command>
             <script>
                showroll();
          </script>
     </binding>
   </mod-ctrl>
 <mod-alt>
   <binding>
            <command>property-adjust</command>
            <property>instrumentation/nav/radials/selected-deg</property>
            <step type="double">-0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
    <binding>
            <command>property-adjust</command>
            <property>instrumentation/kcs55/ki525/selected-course-deg</property>
            <step type="double">-0.25</step>
     <min>000</min>    <!-- the property will never be allowed to go below 0 -->
      <max>360</max>    <!-- ...or above 360 -->
      <wrap>true</wrap> <!-- when we hit 361, wrap back to 0 and vice-versa -->
         </binding>
     <binding>
            <command>nasal</command>
             <script>
               showcourseselect();
          </script>
     </binding>
 </mod-alt>

Summary

For convenience all of the above bindings are summarised into one file accessible at pastebin (at the time of writing identical to the combination of the snippets above but of course the pastebin might not always keep up with the Wiki - beware)