Friday 5th June, 2015
The hitch-hiker's guide to raildriver.dll, part 3: simulating a simulator. Under normal circumstances, your plugin talks to one side of RailDriver.dll, and the train simulator talks to the other. But the simulator's API is also well-defined, and you can build software that simulates the simulator. One example of this can be found in the RDShark software referred to in the previous post on this blog, which pretends to be a train simulator so that you can see what your plugin is doing.

This post will tell you how to write software that talks to the 'simulator side' of the RailDriver.dll API. It will use RDShark as an example, so if you are following along at home you may find it useful to get the RDShark code out of github here.

First, mark yourself as connected. Much like a plugin, you are expected to mark yourself as connected when you start paying attention to RailDriver.dll. You do this with the SetRailSimConnected call, thus:


It's probably good practice to do this as soon as possible before starting listening. RDShark is always listening so it does this when the program starts (here).

Now, you can respond to messages from the plugin. The messages are not magically sent to you. Instead, you have to ask RailDriver.dll regularly whether there are any messages for you. If there are, you should act on them and do so fairly promptly. Remember, if a plugin has asked you for information, it will be blocked until you reply to it! The polling in RDShark is done in a timer event (here).

As noted in the previous installments, there are two kinds of message that a plugin can send to a simulator: either it can request a piece of information (such as the current speed of the train) or it can set a control (such as putting the throttle at 0.5). Your simulator needs to poll for each of these possibilities separately. You will note that RDShark checks first for one possibility, then the other.

First, let's look at information requests, because they're a bit simpler. To recap, these come from a plugin calling GetRailSimValue(someIdentifier, mod). To see if you have one of these messages to respond to, call GetRailDriverGetId. This returns -1 (which in RailDriver.cs I call 'None') if there is no message pending, or the ID if there is. You should use it approximately thus:

RDid id = GetRailDriverGetId();
if (id != RDid.None) {
        // ... process your message here ...

To be able to answer the message, you need to know the value of the other parameter: is the simulator requesting the current value, the minimum, or the maximum? You do this using the GetRailDriverGetType() function, which returns the RDmod that the plugin requested, thus:

RDid id = GetRailDriverGetId();
if (id != RDId.None) {
        RDmod mod = GetRailDriverGetType();
        // ... find out the value and send it back ...

Note that you should only really call each of these getters once: RailDriver.dll has a nasty habit of having internal side-effects on things that look like simple data retrieval, and it's best not to risk doing things it doesn't expect.

To send the data back, use the SetRailDriverValue(id, value) call. The id parameter should be the same as the id that the plugin requested, and the value parameter should be a float. Therefore, the whole code fragment to respond to get messages is something along the lines of:

RDid id = GetRailDriverGetId();
if (id != RDId.None) {
        float value = 0.0;
        RDmod mod = GetRailDriverGetType();

        // ... here, find out what value you need to send back ...
        // ... and stick it in the 'value' variable

        SetRailDriverValue(id, value);

If you look at RDShark's code for handling these messages (here), you'll note that it follows this pattern nearly exactly. I emphasise again: do not do any complicated processing while you're handling messages, because the plugin is completely blocked until you answer!

Now for control messages. To recap, these come from a plugin calling SetRailSimValue(someControl, value). Remember as well that a plugin will never block when sending a control message. This means that there may well be a queue of control changes waiting for you to deal with when you get around to it.

To see whether you have any incoming control messages, use the GetNextRailDriverId(start) function. This is a slightly more complicated function: it starts from its 'start' argument and iterates through all the controls until it finds the next one that has changed. If it finds one, it returns that control ID; otherwise, it returns -1 (None).

If this sounds convoluted, that is because it is convoluted. The easiest way of understanding it is to look at a code fragment that uses it:

RDId id = GetNextRailDriverId(None);
while (id != None) {
        // ... do something useful ...
        id = GetNextRailDriverId(id);

To actually read the control change, you need to use GetRailDriverValue(id), which returns the last value that a plugin set for the id parameter. Thus, your code fragment for dealing with incoming control set messages looks like this:

RDId id = GetNextRailDriverId(None);
float value = 0.0;
while (id != None) {
        value = GetRailDriverValue(id);
        // ... do something useful with value ...
        id = GetNextRailDriverId(id);

Again, if you look at RDShark, this is very nearly the code it actually uses (here).

posted by Rob Mitchelmore, 15:09 (anchor)
Friday 15th May, 2015
RDShark is a debugging tool for RailDriver.dll plugins. It is a train sinulator simulator, showing you what your plugin is sending, and responding in a vaguely sensible way back to the plugin.

RDshark screenshot

To install this, copy it into the plugins folder with RailDriver.dll, then run it instead of Rail Simulator while you run your plugin. If you run both this and Rail Simulator at the same time, expect strange things to happen. You can download the binary here or browse or download the source here.

Incoming messages, either 'gets' or 'sets', show up in the list box. The list box, by default, automatically scrolls to the latest get or set. You can stop it automatically scrolling by using the View -> Follow new requests menu item.

When the plugin sets a value, it shows up in the list. When the plugin requests a value, that too is logged in the list and the debugger responds (in this version it always responds with 42.0).

Note that queries from plugins about changing of locomotives don't show up in the list. They work the other way: the simulator notifies the DLL of the change of locomotive and that state just sits there until the plugin picks it up. The plugin's request for information never actually reaches the simulator program at all. To simulate a change of locomotive, use the 'send' menu, which lets you simulate a change of locomotive either with or without combined throttle and brake.

More features incoming.

posted by Rob Mitchelmore, 14:57 (anchor)
Tuesday 5th May, 2015
An important post-scriptum to the previous posts about raildriver.dll: as currently shipped, raildriver.dll is 32-bit only. When you're driving it, you should do one of two things:

If you do not do this, when your executable is run on a 64-bit OS, the executable will run in 64-bit mode, and when it tries to call the (32-bit) raildriver.dll it will raise an ImageFormatException.

posted by Rob Mitchelmore, 23:07 (anchor)
Thursday 2nd April, 2015
The hitch-hiker's guide to raildriver.dll, part 2: more functions for your plugins. There are three other functions that you as the plugin can call to extract information from the simulator. I'm not going to give full examples for these because they're fairly straightforward. I've updated RailDriver.cs in the previous post to include these functions.

GetRailSimConnected does exactly what it says on the tin. Note, however, that this relies on a flag that is stored in the DLL. It is quite possible for a railway simulator to go away and not unset this flag. Thus, don't use this as a way of predicting whether GetRailSimValue will block, or whether the rail simulator is actually there.

Here might be a good place to talk about something mildly contentious that I did in the demos in the previous posts. Note that they call SetRailDriverConnected(true) but they never clear that when they leave: they never call SetRailDriverConnected(false). The reason for this is that this, too, is implemented as a flag inside the DLL. If you have multiple programs talking to the DLL, and one of them calls SetRailDriverConnected(false), then the rail simulator is quite within its rights to stop talking to all of those programs.

In the general case, in fact, the DLL may not behave at all well if more than one plugin uses it at once. If you are just sending control traffic in using SetRailSimValue, then this is likely not to be a problem: later changes to any given control will override earlier ones, and the rail simulator will just pick up the latest. However, if you are calling GetRailSimValue, be careful! If two plugins call GetRailSimValue at the same time, and the rail simulator is being slow, then one of the requests will get lost. Caveat haxor.

Returning to the point:

GetRailSimLocoChanged returns true if the simulator has changed locomotive since the last time GetRailSimLocoChanged was called.

GetRailSimCombinedThrottleBrake returns true if the throttle and brake are combined in one lever on the locomotive currently being simulated, or false if they're not.

posted by Rob Mitchelmore, 18:54 (anchor)
Wednesday 1st April, 2015
The hitch-hiker's guide to raildriver.dll, part 1: the plugin side. This blog post is the second of a small series of posts about train simulation. These posts are side-effects of some work I'm doing for Human Factors' Transport Simulation Laboratory at the University of Nottingham. Needless to say, all opinions and words in this series are my own: if you take issue with them, they're my fault entirely, not theirs. Contact me and don't bother them!

The RailDriver controller, which I talked about last time, uses a DLL to talk to the railway simulator game on the PC. This blog post will tell you how to call that DLL from your own software in order to control the train simulator game and to get information out of it. This will let you write simple plugins for games that support raildriver.dll. I'm using Train Simulator 2015.

If you're going to follow along at home, I've generated a C# file that contains bindings for the functions you need and the relevant contants, here.

raildriver.dll provides two communications channels, one from the plugin software to the simulator, and one from the simulator to the plugin software. These are accessed with the GetRailSimValue function, to get data from the simulator to the plugin, and SetRailSimValue, to send controls to the simulator. Do not be confused by the names of these functions! They do not access a single data structure: if you push something to the emulator using SetRailSimValue you will not be able to get it back using GetRailSimValue. Think of SetRailSimValue as "send information to simulator" and GetRailSimValue as "request information from simulator".

A plugin is just a standard executable that uses raildriver.dll. There's nothing magical about it at all. When your program loads, it should call SetRailDriverConnected(true). It can then use SetRailSimValue(id, value) to send a control to the simulator, and GetRailSimValue(id) to request information from the simulator.

This is best, perhaps, shown by examples. Let's start with getting information out of the simulator. This (binary) is a small speed recorder. Drop the executable into your RailWorks\plugins directory and run it, then drive a train around a bit. Every second, the current speed should be logged to the list box (most recent speed first). The speedometer is the only thing that I've reliably managed to get out of Train Simulator 2015.

Note that it calls SetRailDriverConnected(true) when the form is loaded. It then, in the timer, calls GetRailSimValue to get the current value of the speedometer.

Note that GetRailSimValue is a blocking call. It requests the value from the simulator and then waits. If it reaches the end of its wait period, then it bails out and returns 0. The timeout period is a little unpredictable, but is usually about a second.

(Gory details: GetRailSimValue calls Sleep(1) 100 times while waiting. How long this actually corresponds to may vary.)

Here's another example: this one (binary) controls the throttle. Every second, it increments the throttle by 10%. When it hits the top, it changes its mind and decreases the throttle by 10% until it hits the bottom. And so on. This is obviously a slightly contrived example. Again, put the executable in your plugins directory, run it, then start driving your train around.

Here, the timer is calling SetRailSimValue to change the throttle, with 1.0 being full scale and 0.0 being... zero. SetRailSimValue will not block no matter what you do to it, and you receive no notification whether or not the rail simulator has acted upon your information or not.

For writing a simple plugin there's not really a great deal more to it than that! The list of knobs you can fiddle with is in the RailDriver.RDId enum in RailDriver.cs.

posted by Rob Mitchelmore, 21:16 (anchor)
Wednesday 11th June, 2014
What's inside a RailDriver controller? This blog post is the first of a small series of posts about train simulation. These posts are side-effects of some work I'm doing for Human Factors' Transport Simulation Laboratory at the University of Nottingham. Needless to say, all opinions and words in this series are my own: if you take issue with them, they're my fault entirely, not theirs. Contact me and don't bother them!

The RailDriver is a desktop "train controller": it's basically a USB game controller that gives you levers to control your train with, buttons also to control your train with, and a bassy speaker to make the whole thing rumble with the engine noise.

This post is aimed at people who are interested in hacking one of these to act as a basis for a larger cab-type controller, or who are just curious about what's inside. I strongly recommend being content with the photos herein, rather than attempting to take it to bits yourself, unless you're happy with the idea that it might not go back together. The one I have does not at all like coming apart, and it likes going back together even less.

TL;DR: is it hackable? Yes, but mechanically. It is not really sensibly feasible to replace the knobs on the electronic side without serious care. On the other hand, if you can rig up gearing to turn the little grommets in the mainboard appropriately, all should be well.

To take the lid off: take out two screws on either side, three at an angle on the bottom holding the front on, and the three topmost screws on the back. The entire top comes off: there is one small ribbon cable connecting the top to the bottom. It becomes obvious here that there are basically two entirely separate modules inside the case.

The bottom half is just an audio amp for rumble: it has a low-pass filter that can be turned on and off with a jumper, and a compressor that can be turned off with a jumper. There's not a great deal more to it than is visible here.

The top half is where all the control stuff happens. It's the top half I'll be concentrating on in the rest of this blog post.

The little cable between the top half and the bottom seems to carry 5V from the USB supply.

The main board is the dual-board assembly on the left hand side of the photo of the top half of the controller above. At the top of the mainboard there are three connectors. The USB cable is the middle one and can be disconnected: getting it out of the way is handy. Don't do what I did: remember to plug it back in before reassembling the unit. You will be annoyed at yourself if you get it back together and the USB cable is sitting on the other side of the room.

There are two screws at the back edge of the case that hold down the cabling. You can take these out. These will also release the little metal brackets, so don't let them fall out and get lost! You can then unplug the cables to the speed display and to the side pushbutton board. The speed board attaches to the topmost connector of the three in a row (when the board is held with the writing the right way up): the side pushbutton board attaches to the connector that is at right angles to the others.

You can now sensibly remove the speedometer board.

The ICs are 74HC595s, which are 8-bit shift registers. There's one per 8-segment LED display. That, together with the five-pin header connection to the mainboard makes me think that the lines on that header are Vcc, GND, and three signal lines, one for each display: the controller on the main board pushes 8 bits per display down the signal lines which are fiddled with by the shift register.

Which lines are which are left as an exercise for the reader. I haven't checked this is true at all.

The side switch PCB is held in by four screws. These also hold in the plastic tray that keeps the physical buttons in, so undo carefully and don't let all the buttons go everywhere like I did. The board itself just has little rubber push buttons on it.

Holding it up to the light shows it's a pretty standard key matrix so it's probably hackable.

The bottom switch board plugs into the remaining connector on the main board, the middle one of the three. It has lots of screws keeping it in. You will need to undo the leftmost of the four screws that are holding the front of the case on (through plastic tabs) as this is holding down the cable tie. Again, this is just a key matrix.

The actual levers are held in by various mechanical contrivances and I strongly suggest not even attempting to remove them bodily. Instead, remove the mainboard. To do this, undo the three screws in a line between the board(s) and the wall of the case. These go into black plastic. You will also need to pop off the tops of the 'wipers' and 'lights' rotary switch things on the front panel. You may find it helps to undo the four screws that go into black plastic on the lever assembly that's nearer to the board, too. Then, pull the board upwards, bend it very gently backwards towards the case until the top two shafts can be popped out, then wiggle it furiously in a state of frustration until the bottom two do. You'll find yourself with an assembly consisting of two PCBs and a plastic frame in your hand if you haven't broken anything.

Now you can remove the plastic harness thing from the back of the mainboard. This contains the two rotary switch things on the right of the top of the case. Before you do anything make sure you understand how the rotary switches fit into the plastic frame, because they will fall out in a moment. Now, undo the four brass-effect screws that hold on the plastic frame and take it and the rotary switches off if they haven't already fallen off and rolled under something.

The two boards in the board sandwich are actually soldered together by means of the headers J6 (on the chip side) and J7 (n the other side). I'd suggest desoldering from the non-chip side if you must do it, if only because there's less to get in the way. A vacuum desoldering gun will be handy here. Desoldering the pins is very fiddly however, and I don't recommend you do it, first because it's a massive pain and secondly because:

There's actually nothing inside.

There are no electronics on the back board at all. Rather startlingly, what we have here, I think, is a bunch of variable capacitors created from PCB planes and plastic (remember, a capacitor is just two planes with a dielectric). The knobs and levers move the plastic in and out of the space between the plates (note that the area for each bit of plastic is divided into two copper areas) and this changes the capacitance.

That's about all that's inside this thing. I rather like its innards!

I'm going to blithely assert that all you need to do to get it back together is follow the above steps in reverse, except that the wiggling is even more furious and trying to get everything lined up is extremely painful. Caveat haxor.

posted by Rob Mitchelmore, 20:26 (anchor)
Tuesday 28th January, 2014
"Can you hear me?" My uncle asks. "One puff for yes, two puffs for no."

The wind has dropped and a thin, even pencil-line of smoke is climbing the still summer air.

"Is it warm where you are?"

At the bottom of the strand of smoke is a lit cigarette. It is sitting in a clean, white saucer on the grass. Crickets are singing in nearby undergrowth.

"Do you have many animals there?"

Behind the saucer is a comfortably human-sized bump in the grass: not too big, not too small. The air is carrying the smell of recently-disturbed soil to his nose.

An unidentified bird starts singing, quite close to him: coo-COO. coo-COO. coo-COO. It stops for breath, then repeats itself.

My uncle is slowly falling into a sunlit reverie. His voice is getting more distant, and he leans back on a small vertical limestone slab. Lichen cracks off and falls onto his shirt. The smoke continues rising, uninterrupted, silent.

"You—all this—this is you, you're feeding this, it's yours, did you know that?" he asks, drowsily.

A bee hums past, avoiding the smoke. Two birds scuffle in a nearby tree. There is a silence: and my uncle, with his eyes closed and his back warmed by the sundrenched limestone, calmly carries on playing twenty questions with the dead.

posted by Rob Mitchelmore, 21:48 (anchor)
Wednesday 22nd January, 2014
Here is a drabble, a microfiction thing, that I wrote for Johannes Punkt. It is about water, and sin, and memories.
posted by Rob Mitchelmore, 22:09 (anchor)
Tuesday 26th November, 2013
USB errors in fastboot: I was just trying to upgrade my development tablet to Android 4.4 "some kind of allegedly cute codename probably goes here". While upgrading, using whatever weirdy version of fastboot happened to be in my $PATH, I was getting somewhat opaque errors. Noticeably:

apsu:nakasi-krt16s cheesey$ fastboot erase boot
ERROR: could not get pipe properties
erasing 'boot'... OKAY

And then, later:

sending 'system' (611886 KB)... ERROR: usb_write failed with status e00002be
FAILED (data transfer failure (No such file or directory))

And again, later still:

getvar:version-bootloader FAILED (remote: (Nv3pBadReceiveLength))

The results for searching for error e00002be on the Internet seemed to result in a lot of people thrashing around being ad-hoc, but the actual problem (at least for me) was embarassingly simple:

I was accidentally using an absolutely ancient version of fastboot.

Using a more recent fastboot solved the problem entirely. Unfortunately fastboot doesn't have a --version option or anything similar but the newer versions have a longer help message.

Broken fastboot:

$ fastboot 2>&1 | wc -l

Working fastboot:

$ /SDKs/Android/fastboot 2>&1 | wc -l

If in doubt, there's an up-to-date fastboot included as part of the Android SDK for your platform (in the platform-tools directory).

posted by Rob Mitchelmore, 14:19 (anchor)
Saturday 9th November, 2013
Update to previous RPCEmu post: if you check the code out of mercurial, that now works on OpenBSD.

posted by Rob Mitchelmore, 18:29 (anchor)
June 2015May 2015April 2015June 2014
January 2014November 2013October 2013July 2013
April 2013March 2013January 2013November 2012
older posts