Intro to my model trains and their features
A while back in the "1 month just do it" thread, I mentioned the software I am writing. As some of you probably already know, I am a total model train nut. I have a 5x9 HO scale layout and model primarily UP/Southern steam/all things CB&Q/BN. Being interested in computers, I've got all of it DCC-enabled and hooked up to a Linux box running JMRI so I can control the whole thing via IP.
All the trains are independently controllable, and most have sound/lights that are accurate to the prototype. For example, this locomotive is a model of an EMD GP15-1, and has the correct number for its railroad (the Burlington Northern, which has since become the Burlington Northern Santa Fe, or BNSF). It's also got recordings of the correct prime mover sounds, the EMD 645E, which is a 1500HP 12cyl diesel.
And all these functions are controllable from JMRI.
My App - ZephyrCab
JMRI has a WebSockets server, which opens up some really cool web app possibilities. I always thought it would be cool to run the model trains with the same controls as the real ones. Instead of just a single speed knob, you'd have the throttle, brakes, and all the stuff that goes with that. They're very complex machines, especially the braking, so I kind of wanted to cross a train simulator with a model train.
Because of the WebSockets/JSON-friendly nature of JMRI, I chose JavaScript to write it. It also had the bonus of being totally cross-platform, and the idea of running the app from my Nexus tablet while walking around the layout was a huge plus. And so it began. I started writing the app in late May of 2015, with no idea what I was getting into as far as math goes. I started with the lower-level stuff like controlling sounds, which is easier said than done since not every DCC decoder is the same. I had to build a layer-cake style system where there are functions that handle directly talking to the decoder, and everything else is decoder agnostic and just uses those functions. It's evolved a little bit in implentation since then, but the concept is still the same.
The Physics
Turns out train physics are complicated. Really complicated. Developing the program has taught me a lot about JavaScript, but more than anything it's taught me about physics. There's all sorts of physics at work on a train. I had to figure out a way to find the tractive effort of the engine at a given throttle speed. I had to find data on fuel consumption. I had to find data on air reservoir sizes, and I had to use various twisted versions of Boyle's law to create a functioning air pressure simulation system.
Lately...
Lately I've been working on some under-the-hood tweaks for interfacing with the layout, and for "building" your train in the software. Obviously to simulate the physics accurately, the software needs a ridiculous amount of information about the locomotives and rolling stock. It's not enough just to say "it's a 1500hp diesel," it needs way more information than that, and way too much for the user to input manually. To deal with this, I've created a "train builder" that bundles this (and some other things) into a single GUI. It synchronizes with the JMRI roster, so users just add their locomotives using the names from the roster. You can see a demo in this gif.
Just by adding "CBQ2" to the roster, it is able to grab everything you could possibly need to know about that locomotive in the form of a JSON object.
Physics Engine Structure
The physics engine is simple from a programming standpoint. It stores everything in a single list object: train.all
. This is an array of objects, and is set up so that the model can be controlled from the object, and the prototype information can also be fetched. For example, running train.all[0].dcc.f.bell.set(true)
turns on the bell on the first locomotive. This is thanks to the decoder abstraction layer I've built. Additionally, I can get the PSI of the main air reservoir (updated with each physics engine cycle) like so: train.all[0].prototype.realtime.air.reservoir.main.psi.g
. Every 500ms, the physics engine "cycles" through the entire train using a big for
loop. This allows you to have as many or as few things on the train as you want, and the physics engine will "just work" regardless.
I'll be posting more updates on development progress here, but do you guys have any questions? I know it's kind of a complex idea with the various pieces that have to fit together to make the finished product, so I'm happy to answer any kind of questions you have. I love constructive criticism, suggestions, or feature requests!