Introduction to Track Definition Language (TDL)

In my presentation to Merg in April 2001 I described my system of using modular software - each section of track being represented by code dedicated to that track. Although things have moved on a long way since then I am still using basically the same mechanism:

Each section of track, or turnout, or crossing is handled by a separate module of code. All identically behaving tracks have a copy of the same module of code. Each module represents one track element and it only refers to its immediate neighbours. It might look at its predecessor to see if a train is coming, and at its successor to see if it may forward that train.

Conceptually it is like a railway running a job-creation scheme - you have a signalman dedicated to each and every track section! These signalmen are allowed to talk to the next one up and down the line, but none of them know anything further away than their immediate neighbours.

Each man knows if his section is occupied and which train is in it. Therefore a section can ask the preceding section about the arrival of the next train, and can find out when the next section has taken the train.

The code we are talking about here is written in TCL (Train Control Language) script which Tcc executes. This is a similar TCL to that used by the CTI software TBrain.

This sort of modular approach makes it possible to generate the code automatically and that is where TDL (Track Definition Language) files come in.

Tcc can read a TDL file, generate a TCL script from it, and then execute that script. The TDL file is just a description of a layout. If you have a simple tail-chasing loop of six sections, the TDL file might look like this:

Long BkA - to BkB
Long BkB - to BkC
Long BkC - to BkD
Long BkD - to BkE
Long BkE - to BkF
Long BkF - to BkA

This defines a layout consisting of six "Long" sections BkA to BkF. A long section is one long enough for a typical train to slow down and stop in.

The 'to BkB' part defines which section this one connects to. In other words a train in BkA will progress forwards to BkB.

The '-' is a placeholder for options which further define characteristics of this section, which we can ignore for now.

Thus this simple file defines a loop of six sections, and will cause script to be generated that will allow a number of trains to follow one another around the loop without catching up and crashing.

Note that there is no significance to the ordering of the lines in the tdl file (but see below for a description of hardware connectivity).

Most layouts are a little more interesting than that of course - we need points. Here I define two further module types:

So with these we can make a passing loop:

Long BkA - to TO1
TurnOut TO1 - from BkA leftTo BkL rightTo BkR
Long BkL pass to TI1
Long BkR pass to TI1
TurnIn TI1 - leftFrom BkL rightFrom BkR to BkB
Long BkB - to BkC
Long BkC ... and so on

The track connectivity should be clear from the wording:

Block A connects to a diverging point that connects to two blocks L & R.

These two then conect through a converging point to block B.

Note the 'pass' in the options field of BkL and BkR. This option means that these sections are passing sections. If a train enters either BkL or BkR and a faster train is coming up behind then the slower train will stop and allow the faster one to pass.

Currently TurnOuts decide for themselves which way to send trains and base that decision on clearest track. I do have plans for adding either (or both of) destinations or routes to define what trains should do.

TurnIns give precedence to faster trains - so again the express will pass the passenger train for example.

In addition there is a (diamond) 'Crossing' track, which only allows one train to pass at a time.

So far all of this trackwork is defined to be unidirectional - trains can only ever proceed forward. There is no mechanism for passing a train back to a preceding section. In fact all the code modules are fundamentally unidirectional - trains approach from the entry and depart at the exit

This poses slight problems in sidings and at a terminus so a higher layer of control is employed - a 'Network'.

A Network is an arbitrary collection of points, crossings and other trackwork that connects some number of arrival lines to some number of departure lines.

The significant change here is that a line can be both an entry and an exit as far as the software is concerned so a siding off a single track line could be represented by:

Network NetS TO1
Long BkA - to BkB
Long BkB - to NetS
Long BkC - to BkD
Siding s - to NetS

We are defining a network (called NetS) consisting of single piece of trackwork, a point (called a NetSwitch when its in a Network), TO1.

Block B goes to the network, and hence is an input (entry) to that network.

Block C comes from the network (not shown above) and carries on to D.

A siding 's' also connects to the network.

Now we can define what routes are possible through this network:

NetRoute NetS 1 from BkB to BkC uses TO1L
NetRoute NetS 2 from BkB to s uses TO1R

This says that a train arriving on block B can go across TO1 to the left (TO1L) to block C or to the right to siding s. The '1' and '2' are simply route numbers across network NetS.

After the word 'uses' is a list of track parts (mostly NetSwitches) some with direction indicators (L & R) appended which defines how to set the route. While the train is traversing the network these resources are reserved for that train. Only when the next section reports that the train has fully entered that section are these resources freed for reuse. If there were other resources available in the network, then they are potentially available for another train to use at the same time.

Naturally the train needs to come out of the siding again. To do this we need a reversing indicator. Such an indicator is called a NetDir (Network Direction):

NetDir  D1 BkB,s

This defines a reversing flag D1 and when set it reverses the feed to both block B and the siding. The state of this flag is also used to control the overall movement of the train. So the route list now looks like this:

NetRoute        NetS    1       from BkB to BkC uses TO1L,D1-F
NetRoute        NetS    2       from BkB to s uses TO1R,D1F-
NetRoute        NetS    3       from s to BkB uses TO1R,D1-R

The first route along the main line now has an extra 'D1-F' which means that when this route is in use D1 is set forwards (and hence block B sends the train forwards).

The second route says that to get into the siding D1 must already be set forwards.

The third route is from the siding reversing back to block B. This sets D1 to reverse.

By insisting that D1 is already forwards in route 2, the train reversing out of the siding is prevented from going straight back in!

Networks can also handle class-based routing. Lets suppose that this siding is a goods siding and passenger trains should not enter. Route 2 changes to:

NetRoute        NetS    2       from BkB to s uses TO1R,D1F- goods

Lets also suppose you only wanted an occasional train to enter the siding, lets say roughly every third goods train, that makes a 33% chance of each goods train going into the siding:

NetRoute        NetS    2       from BkB to s uses TO1R,D1F- goods/33

Naturally there is a little more to put in TDL files, for example you might need some control over how inputs and outputs are wired. This is controlled using the Stack directives.

A CTC panel can be animated as the train moves around the layout. To add this you simply define a list of co-ordinates for each track section.

Also you need to define how the throttles connect to each block. Currently TDL files support both zone control and throttle-per-section. PCC is not yet supported, but is on the to-do list. Zone control is a compromise between PCC which uses a lot of relays, and requires major changes to add extra throttles and throttle-per-section which uses no relays but obviously lots of throttles.

Stations can be defined in the options for any Long section, and it is also possible to define how long the trains stop there for.

If sections have optical sensors then these are also options and you can even define where they are. Optical sensors give better knowledge of where the ends of a train are - to ensure that points are not fouled when a train stops.

The sensor positions are used to estimate the length of trains - if a train fits between two sensors then it must be shorter than the distance between those sensors. The length 'measured' in this way allows a check to be made that a train will actually fit into a siding.

Once you have a TDL file that describes a layout then Tcc will be able to create a TCL script that can control that layout.

Naturally it then becomes very easy to update the TDL file should the layout be extended and developed. You should even be able to quickly generate a script to control a modular layout whose configuration varies each time it is used.

If the layout is using QTUs (which support dead-reckoning) then optical sensors are not required for following train position. The dead-reckoning system allows TCC and the TCL script to know exactly where each train is at all times just using the track feed wiring.

The hardware connectivity can be very simple - you need only specify the stack configuration. Alternatively you can have considerable control over wiring, but that is beyond the scope of this description.

In simple terms your hardware consists of three lists of signals: inputs, outputs and throttles. These are the 'Sensors', 'Controls' and 'SmartCabs' (or 'QtuCabs') sections in the TCL file.

These I/O lines are considered to be in order as they appear in the hardware. The first outputs if using RPC for example are the first bits of the first SRO4 or DPR in the stack (nearest the RPIC or other stack header).

As lines are encountered in the TDL file inputs, outputs and throttles might be required. For example a Long section requires a current detector and might also have some optical sensors. A point requires one or two outputs to set its direction. These are simply added to the appropriate list in the order they are

seen in the file.

As a minimal example, consider the six section loop. Lets assume that we are interfacing with RPC and have:

Further lets assume that to drive the trains we have two CTI SmartCab modules.

If we want each of the six sections to be fed from either of the two throttles so that two trains can run around our simple loop, then the whole tdl file

would be:

    InStack     Declare:in
    OutStack    Declare:out
    StackLen    in=8 out=8

    Long BkA - to BkB power 1/2
    Long BkB - to BkC power 1/2
    Long BkC - to BkD power 1/2
    Long BkD - to BkE power 1/2
    Long BkE - to BkF power 1/2
    Long BkF - to BkA power 1/2

The first two lines declare that inputs (current detectors and optical sensors if they were used) are connected to a stack called 'in', and that outputs (power switching relays, turnouts, signals etc) are connected to a stack called 'out'. Naturally in the real world they are both a single RPC stack consisting of RPIC, DPR and FTC modules.

The third line defines the number of lines that 'in' and 'out' have. Both FTC and DPR are 8 bit modules, so length is set to 8. Defining the length here allows 'spare' lines to be added to the tcl file, and also produces a warning if too many items are added.

The 'power 1/2' added to each 'Long' line says that each section is fed from two throttles - number 1 and number 2. This automatically adds the appropriate entry to the TCL script and no further declaration is required.

Naturally this configuration requires two serial interfaces - one for RPC and one for CTI. The TCL script sensors are defined as:

Sensors:
CurBkA#, CurBkB#, CurBkC#, CurBkD#, CurBkE#, CurBkF#

So we can see that current detectors should be wired in the same order as the lines appeared in the TDL file. The same holds for the power routing relays on

the DPR module.

Thus by writing a nine line text file (listed above), and connecting up a few modules to a loop of six sections we could put two trains on the track in any two sections and they should follow one another around the loop.

OK, not a terribly interesting layout but not bad for nine lines of text! Adding a passing loop is only three extra lines, as shown earlier.

For more details on the tdl scripting system, look here.