Gauge Development Notes & Reference

Developing custom gauges for the Saitek FIPs is quite challenging and is not for the faint of heart; not because they are difficult to design and create in themselves, but because the implementation of the X-Plane plugin is quite buggy and because you don't get any feedback to debug from; even perfectly valid changes can cause your gauge to either not display at all or in some cases crash and require a complete system reboot.

Creating these gauges is a process of trial and error, and given that you need to wait for X-Plane to fully load every time you make a change it is also very time consuming. On the flip side it is also very rewarding to bring your idea for custom avionics to life and, for me at least, this makes all the frustration and wasted time worthwhile.

Gauge Definitions


There is one main XML file called XSaitekProFlight.xml which resides in the C:\X-Plane 10\Resources\plugins\XSaitekProFlight\Data directory and contains the list of gauges you want to use as well as mappings for the buttons and dials on the FIP.

The default XSaitekProFlight.xml file serves as a good guide; the root element of this file is a <GaugeList> with each gauge you want to be selectable listed in a <Gauge> child element. Each <Gauge> definition here needs to contain six <Button> elements with Id attributes set in sequence from 1 to 6. In addition you can also add four more buttons with Ids set to 7, 8, 11 and 12 - 7 & 8 trigger when rotating the left hand dial/bug on the FIP and 11 & 12 trigger on the right hand dial/bug in the same way.

Each <Button> definition can be made to trigger various event types, as you will see from the comment at the top of the default XSaitekProFlight.xml file, but the "command" type is the most useful, allowing you to trigger any X-Plane command you wish. Set the Event attribute to the CommandRef that you would like to trigger when the button is activated. Setting the Name attribute sets what text should appear on the FIP to the right of the button - Unlike the FSX incarnation of this system, you can't turn the buttons or their LEDs on and off, and the value of the Visible attribute doesn't seem to have any effect at all.

In addition to defining the button actions, each <Gauge> element will also link to a separate XML file which actually defines the gauge itself. You can name your gauges whatever you like, just remember to include the name in the appropriate <Gauge> element so that it is loaded the next time you start X-Plane.

Alongside these gauge.xml files you will also find a directory named "1024". I'm not sure why it's named 1024, it just is. This is where all your resource images go. These images are then referenced in the gauge.xml files in order to create and animate your gauge. As noted elsewhere, the images must be in Windows Bitmap (.bmp) format, must be at 24-bit colour depth and should have the background colour set (even if it's not used) to rgb(1,1,1) or #010101.

Grouping gauges into folders

You can continue to fill your Gauges directory with gauge.xml files, and your 1024 folder will continue to fill with more and more resource images - if you're going to do things this way I'd advise that you prefix all the filenames of your resource images with the name of your gauge - but there is a better way.

Instead of creating your gauge,xml file directly in the Gauges directory, create a new subdirectory for each of your gauges and then put the gauge.xml file in that directory, and create a 1024 subdirectory for your resource images. Doing things this way allows you to keep your resource images separate, which will make it much easier to share your gauges with others as well as making it easier for you to maintain if you have several gauges. All you need to do differently is to insert the relative path into the XSaitekProFlight.xml file's <Gauge> definition. 

Gauge Functional Logic: Expressions

The first thing that will (might - not everyone is me!) grab you when you look at one of the XML files is the Reverse Polish Notation in the <Value> elements. I must admit that RPN was something I learned in the before times (Well, first year of college anyway), never used and ultimately forgot all about, and if you're not used to seeing RPN it looks like someone got an equation in a box, shook all the symbols around a bit and then dumped them on the table.

From a programming perspective it makes sense to use RPN, as doing so is quicker to decode into instructions the computer can understand and we want these FIPs to use as few resources as possible; the same goes for the lack of error checking, but unfortunately that leaves hackers like us a bit in the dark when we're trying to figure these things out!

We can apply this logic to any elements we want to animate using expressions (and it'd be a pretty useless gauge if it never moved) which are generally placed in <Value> elements. If we make a mistake in en expression, either the element we're trying to animate just won't appear, or (more often) it will appear but won't animate. In the more extreme cases your whole gauge won't appear at all, but this tends to occur more often if you have one of your images at the wrong colour depth.

In terms of logic, we have various tools available - I have been through the Expression Evaluation section here and tried a good many things out; Below is a list of these expression operators for convenience (I have noted which I have used and know to be working):

Common Arithmetic Operators
OperatorOperationNumber of ArgumentsExampleExample ResultUsage Notes
+Addition26 5 +11Tested, works OK
-Subtraction26 5 -1Tested, works OK
/Division26 5 /1.2Tested, works OK
divInteger Division26 5 div1Tested, works OK
*Multiplication26 5 *30Tested, works OK
%Modulo (Integer value from Float)13.141 %3Tested, works OK
/-/Negation16 /-/-6Tested, works OK
negNegation16 neg-6Tested, works OK
Comparison Operators (use with if{} function)
OperatorOperationNumber of ArgumentsExampleExample ResultUsage Notes
==Equal To25 5 ==TrueTested, works OK
!=Not Equal To26 5 !=TrueTested, works OK
<Less Than26 5 <FalseNot Yet Tested - not valid within XML as is
<=Less Than or Equal To26 5 <=FalseNot Yet Tested - not valid within XML as is
>Greater Than26 5 >TrueNot Yet Tested - not valid within XML as is
>=Greater Than or Equal To26 5 <TrueNot Yet Tested - not valid within XML as is
?Inline If - first argument is used if third argument is True, otherwise second argument is used36 5 True ?5Not Yet Tested - not valid within XML as is
Logic Operators
OperatorOperationNumber of ArgumentsExampleExample ResultUsage Notes
!NOT1True !FalseNot Yet Tested
NOTNOT1True NOTFalseNot Yet Tested
||OR2True False ||TrueNot Yet Tested
OROR2True False ORTrueNot Yet Tested
&&AND2True False &&FalseNot Yet Tested
ANDAND2True False ANDFalseNot Yet Tested
Bitwise Operators
OperatorOperationNumber of ArgumentsExampleExample ResultUsage Notes
~Bitwise NOT15 ~-6Not Yet Tested
|Bitwise OR11 2 |3Not Yet Tested
&Bitwise AND11 2 |0Not Yet Tested
^Bitwise XOR15 3 ^6Not Yet Tested
>>Bit Shift Right (by 2nd parameter bits)18 2 >>2Not Yet Tested
<<Bit Shift Left (by 2nd parameter bits)18 2 <<32Not Yet Tested

The expression is evaluated as a stack, with spaces separating each operator or symbol. If a given item takes no parameters, its value is simply pushed on to the stack. If one or more parameter is required for a given item, the relevant number of items are popped from the stack, used to perform the operation, and the result of this operation is then pushed back on to the stack. Whatever value remains is used as a value. This is an important concept to grasp, as it makes understanding these RPN expressions much easier.

My advice when figuring out your logic is to try things in baby steps; alter one parameter at a time and test it thoroughly to try and figure out what's happening and what you can do to achieve what you want.

Gauge Creation Gotchas


I've ran into various problems and discoveries along the way, and to save everyone some time I've listed the main points to bear in mind when developing new gauges (and modifying existing ones). Please note that these are guidelines to make things easier - consider this a list of good practices to prevent headaches rather than a list of absolute requirements:
  • XML Implementation
    • Be careful using <!-- comments -->. XML within comments is not escaped properly by the parser somewhere - something that took me a while to work out. I have yet to narrow it down to an exact behaviour or problem, and in some cases I have successfully used these comments even to "comment out" XML I wanted to temporarily disable, but after various multi-hour headaches only to discover that removing a comment fixes the issue. Commenting the files does make them much easier to understand, however, so I would recommend using short, simple comments on their own line which contain no special characters in order to minimise the likelihood of the plugin parsing one of your comments as normal XML.
    • In order to boost performance (I assume) there is very little by way of error checking, so be very careful to ensure your XML syntax is correct. Instead of an error message or a predictable behavior, you can get the most random and inexplicable results, varying from your gauge just being ignored all the way to crashing X-plane and needing a reboot.
    • Make sure you set ImageSizes="320 240 320 240" in your background image if you want to use the whole screen area. The size of your background defines the useable size of your gauge - if you only have a 240x240 background, any elements drawn on top of it will be clipped to the edges of that background.
  • Side Buttons (S1-S6)
    • You can turn the buttons off; simply omit the <Button> element from your gauges' declaration in XSaitekProFlight.xml
    • Omit the Name="" attribute from all <Button> elements within a gauge declaration if you want to use the full screen. 
  • Image Format (note I am using GIMP; you may find differences using other image editing software)
    • Your images must be in Windows Bitmap (.bmp) format
    • You must save in 24-bit colour. 32-bit colour will cause your gauge to display completely blank.
    • When saving your BMP files, ensure your background colour is set to rgb(1,1,1) or #010101. You don't actually need any pixels of your image to be this colour, but as BMPs store the foreground and background colours regardless of if they are used or not you will need to ensure this background colour is set in your editor when you save.
    • When exporting to BMP with GIMP, you need to select "Do not save colourspace information" as well as "24-bit colour".
    • Mask images should use colour rgb(1,1,1) or #010101 as its masking colour - that is to say that any pixels in your mask image that are this colour will be transparent.
    • Your background image needs to be 239x239 pixels, not 240x240 - not sure why this is as you can use the extra pixels using normal elements, but bear this in mind for background images
    • Avoid using "hard pixel colours"; make use of blurring and antialiasing in order to surround individual colour pixels with blended colours. If you don't do this, your end result will be "pixelated" and won't look smooth, especially when rotated.
    • Avoid including areas of solid grey - the clock on the PWM that controls the brightness of pixels seems to be pretty low, so using lots of greys makes the screen flicker.

No comments:

Post a Comment

Please feel free to leave comments, add questions, correct my errors, leave handy hints, suggest additions, request new gauges and so forth - No abuse please, and no flaming each other!