<?xml version="1.0"?> <!-- This is X52.xml, the joystick config file for the Saitek X52 USB stick and throttle. This configuration is meant to be suitable for a generic single-engine aircraft. Here are some of the features of a real aircraft that we would like to implement, and our "preferred" way of assigning them on the X52. Flight Controls: Aileron stick L/R Elevator stick fwd/aft Rudder stick twist (or pedals!) Flaps T3/T4 Speedbrakes shift T3/T4 Pitch trim stick upper hat Rudder trim stick upper hat Aileron trim throttle rocker Engine controls: Throttle throttle Mixture throttle lower rotary Propeller RPM throttle thumb slider Carb heat (alt air) T5/T6 Cowl Flaps shift T5/T6 Fuel tank select **not implemented** Mag switch keyboard "{" and "}" Starter keyboard "s" Other important stuff: gear handle up/down T1/T2 Overall brakes pinky trigger L/R toe brakes keyboard dot and comma Parking brakes keyboard capital-B Push-to-talk button (A) is a good candidate also keyboard "space" Timer reset MultiFunc reset Timer -> clock MultiFunc function Autopilot disengage **not implemented** Autopilot go-around **not implemented** Electrical: Master **not implemented** Radio master **not implemented** Landing lights **not implemented** Rotating beacon **not implemented** Strobes **not implemented** Cabin lights **not implemented** Instrument lights **not implemented** Circuit breakers **not implemented** Instruments: Alternate static **not implemented** Instruments on/off mouse Instruments tuning mouse or popup dialog Simulator and view control: Pan L/R Tilt Up/Dn throttle lower hat Shift PoV D-shift + throttle lower hat Restore std view (C) button Zoom out (B) button Zoom in D-shift + (B) button ============================================================= Here are the Unix axes observed by jsd using js_demo, and Windows axes observed by Satia Lumbar. Mac axes are little more than guesses based on reports of X45 behavior. An "x" means observed to have no effect using js_demo. U/M/W means unix/mac/windows: U/ M/ W Hardware Function Interpretation ======= ================= ============== 0/ 0/ 0 Stick yaw +=R Aileron 1/ 1/ 1 Stick pitch +=aft Elevator 2/ 2/ 2 Throttle +=aft Throttle 3/ 5/ 5 Throttle lower rotary +=cw Mixture (fwd = rich) 4/ 8/ 8x Throttle upper rotary +=cw [unassigned] 5/ 3/ 3 Stick twist +=R Rudder 6/ 4/ 4 Throttle thumb slider +=up Prop (fwd = high RPM) 7/ 6/ 6 Stick lower hat +=R Pan View Left/Right ... or shift 8/ 7/ 7 Stick lower hat +=down Tilt View Up/Down ... or shift 9/ 9/ 9x Green mouse +=up [Mouse???] 10/10/10x Green mouse +=away [Mouse???] On the Windows platform, the assignment of axes 8, 9, and 10 as given above is only a hint, suggesting how the axes /should/ be assigned, if the lower-level implementation ever gets around to assigning them. In the meantime, I have maintained cross-platform compatibility by not assigning any critical functions to axes that don't work on the Windows platform. ======================= Now for the buttons. Unix button numbers observed by jsd using js_demo. (Others rumored to be identical on Unix/Mac/Windows.) Before 1 Jan 2007, all versions of plib had a bug that affecting bits 32 and 33. The C and C++ language spec says that an out-of-range shift (such as shifting a 32-bit word 32 or more places) produces an /undefined/ result. It is really not a good practice to be generating undefined results. I observe that on /some/ systems, bit #32 creates a conflict with bit #0, and bit #33 creates a conflict with bit #1. That is, they "wrap around" the word boundary. This means that if you roll the gray trim wheel, it fires the gun! If that weren't bad enough, this behavior is not guaranteed from compiler to compiler or from hardware to hardware. Remember, according to the C language spec, an out-of-range shift produces an "undefined" result, so implementers can do anything they want with it. Bit Binary Hardware Function Interpretation === ========= ================= ==================== 0 = 0000 0001 Trigger (see also #14) 0' (see also #32) 1 = 0000 0002 FIRE Button (guarded) 1' (see also #33) 2 = 0000 0004 (A) Button (should be push-to-talk) 3 = 0000 0008 (B) Button Expand field of view (shift: contract) 4 = 0000 0010 (C) Button Reset View 5 = 0000 0020 Pinky subtrigger Apply brakes 6 = 0000 0040 (D) Button D-Shift, modifies other functions 7 = 0000 0080 (E) Button 8 = 0000 0100 T1 Gear Handle Up 9 = 0000 0200 T2 Gear Handle Down 10 = 0000 0400 T3 Flaps retract 1 notch (shift: speed brakes extend) 11 = 0000 0800 T4 Flaps extend 1 notch (shift: speed brakes retract) 12 = 0000 1000 T5 Carb heat on (shift: cowl flaps close) 13 = 0000 2000 T6 Carb heat off (shift: cowl flaps open) 14 = 0000 4001 More Trigger (==> #0) 15 = 0000 8000 Yoke upper hat Fwd Elevator trim 16 = 0001 0000 Yoke upper hat R Rudder trim 17 = 0002 0000 Yoke upper hat Aft Elevator trim 18 = 0004 0000 Yoke upper hat L Rudder trim 19 = 0008 0000 Throttle rocker aft 20 = 0010 0000 Throttle rocker R Aileron trim 21 = 0020 0000 Throttle rocker fwd 22 = 0040 0000 Throttle rocker L Aileron trim 23 = 0080 0000 Mode Aft (green) 24 = 0100 0000 Mode Mid (pink) 25 = 0200 0000 Mode Fwd (red) 26 = 0400 0000 MultiFunc Function Timer -> clock again 27 = 0800 0000 MultiFunc Start/Stop ** Timer start/stop not implemented ** 28 = 1000 0000 MultiFunc Reset Timer reset 29 = 2000 0000 (i) Button 30 = 4000 0000 Green mouse click 31 (not observed) 32 = 1 0000 0000 Gray Roller step fwd (see also bit #0) 33 = 2 0000 0000 Gray Roller step aft (see also bit #1) == Notes: *) Note that some joystick configurations specify square-law behavior for some flight control axes (e.g. ailerons and/or rudder). This is very unlike the behavior of flight controls on real aircraft. Aileron authority should be mushy at low airspeeds and crisp at high airspeeds ... but this should be part of the flight dynamics of the aircraft. Building the correct behavior into the control stick configuration is neither possible nor desirable. *) There are some aircraft in the simulator fleet that have very little roll damping and very little yaw damping. (You could also say they have very little damping of the Dutch roll mode, but this is merely a natural consequence of the roll and yaw issues, not really a separate issue.) Such aircraft are vastly easier to fly with auto-coordination turned on. In my opinion, you want auto-coordination on for flying an ILS partial panel, i.e. no DG and no AI. For a crosswind landing, you need to have auto-coordination turned off. That's why the switch to do that is on the yoke. *) This configuration does not bind the parking brake to any button on the joystick (or throttle). Using the keyboard (shift-B) seems entirely satisfactory and more realistic than putting the /parking/ brake button on the stick. *) In general, he HOTAS idea (hands on throttle and stick) should apply to things you need /in flight/. --> <PropertyList> <name>Saitek X52</name> <name>Saitek X52 Flight Controller USB</name> <name>Saitek X52 Flight Controller</name> <name>Saitek X52 Flight Control Stick </name> <name>Saitek X52 Flight Stick (USB)</name> <name>Saitek X52 Flight Control System</name> <name>Saitek Saitek X52</name> <name>Saitek Saitek X52 Flight Stick (USB)</name> <name>Saitek Saitek X52 Flight Control Stick</name> <name>Saitek Saitek X52 Flight Control System</name> <axis> <!-- 0/0/0 --> <desc>Aileron</desc> <number> <unix>0</unix> <mac>0</mac> <windows>0</windows> </number> <binding> <command>property-scale</command> <property>/controls/flight/aileron</property> </binding> </axis> <axis> <!-- 1/1/1 --> <desc>Elevator</desc> <number> <unix>1</unix> <mac>1</mac> <windows>1</windows> </number> <binding> <command>property-scale</command> <property>/controls/flight/elevator</property> <factor type="double">-1.0</factor> </binding> </axis> <axis> <!-- 2/2/2 --> <desc>Throttle</desc> <number> <unix>2</unix> <mac>2</mac> <windows>2</windows> </number> <binding> <command>nasal</command> <script>controls.throttleAxis()</script> </binding> </axis> <axis> <!-- 3/5/5 --> <desc>Mixture</desc> <number> <unix>3</unix> <mac>5</mac> <windows>5</windows> </number> <binding> <command>nasal</command> <script>controls.mixtureAxis(-1)</script> </binding> </axis> <!-- Alternate Prop RPM :: throttle upper rotary --> <axis> <!-- 4/8/8 --> <desc>unassigned (was: Propeller RPM)</desc> <number> <unix>4</unix> <mac>8</mac> <windows>8</windows> </number> <binding> <command>nasal</command> <script> ####controls.propellerAxis(-1) </script> </binding> </axis> <!-- Rudder :: stick twist --> <axis> <!-- 5/3/3 --> <desc>Rudder</desc> <number> <unix>5</unix> <mac>3</mac> <windows>3</windows> </number> <binding> <command>property-scale</command> <property>/controls/flight/rudder</property> </binding> </axis> <!-- Propeller RPM :: throttle thumb slider --> <axis> <!-- 6/4/4 --> <desc>Propeller RPM</desc> <number> <unix>6</unix> <mac>4</mac> <windows>4</windows> </number> <binding> <command>nasal</command> <script>controls.propellerAxis(-1)</script> </binding> </axis> <!-- View Direction :: stick lower hat --> <axis> <!-- 7/6/6 --> <desc>View Pan Left/Right (D-shift: move PoV)</desc> <number> <unix>7</unix> <mac>6</mac> <windows>6</windows> </number> <low> <repeatable>true</repeatable> <binding> <command>nasal</command> <script> if (getprop("/sim/gui/d-button")){ setprop("/sim/current-view/x-offset-m", -0.01 + getprop("/sim/current-view/x-offset-m")); } else { view.panViewDir(1) } </script> </binding> </low> <high> <repeatable>true</repeatable> <binding> <command>nasal</command> <script> if (getprop("/sim/gui/d-button")){ setprop("/sim/current-view/x-offset-m", 0.01 + getprop("/sim/current-view/x-offset-m")); } else { view.panViewDir(-1) } </script> </binding> </high> </axis> <axis> <!-- 8/7/7 --> <desc>View Tilt Up/Down (D-shift: move PoV)</desc> <number> <unix>8</unix> <mac>7</mac> <windows>7</windows> </number> <low> <repeatable>true</repeatable> <binding> <command>nasal</command> <script> if (getprop("/sim/gui/d-button")){ setprop("/sim/current-view/y-offset-m", 0.01 + getprop("/sim/current-view/y-offset-m")); } else { view.panViewPitch(1) } </script> </binding> </low> <high> <repeatable>true</repeatable> <binding> <command>nasal</command> <script> if (getprop("/sim/gui/d-button")){ setprop("/sim/current-view/y-offset-m", -0.01 + getprop("/sim/current-view/y-offset-m")); } else { view.panViewPitch(-1) } </script> </binding> </high> </axis> <!-- End of axes; now on to buttons --> <button n="1"> <desc>(FIRE) : Auto-Coordination off (D-shift: on)</desc> <binding> <command>nasal</command> <script> setprop("/sim/auto-coordination", ! !getprop("/sim/gui/d-button")); </script> </binding> </button> <button n="3"> <desc>(B) : Zoom out i.e. increase field of view (D-shift: zoom in)</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { view.decrease(0.5) # zoom in } else { view.increase(0.5) # zoom out } </script> </binding> </button> <button n="4"> <desc>(C) : Reset View</desc> <binding> <command>nasal</command> <script> view.resetView(); # only resets tilt/pan/zoom: # must reset x/y/z view point separately vn = getprop("/sim/current-view/view-number"); conf = sprintf("/sim/view[%d]/config", vn); foreach (parm ; ["x-offset-m", "y-offset-m", "z-offset-m"]) { setprop("/sim/current-view/", parm, getprop(conf, parm)); } </script> </binding> </button> <!-- Main brakes (not parking brakes) :: pinky subtrigger --> <button n="5"> <desc>(Pinky) : Brakes</desc> <binding> <command>nasal</command> <script>controls.applyBrakes(1)</script> </binding> <mod-up> <binding> <command>nasal</command> <script>controls.applyBrakes(0)</script> </binding> </mod-up> </button> <!-- Shift :: (D) button --> <button n="6"> <desc>(D) : Shift Key</desc> <binding> <command>nasal</command> <script> setprop("/sim/gui/d-button", 1); </script> </binding> <mod-up> <binding> <command>nasal</command> <script> setprop("/sim/gui/d-button", 0); </script> </binding> </mod-up> </button> <!-- gear handle up/down :: button T1/T2 --> <button n="8"> <desc>(T1) : Landing Gear Handle Up</desc> <binding> <command>nasal</command> <script> controls.gearDown(-1) </script> </binding> </button> <button n="9"> <desc>(T2) : Landing Gear Handle Down</desc> <binding> <command>nasal</command> <script> controls.gearDown(1) </script> </binding> </button> <!-- Pitch trim :: stick upper hat Up/Dn --> <button n="15"> <desc>Elevator trim down</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.elevatorTrim(0.6)</script> </binding> </button> <button n="17"> <desc>Elevator trim up</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.elevatorTrim(-0.6)</script> </binding> </button> <!-- Rudder trim :: stick upper hat L/R --> <button n="16"> <desc>(Throttle Rocker) : Rudder trim right</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.rudderTrim(1)</script> </binding> </button> <button n="18"> <desc>(Throttle Rocker) : Rudder trim left</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.rudderTrim(-1)</script> </binding> </button> <!-- Flaps (shift: Speed Brakes) :: T3/T4 --> <button n="10"> <desc>(T3) : Decrease flaps (shift: Speed Brakes extend)</desc> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { setprop("/controls/flight/speedbrake", 1); } else { controls.flapsDown(-1) } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { } else { controls.flapsDown(0) } </script> </binding> </mod-up> </button> <button n="11"> <desc>(T4) : Increase flaps (shift: Speed Brakes retract)</desc> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { setprop("/controls/flight/speedbrake", 0); } else { controls.flapsDown(1) } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { } else { controls.flapsDown(0) } </script> </binding> </mod-up> </button> <!-- Carb Heat (shift: Cowl Flaps) :: T6/T5 --> <button n="12"> <desc>(T5) : Carb Heat On (shift: Cowl Flaps Close)</desc> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { props.setAll("/controls/engines/engine", "cowl-flaps-norm", 1); } else { props.setAll("/controls/engines/engine", "carb-heat", 0); } </script> </binding> </button> <button n="13"> <desc>(T6) : Carb Heat Off (shift: Cowl Flaps Open)</desc> <binding> <command>nasal</command> <script> if ( getprop("/sim/gui/d-button") ) { props.setAll("/controls/engines/engine", "cowl-flaps-norm", 0); } else { props.setAll("/controls/engines/engine", "carb-heat", 1); } </script> </binding> </button> <!-- Aileron trim :: throttle rocker --> <button n="20"> <desc>Aileron trim right</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.aileronTrim(0.5)</script> </binding> </button> <button n="22"> <desc>Aileron trim left</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>controls.aileronTrim(-0.5)</script> </binding> </button> <!-- Mode roller switch (buttons 23-25). --> <!-- Just put it in the property tree, --> <!-- in case somebody wants to look at it. --> <button n="23"> <desc>Mode 1</desc> <binding> <command>nasal</command> <script> setprop("/input/joysticks/js/saitek-x52-mode",1) </script> </binding> </button> <button n="24"> <desc>Mode 2</desc> <binding> <command>nasal</command> <script> setprop("/input/joysticks/js/saitek-x52-mode",2) </script> </binding> </button> <button n="25"> <desc>Mode 3</desc> <binding> <command>nasal</command> <script> setprop("/input/joysticks/js/saitek-x52-mode",3) </script> </binding> </button> <button n="26"> <desc>(MFD Func) : Stopwatch timer becomes clock again</desc> <binding> <command>nasal</command> <script> props.globals.getNode("/instrumentation/clock/offset-sec", 1).setValue(0); </script> </binding> </button> <button n="28"> <desc>(MFD Reset) : Stopwatch timer reset</desc> <binding> <command>nasal</command> <script> <!-- Note that the "indicated-sec" variable is a large number, probably seconds since midnight ... *not* modulo 60. --> ttt = props.globals.getNode("/instrumentation/clock/indicated-sec", 1).getValue(); node = props.globals.getNode("/instrumentation/clock/offset-sec", 1); off = node.getValue(); node.setValue(off-ttt); </script> </binding> </button> <!-- (i) button :: unbound key --> <!-- just put it in the property tree --> <button n="29"> <desc>Mode 1</desc> <binding> <command>nasal</command> <script> setprop("/input/joysticks/js/saitek-x52-i",1) </script> </binding> <mod-up><binding> <command>nasal</command> <script> setprop("/input/joysticks/js/saitek-x52-i",0) </script> </binding></mod-up> </button> <!-- Parking brake :: unbound function --> <button n="666"> <desc>Toggle parking brake on or off</desc> <binding> <command>property-toggle</command> <property>/controls/gear/brake-parking</property> </binding> </button> </PropertyList>