Hello Again!
===================
Abstract of this post
===================
This is a follow-on of my previous post on capturing DCS signals into a terminal (Old Post). In this previous post I talked about how to capture a DCS signal from an engine and TIU towards spoofing the signal (IE generating fake TIU signals to control your engine).
As mentioned last time.... There are some interesting things you can do with this, like make non-train things respond to train commands or have parts of the layout react on certain conditions (like something only happens if the train is a certain speed when it passes by... or only happens if the train's lights are on) or other creative stuff. The .v code in this example simply sends a static command to the engine, but you can certainly modify it to do all kinds of great stuff...
if (some condition == true)
{
(make the train do something)
}
Then you can have the train react to the layout (sensors and blocks and such), like have it auto slow down at stations, auto ring the bell in the yard, auto blow the horn at level crossings... use your imagination! Again like the last one, this post is just equipping everyone with a framework for completing the transmission process. This is not a finished product. The idea is you can build on what's here to get you started.
============================================================
Hardware Interfacing for DCS spoofing
============================================================
Similar to the (Old Post), I use the ARTY 7 FPGA (FPGA) with Xilinx Vivado for my train projects. Basically we use the technique from the old post to capture the sequence, and then stream it to the engine using the spreading codes. If you want to know more about the spread-spectrum format details that DCS uses you can check the post that explains all the details (DCS details). While that previous post was for capturing/receiving the DCS packets and used a 74HCxx chip as a comparator, this project will send the commands to the engine so we need a line driver.
It turns out looking into the track is a pretty low impedance (a few ohms), and so we need drive strength from our digital output so that we can source/sink enough current that the track propagates our signal voltages. A regular FPGA pin or CMOS chip won't be able to sink/source more than a few mA, so we turn to line driver chips. The one I went with is the SN75121 from Texas Instruments available for a few dollars (SN75121) which can drive as high as 75mA. If you look at the schematic on the data-sheet the output of the chip is only a pull up stage, so we need an external pull-down resistor outside. I've found 25 ohms works nicely. Note: it's going to 5V so that resistor needs to dissipate at least 1W of power.. (5V)^2 /(25 ohms) = 1W.
Basic Schematic Diagram
Now the track carries about 20V AC from the transformer, so that would blow up the line driver chip if we directly connect the pin. The trick we use to fix this is selective AC coupling. By using capacitive coupling we can keep a low impedance connection at the high DCS frequency while avoiding letting the 60Hz flow back into the chip. The cutoff frequency depends on our pull-down resistor and the capacitor. For the cap, I found 4uF would be a pretty good choice:
The voltage that the 18V sees at 60 Hz is the voltage divider of the 25 ohms and capacitor so
Vin X (25 ohms) / mag(25 ohms + j[1/2pi 60Hz 4uF]) = 0.03 Vin = 0.75V
This is pretty okay for the low impedance line driver. However at DCS frequencies (lets say 1 MHz for simplicity) traveling in the other direction the series impedance is only going to be
j [1/(2pi 1MHz 4uF)] = ~0.05 ohms (so basically directly connected)
In this way the DCS can flow to the track, but the track power can't flow back into our driver chip and blow it up. Remember that capacitor can't be a polarized one or you'll blow yourself up.
============================================================
Packet Formats and What to Send
============================================================
Okay before I go any further a gigantic thank you to SanDiegoMark here on the forum who helped me sooooooooooo much going through the data we captured and figuring out the command structure. I was able to build the hardware, but he had the insight to see what was going on with the bits inside the captured packets. Here is what he figured out from the captures in the (Old Post):
The DCS track signal format is essentially made up of:
11111111111111
0
<ENGINE NUMBER +1 in LSB to MSB order>
1
< some command>
111111111111111111
All run together. The long string of 1's at the start/end of the packet allow the DC conditions to stabilize before the actual command is sent. The # of 1s doesn't matter as long as it's longer than the time constant of the coupling structure above. Remember for each 1 or 0 we are actually sending an entire spread-spectrum sequence at much higher frequency (chip rate is 3.75 MHz).
So everytime we encounter a "1" in the transmit sequence we send 0100010111110110011100001101010
and everytime we encounter a "0" in the transmit sequence 1011101000001001100011110010101 (flipped)
You can use the other material from the (Old Post) to capture all the commands you want to use for engine.
For my train (engine #49) here are two sample commands:
1111111111111110010011001010000000000000010000000110001111111111; makes the whistle blow
1111111111111110010011001001000000000000000000000010001111111111; makes the front coupler fire
Looking at the whistle more closely:
111111111111111 0 01001100 101000000000000001000000011000 1111111111
(leadings 1s) (a single zero) (engine # +1 in LSB to MSB) (Command) (trailing 1s)
So that's all there is too it really, just make a dictionary of commands with the capture program, and write code to send these sequences in spread-spectrum format with the spreading code above.
In terms of code, I've attached my RTL/verilog to the post in case anyone wants to play with it. It's a very simple test code. Also attached is my XDC contraints file specifically for the ARTY7 FPGA. There's really not much to it.... Just read the command bits one by one and stream out the appropriate spreading code. Repeat until the packet is done and the train will respond.
============================================================
Testing
============================================================
Here is an oscilloscope capture at the output of the line driver comparing the real TIU output to our spoofed one:
One interesting thing we found is that 3750 KHz isn't necessarily best. I found 3748 KHz works better. The time base inside the engine or the cheapo signal generator I'm using may be slightly off in it's frequency reference crystal. Anyways don't feel it has to be exactly 3750.00000, the double-edge sampling nature of spread-spectrum has some slop built into it.
Finally a short video showing me blowing the whistle on my MTH train from the FPGA spoofer. Note I'm only sending the whistle start signal for the whistle in this example, so I need the remote to send the stop signal so the train will stop making noise.
============================================================
Summary & Conclusions
============================================================
Between this post on how to transmit and the (Old Post) on how to receive and capture this now equips everyone with the means to mold/modify/enhance the DCS system to do whatever they want. All the engineering and design has been presented so all that's left is to sit down and capture all the codes for a given engine and make a command dictionary. You can add DCS functionality to anything, and have anything interact with the train.
If you were ambitious enough, you could even combine this setup with SanDiegoMark's neat PC program and basically run your DCS with no TIUs or remotes at all. Again, thanks so much to him on helping us figure out the packet format.
Last note: If you watch the video at 00:21 there is a USB stick that looks like a train. At only $16 how can you not buy this (usb-train) and still call yourself a train enthusiast?
Cheers all!
~Adrian