This tutorial is for an older version of X-Plane. Newer versions of X-Plane have an updated format for the UDP packets, and different port binding requirements. If you're not using X-Plane 6, please go to the index to check for a version of this tutorial for newer versions of X-Plane. The newer tutorials also include revised explanations of some general principles, such as single precision floating point numbers, a brief explanation of double precision floating point numbers, and an improved method of converting from bytes to floating point values and vice versa.
X-Plane UDP Index
If you don't know what any part of this page's title means, you might not be in the right place. But don't worry, I'll explain it all. X-Plane is a popular computer flight simulator. It has a very accurate flight model, making it very powerful not only for entertainment, but also as an engineering tool. It comes packaged with additional programs that allow you to design your own aircraft and fly it in X-Plane. Besides its accurate flight model, X-Plane has another feature which makes it very powerful- it outputs flight data over a network, and allows certain parameters, such as control positions, to be sent back to it. The protocol that X-Plane uses to send/receive the data is UDP, hence the UDP in the title above. As for the Visual Basic, well, to be able to make use of the data you have to have a program to do something with it, and Visual Basic is the programming language I use. If you still don't understand any of that, go check out the X-Plane website. From there, you can download a demo version of the simulator, and see what makes it so great.
This tutorial was written using X-Plane 6.51. I believe this is the earliest version of X-Plane that allows you to send data back to X-Plane using UDP, while earlier versions only allowed X-Plane to send the data one way to let another computer "listen" to the X-Plane data. Newer versions of X-Plane have an updated format for the UDP packets. Please refer to the X-Plane UDP Index to check for a version of this tutorial for newer versions of X-Plane.
One more thing before we get started. You can download some source code to see all of this in context in a program while your reading.
Download source code
Now to get down to it. There's already a great site for info on UDP and X-Plane. It is http://www.x-plane.info. I'm not going to try and repeat everything that's already on that site. The reason I'm writing this page, well, actually two reasons, are because I didn't see anything on that site that dealt with Visual Basic, and because that site assumed you already had some network programming experience. Well, when I first started trying to play around with this stuff, I'd never programmed anything that dealt with a network, so I had to figure it all out by scouring web sites looking for the relevant information. Hopefully, by me putting all this info in one spot, someone else in the same situation that I was will read this and save some time. By the way, I'm assuming that you at least know how to program in Visual Basic.
WinSock
To start off, you'll need to add a WinSock component to your program. WinSock stands for Windows Sockets. It is the interface between programs and the network. We'll use the suppport in Visual Basic for our program. First, go to the Project menu and select Components. In that window, scroll down until you get to Microsoft Winsock Control 6.0, and click the check box. Then click OK. A little icon will appear on your tool bar that looks like two computers connected together. Click it, and then add one anywhere on your form.
There are four parameters that we have to set on the Winsock control- the local port, remote port, remote host, and most importantly- the protocol. First, set the protocol to 1 (UDP). The other protocol (0) is TCP, which we won't use. Set the local and remote ports to 49000. These are the defaults used by X-Plane. Set the RemoteHost to whatever the IP address is of the machine that will be running X-Plane.
For our purposes, there are one event and two methods that we need to be concerned with. The event is _DataArrival. This occurs whenever a UDP packet is sent from the IP address at the port specified. Since I named my winsock control WinsockUDP, the subroutine for this event is:
Sub WinsockUDP_DataArrival(ByVal bytesTotal As Long)
End Sub
Now, whenever a packet of data is sent, this subroutine will be run. The methods that we need to be concerned with are .GetData and .SendData. Their purposes are pretty much self explanatory. They're very easy to use. When you receive data, you need to store it to a variable, so use code that looks about like:
WinsockUDP.GetData VariableName
If you want to send a variable, use code that looks about like:
WinsockUDP.SendData VariableName
Just a small note now before I get into the explanation of UDP packets- I declare the variable that I'm going to use to store the data as a byte array. I also do this for the variable that I use to store the data that I'm going to send. I do this because UDP packets can only send bytes. But I'm starting to get ahead of myself. Lets start looking at UDP packets.
UDP
This is the section of the tutorial that I think will be the most help, because it's the section that I had the most trouble with, myself. UDP stands for Universal Data Packet. It is a method of sending information over a network. We don't really need to be concerned with all the details of how it works, but there are a few things that we need to know. First, unlike other protocols, like TCP, UDP does not do any error checking. If we send a packet, and the other computer gets the wrong data, or doesn't even get any data at all, we have no way of knowing, unless we program our own error checking into the program. But since we can't really change the code of X-Plane, we're kind of stuck on that. This really shouldn't be much of a problem, especially if you're going to be transmitting over a local network, but it could be the cause of a hard to track down problem.
The next important thing to know is that UDP packets are composed entirely of bytes, as are all packets sent over networks, and even everything stored on your hard drive. It has to be this way- computers only work with ones and zeros. To get decimals, you have to do a bit of math on the bytes that you've stored. To get letters and other symbols, you have to know the ASCII code for that symbol, to translate the byte into the symbol. X-Plane uses what are known as single precision floating point variables. This means that the number can be stored using four bytes. A double precision floating point variable would require eight bytes. So let's take a look at how to convert those four bytes into a number.
Single Precision Floating Point Numbers and Bytes
A single precision floating point number is stored as 4 bytes. Let's just use this as an example:
66 246 64 0
The first step is to convert the variables to binary. Always use an eight digit binary number. Use zeros if you have to:
01000010 11110110 01000000 00000000
Now from this list of digits, we need to get three numbers. So first, combine all the digits into one long list:
01000010111101100100000000000000
Now we need to redo where the breaks are. Do it like so- 1 digit, 8 digits, 23 digits:
0 10000101 11101100100000000000000
The first digit is the sign digit. It tells us whether the number is positive or negative (0 for positive, 1 for negative). Since it is a zero here, our number is positive. The second number is called the biased exponent. It is a biased exponent, because it is 127 more than the actual exponent. So, just convert it from binary into decimal, and then subtract 127:
Biased Exponent = 133 - 127 = 6
Now there're those last 23 digits. These are called the mantissa, or fraction. Basically, it's still a number in binary, only they're the digits that come after a decimal place. The conversion back to decimal is the same, only now you're using negative powers of two. The people who invented this method assumed that there would always be a 1 plus the decimal, so they didn't include the digit for the one. The first digit in the mantissa is the first digit after the decimal point (binary point?). So to convert the above series of digits into the number that we're going to use, do the following, where the numbers in bold are the digits in the binary sequence:
Mantissa = 1 + (1 * 2^-1) + (1 * 2^-2) + (1 * 2^-3) + (0 * 2^-4) + ... + (0 * 2^-23)
Mantissa = 1.923828125
Now that we have the sign, the biased exponent, and the mantissa, we're ready to calculate the value. It is of the following form, remembering that we have to make it positive or negative depending on the sign:
Value = Mantissa * 2^Biased Exponent
Value = 1.923828125 * 2^6
Value = 123.125
There, we've just calculated a value from four bytes. To calculate the four bytes that represent a given value, you just have to go through the above calculations in reverse.
One more thing that's really important to mention here, is that there is a slight difference between the way PCs and Macs do this algorithm. A Mac does it exactly the same way as described above. A PC throws a slight twist in there, by reversing the order of the four bytes. So, our number of 123.125 would be represented on a Mac as:
66 246 64 0
while on a PC it would be represented as:
0 64 246 66
Just keep this in mind when you're writing your program.
Integers and Bytes
Calculating an integer from four bytes is pretty similar to calculating a floating point variable. You take all four bytes, convert them into four 8-digit binary numbers, and combine them into one long 32 digit binary number. Remember to use the proper Mac/PC convention for the order that the bytes are in. Once you have the 32 digit number, the first digit controls the sign of the number. If it is a zero, the number is positive. If it is a one, the number is negative.
Calculating a positive integer is easy. Just convert the 31 digit binary number into a decimal number. However, a negative number is a bit different, because early computer engineers wanted to solve the problem of finding an easy way to do subtraction. What they came up with is called "2's complement." It's really pretty simple- just invert all the bits and add 1. So as an example, 3 would be represented in binary as:
0 0000000 00000000 00000000 00000011
Note that this is just 11 (bin), with a whole bunch of leading zeros, and a zero in the sign bit. Negative 3 would be:
1 1111111 11111111 11111111 11111100 + 1
1 1111111 11111111 11111111 11111101
An X-Plane specific note: if the only integers you're going to deal with are the index numbers, then you only need to look at either the last byte for a Mac, or the first byte for a PC. And in this case, there's no need to go through the steps of converting to binary and back to decimal, since you know the first three bytes (or last for PC) are going to be zero, and the other byte's going to be the number.
If you're interested as to the reason why computer engineers decided to use 2's complement, here's the explanation. Say you wanted to perform the function 5-3. Well, this would be the same as 5 + -3. So, we can use 2's complement of 3 to do the addition. Remember that in binary, 1 + 1 = 10.
0 0000000 00000000 00000000 00000101
+1 1111111 11111111 11111111 11111101
------------------------------------
10 0000000 00000000 00000000 00000010
Since the computer can only handle eight digit bytes, that leading 1 goes into overflow, so we're left with:
0 0000000 00000000 00000000 00000010
This converts to 2 in decimal, so we can see that the math did come out correctly. You don't really need to know this theory for X-Plane, but it is nice to know why it's done the way it is.
Letters and Symbols and Bytes
This is a lot simpler than the conversion between a floating point number and the four bytes. Letters and symbols each correspond to an integer number. The number is between 0 and 255, so it can be represented as a byte. Here is a list of all the symbols and their corresponding code, in MS Word format. So, to convert the letter "A" to a byte, we just look up what its code is, and find that it is 65. Note that a lower case "a" is different from an uppercase "A". A lowercase "a" would be 97. If you look at the above list, you'll also notice that each of the digits has a code. This code is different from the digit itself. For example, a "9" is 57. This is because in this format, the digits are being represented as strings, not as numbers.
Handling UDP Packets in Visual Basic
Since all the information sent via UDP is in bytes, it makes sense to use a byte variable to handle it. And since there are a lot of bytes being sent, we should use an array. Here is the way I declared the array to handle incoming data:
Dim PacketData() As Byte
Now, when we read the packet data in, using the code mentioned earlier in this tutorial:
WinsockUDP.GetData PacketData
Each of the bytes is stored into an element in the array PacketData. If we know something about the format of the data being sent, we can decode it into the variables that we need.
Similarly, when we're making up a data packet to send out, it's useful to define it as a byte array. Then, we convert all of our values into the proper bytes, and send out the packet:
WinsockUDP.SendData PacketData
Breaking Down an X-Plane UDP Packet
Now, let's take a look at what a UDP packet being sent from X-Plane looks like. This is another place where I got a little lost looking at the information on x-plane.info. But once I figured out everything was in bytes, it made a lot more sense. A UDP packet contains a header with some network information, but Visual Basic does not import that into the program when we use the .GetData command. So, we only see the body part of the packet. When I talk about packets in Visual Basic, that is what I'm referring to. A typical DATA packet being sent out from X-Plane may look something like:
68 65 84 65 0 48 33 0 0 0 76 87 67 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48
So what does all this mean? The first four bytes are a code to tell what type of packet it is. These are actually ASCII codes, so we convert each of those bytes into a symbol. The bytes 68,65,84,65 correspond to D,A,T,A, respectively, so we know this is a DATA packet.
The next byte is 0. This is just the convention that's used in X-Plane.
The next byte is an ASCII code, used to tell which platform X-Plane is running on. If it is 48 ("0"), it is being run on a PC. If it is 49 ("1"), X-Plane is running on a Mac.
Now comes a group of 36 bytes. This is the data segment. The first 4 bytes are the index, and the next 32 bytes are the data for that index. Like I said before, there is a very good explanation of what each index is, and what data are sent on that index, at http://www.x-plane.info. The first 4 bytes are the index, as an integer. Remember that PCs and Macs reverse the order of the bytes. So, on a PC, look at the first of the 4 bytes, and on a mac, look at the fourth of the 4 bytes. Whatever the byte is is the index number. In our example above the byte is 33, which means index 33, which is engine rpm.
Now there are 32 bytes left in this data segment. This is 8 groups of 4, or 8 single precision floating point numbers. you convert them in the manner as described above. The first number is the four bytes 76 87 67 69, or 3125.456. Remember that this example is a PC, so the bytes need to be reversed. The remaining 7 data points are all zero.
In general, there could be one of two things following a data segment. There could be another data segment, of the exact same format as described above, or there could be a symbol to show that that is the end of the DATA packet. The symbol that shows that it's the end of the packet is the same as the sixth byte at the beginning of the packet- either a 48 ("0") for a PC, or a 49 ("1") for a Mac. In our example, there is a 48, so we know it's the end of the packet.
Miscellaneous
There are a few more things that I discovered while writing my program, that you'll probably find useful. Remember that there is no error checking for UDP. That means that packets can get lost. Through experimentation sending DSEL and USEL packets (the packets used to request X-Plane to start or stop sending a specific data channel), I found that when sending four packets total, one right after the other in the code, on average only two of the packets made it through each time. However, sending the packets with the timer, with the timer set to an interval of 10 ms seemed to work just fine. My recommendation is that if you need to send a lot of DSEL or USEL information at once, or if you need to send several DATA channels at once, you combine them into one packet before being sent. This way, there is much less of a chance that the packets will be lost.
Another note: X-Plane uses the value -999 to represent no data. So, if you want to update only one value in a data channel, specify the other values as -999, and X-Plane will leave them alone.
And finally, if you try to update the joystick or a few other related channels, X-Plane will think you want complete control of the joystick, and will stop looking at data from the actual joystick. The only way I found to restore it was to restart the program. If you want your program to control the airplane, have it control the trim settings. This will still let you use your joystick.
Conclusion
Well, I think that should be a pretty good starting point. I know it took me a while to figure all of the above out. But using this page, the source code of the program I wrote, and the information at www.x-plane.info, you should be able to figure out how to write your own programs to interact with X-Plane via UDP. If you have any questions, e-mail me. Good luck.