Goods Loops


Many stretches of mainline carry different classes of traffic, such as slow freight trains and fast express passengers. In order to allow the fast trains to overtake the slow, loops - often designtated goods loops - allow the slow trains to be sidelined while the fast train passes.

Building a goods loop in Rail3D is easy - but the problem is how to get the freight train to enter the loop if there is a fast train to overtake, but allow the freight to continue unhindered if there is no fast train behind.

Setting up the loop

We construct a simple freight loop in Rail3D and place three signals as follows:

E: the entry signal

L: the loop signal

M: the mainline signal

All signals are uk four aspect colour lights in my example and the entry signal is a junction (left) signal. The three loops signal (E, L & M) are controlled, all others are uncontrolled four-aspect signals.

The loop entry point is set to apply “Free Route” to trains of class 4,5,6,7,8 & 9.

The Script

So, we need a script that routes the freight into the loop if there is a faster train following it, but allows it to continue on the mainline if there is nothing behind.

We can use the OnSignalCanClear and OnSignalCanRouteTo entries to control the operation of the loop. Note that these entry points only function for individual signals in build 102.3 and later

Firstly, let us consider the mainline signal, M above.

When a train approaches the loop, the entry signal will attempt to clear a route on the main to the M signal. So the M signal’s OnSignalCanRouteTo function will be called. This function needs to look for any following trains behind the freight.

Consider the situation when the E signal tries to clear:

With four aspect signals, the loop signal (E) will attempt to clear when the train passes the 3rd signal back (3 in the above diagram). If there is a second fast train behind the freight, it will be back around the seventh or eighth signal.

So, to find out if there is a fast train following, we need to count back eight signals before the entry signal and count the number of signals that are “on” - ie red. We would expect there to be two at danger - the E signal itself and the one immedialty behind the freight (4). If there are more than two signals we deduce that another train is following.

Note: consider the length of trains that will be using the line. If a train is so long that, upon passing signal 3, the tail end of it is still in rear of signal 4, then signals 3 and 4 will both be counted as red signals, and the train will be sent into the loop regardless of whether it needs to be.

So, the script for the M signal is:

    // Get the class of the train approaching

	string sClass=Train.GetRoute();

    // Is it a freight train?
	int bFreight=0;

		signal nSig;
		nSig=Signal;            // This signal
		nSig=nSig.PrevSignal();	// Entry Signal
		nSig=nSig.PrevSignal();	// Signal before entry

		int iSigOn=0;       // Number of signals "on"

		int n=8;
		while(n)           // Count back through 8 signals

			if(nSig.IsOn())        // If signal is "on", increment count

			nSig=nSig.PrevSignal();     // Move to previous signal

		if(iSigOn>2)              // If more than 2 on, prevent main route from setting
			return 0;
	return 1;


  • The script checks eight signals before the junction entry (you may choose to adjust this to nine, it does depend on speed differentials.)
  • If more than two signals are on, then there is a following train, so we return 0 to prevent the route (to the main signal) from settings
  • If there is no following train, then we allow the route to set.

Thus, where there is a following train, the route to the main signal (M) is blocked and the train will be routed into the loop by virtue of the free route option on the point.

So far so good - but we need to stop the freight just running through the loop and carrying on. We could just put a stop in the loop - and this would probably be sufficient, but we can also do it with a script on the L signal:

	float Dist=Signal.GetDistance(Train);     // Get the distance to the train
	if(Dist<100)                     // and don't clear until the train gets close to the signal
		signal nSig;
		nSig=Signal.PrevSignal();     // Check the previous signal

		if(nSig.IsOn())         // OK to clear, if previous signal is on
			return 1;


	return 0;                           // Otherwise don't clear.
  • The signal won’t clear until the train is less than 100 metres away - this has the effect of slowing the train down as it runs through the loop.
  • When the freight gets close, the script checks the previous signal - the entry signal.
  • If there is a fast train coming up behind, it will probably be close enough to set the entry signal itself, so if the entry signal (L) is off, don’t clear the loop exit signal.
  • Otherwise, all ok, let the freight out of the loop.

You can download an example package from http://www.rail3d.info/packages/GoodsLoopTest.trp you’ll need build 102.3 or later to open it


I have now packaged the above scripts with two signal models, to make it easier to apply the signals and scripts. The two signal models are:

  • 4OG_GLM (4 aspect, Overhead Gantry, Goods Loop, Main) for the mainline passing the loop (M in the above diagram)
  • 4OG_GLX (4 aspect, Overhead Gantry, Goods Loop, eXit) for the exit signal of the goods loop (L in the above diagram)

Further issues

The above code is not ideal - but it works pretty well in most circumstances. There are some refinements that might be worth considering:

  • What if the train is longer than the block length? (See note above)
  • What if the freight is longer than the loop? - ideally the script would compare the length of the train with the length of the loop and not loop a train that is too short.


I have now updated the script (and it’s in the library with the above signal models) to measure the length of the approaching train and the length of the loop and not put a train in the loop if it is too long.