Autopilots in Perl
by Jeffrey GoffJuly 12, 2004
X-Plane is an ultra-realistic flight simulator used by aviation pioneers like Burt Rutan, who uses it in his simulator for SpaceShipOne. The latest revision sports a flight model so accurate that the FAA has decided that your time spent in the simulator can count toward a pilot's license.
We'll learn how to monitor and control our virtual plane, and display our
virtual aircraft's status in real-time in a Curses window. Our
display will be data-driven so that adding a single entry to a hash reference
is all you need to display a new data element.
Software Requirements
Curses on OS X
Curses v1.06 seems to have some issues building on the author's
machine running OS X 10.3. Replacing Perl_sv_isa with sv_isa
on line 275 of Curses.c, along with commenting out #define
C_LONGNAME and #define C_TOUCHLINE in c-config.h
lets Curses compile with enough functionality to run the monitor
program.
Configuring the Sim
Throughout this article, we assume that you're using X-Plane version 7.41 or higher (the demo version has all the functionality we need), and you're flying the standard Boeing 777. This has retractable landing gears and brakes, two features we'll eventually control from inside the monitor program, X-Plane.pl.
Once you're settled in your airplane's cockpit, choose the Settings menu at the top of the screen and choose Data Input & Output under that menu. A window should open to the default tab, Data Set. This is where we tell the simulator which channels of data to transmit over the network where our monitor can listen for them.
Currently, X-Plane.pl displays gear up/down status, throttle percentage, latitude and longitude, pitch and roll, and the simulation frame rate. To enable these channels for display, click the checkboxes just to the right of rows 12, 23, 18, 16, and 0 (respectively).
If you like, you can also click the checkboxes on the far right of each column to display the channel data on the plane's windshield, as well. This is handy for debugging, especially if you're attempting to monitor new data and don't know what the values look like. Also, the channel numbers seem to change version to version, and if you're using a version other than 7.41, you may need to change the code to correspond to the simulator.
Finally, the simulator has to be told where to send the monitoring packets. Choose the Inet2 tab, and click on the checkbox next to IP address of data receiver. Fill in the left-hand box with the IP address of the machine you're running X-Plane.pl on, and fill in the right-hand box with 49999, the UDP port on which X-Plane.pl listens.
Incidentally, even though the monitor program acts as an instruction console, you don't need to turn on the Instructor Console option listed here. It may be necessary, should you want to deal with more advanced communication such as triggering system failures, but for current purposes all we need to configure is the data receiver.
By default, X-Plane.pl listens to packets sent to 127.0.0.1 over port 49999, and sends packets to the same host on port 49000. Passing command-line parameters to X-Plane.pl can override these settings, but the documentation for the simulator seems to indicate that it's hardcoded to listen on port 49000 for its commands, so be warned if you try to change this particular default.
Running the Monitor
Communicating at the UDP layer is a different, and much simpler, world than dealing with a full TCP/IP packet. The main differences here are that we don't get any acknowledgement that a UDP packet is sent, and can't even guarantee that the packet was sent at all. The practical upshot of this is that we can start and stop X-Plane.pl at any time, without the need for fancy socket tear-down protocols, and we don't need to stop the simulator every time we change X-Plane.pl's code.
Once we've started the simulator and X-Plane.pl it should
start displaying the plane's position (longitude/latitude), orientation
(pitch/roll), speed, and current throttle settings. If it doesn't, switch
back to the simulator window and make sure the cockpit is being displayed. The
gear display mimics an indicator lamp, with [#] representing
a lit lamp, and [ ] being a dark lamp.
The simulator will only send out UDP packets when the cockpit is being displayed,
and if you're running in demo mode, it decides to interrupt your flight six
minutes in with two dialog boxes that effectively stop packet transmission.
If it's still displaying the cockpit, make sure that the simulator is transmitting
data to the right port on the right machine, and if all else fails, watch the
network with a utility such as tcpdump to make sure UDP packets
are being sent out. Press the G key to raise and lower
the gear and the I and K keys
to advance and retard the throttles, even on the runway. Note that the gear
indicator lamp on the monitor will change before the gear handle does. Also,
if running the simulator on the same machine as the monitor, you may need to
bring the window with the simulator to the foreground before changes are registered.
By the way, the Boeing 777 is equipped with thrust reversers, so negative throttle settings actually make sense. Also, the flight profile dictates running the engines up to 110% of rated power on takeoff, so throttle settings beyond 100% are also legitimate.
Talking to X-Plane
Communicating with the simulator is done entirely via UDP packets. While the
simulator can send and receive various types of packets, we'll focus on
one type of packet in particular, the DATA packet. The
simulator communicates using packets like that described in Figure 1. The first
4 bytes of the header are the ASCII characters naming the type of packet, and
the actual data is surrounded by a zero byte on both ends.
In between the zero bytes, we get one 36-byte chunk for every channel the simulator sends out. Each 36-byte chunk is broken down into two sections. The first section is a four-byte index corresponding to the channel number on the "Data Set" screen, and the rest of the chunk contains eight four-byte elements.
The actual data types of the individual elements has changed from version to version, but 7.41 seems to have settled on one layout for inbound, and a different one for outbound. When transmitting data, the simulator sends all elements as floating-point types, but receives a mixture of floating-point and long integers.
Sending Data
Handling the mixture of data types is largely the job of the core data structure
in X-Plane.pl, the $DATA_packet hash reference.
Starting on line 92, this data structure encapsulates everything we want to
know about the simulator packet.
Given that there are currently 113 channels of data that the simulator can send out, and that the author is lazy, we're not going to type in all 113 channels' worth of data. Instead, we'll store just the channels we want to display in what computer scientists call a "sparse array," but Perl programmers call it a "hash reference."
One useful feature the monitoring program doesn't have is controlling the brakes. In the simulator, brakes can be applied with a variable pressure, but for our purposes they only have two settings: "Screeching halt" and "Let it roll." The first order of business is to find out what channel brakes are displayed on.
Going back to the "Data Set" tab in the simulator, we look for "brakes" on the list. In version 7.41 they're on channel 12, "gear/brakes." Conveniently enough, we're already displaying the gear setting, so it shouldn't be much work to add brakes to the list.
Now that we know what channel brakes are on, we need to know where in the channel they're stored. After all, we have eight elements to look through. So, click on the checkbox on the far right of row 12 to display that channel's contents on the windshield and close the window.
At the upper right-hand corner we should see the channel contents being
displayed. We're mostly curious about the wbrak setting, which
is element number 1 (counting from zero, of course.) The simulator also tells
us that it's of part type, which means that it's a floating
point value.
We'll first need to display the brake setting. We do this at line 112, duplicating the block for the gear status and creating the following entry, which specifies that element 1 of channel 12 is a floating-point number representing brakes:
1 => { type => 'f', label => 'Brakes',
label_x => 0, label_y => 4,
x => 7, y => 4 },
Restart the monitor program, and you should have a new entry for "Brakes" followed by a 0 or 1. Clicking on the "BRAKES" light in the cockpit should toggle the value. Once you've verified that the new brake display works, it's time to add the code that actually sets/releases the brakes. On line 270, insert the code that transmits the new brake status to the simulator, that looks roughly like this:
elsif($ch eq 'b') {
transmit_DATA(
$socket,
12,
-999,
$DATA_buffer->{12}{1} ? 0 : 1,
(-999) x 6);
}
Since the gear and brakes are on the same channel, we have to tell the simulator
to ignore the gear while letting us set the brake value, so we use the placeholder
value of -999. The old brake value is saved in $DATA_buffer-{12}{1}>,
so we set the opposite of whatever was there before.
Rerun the monitor program, and pressing "b" should clear and set the
"brake" light on the simulator's indicator panel. When the light
is out, the aircraft should naturally start rolling, and the simulated light
on the Curses window should go out.
Of course, not all types of variable are as simple to deal with as the brakes
and gears. The throttle settings should give you an idea of how to work with
more advanced data types, and of course you're not restricted to a Curses
display. It just happens that Curses is fairly convenient and low-bandwidth,
but nothing is preventing you from transplanting the code into a GTK event loop.
While you can change values in every channel, the simulator may not react to those changes. For instance, you can't change the latitude and longitude of the plane and expect it to teleport to the new location.
The values for latitude and longitude are actually computed from the values in channel 19, the X, Y and Z coordinates. Changing these will actually affect the plane's position in space. However, there doesn't appear to be a simple reference that will describe what values are considered read-only.
The official UDP documentation doesn't contain this, and the only UDP FAQ on the 'net for X-Plane appears not to have been updated since version 6. Of course, some of the channels make no sense on certain aircraft. For instance, the Cessna 172 (which the author has several hours' experience in) doesn't have retractable gear.
Receiving Data
Receiving data is also done through the $DATA_packet sparse array.
In this case, the element type is just used for display purposes, as the channel
is transmitted in floating point, and not the mixed format. The entry for "True
Speed", which is displayed in miles per hour, looks like this:
104 2 => {
105 0 => { type => 'mph', label => 'True Speed',
106 label_x => 0, label_y => 1,
107 x => 12, y => 1 },
108 },
When time comes to walk the DATA packet, this entry
tells us to see if we've received data channel 2 (line 104). If we have,
look at element 0, and display the element at (12,1) in the window (line 107.)
Since the type is "mph" (line 105), we know that we have to format that
specially.
Pages: 1, 2 |

