Passing Loops On Single Lines
Introduction
Any lengthy piece of single track needs a passing loop, but how do you decide when trains should take the loop line or the main line? This article will show you how to script a passing loop to decide which line the trains should take.
1 Background
1.1 Terminology
Some definitions:
- loop - the section of double track where trains can cross, including the points at either end
- Main Line - the straight line through the loop (not usually speed restricted)
- Loop Line - the other line (usually speed restricted), i.e. not speed restricted
- (a) cross - an event where two trains approach each other from opposite directions and pass each other at/in the loop
1.2 Existing methods
You can, of course, set up multiple passing loops on a single line in Rail 3D without needing to script anything. If you plan to cross trains at passing loops, then you’ll usually have one of the following arrangements in place:
- All trains in one direction take one line, and all trains in the other direction take the other line, or
- The points at either end of the loop are set for “Free Route”, so trains work out themselves which line to take, or
- The points are manually configured to send particular trains down particular lines.
These methods all work, however there are drawbacks with each. Their respective cons are:
- All the trains in one direction will be taking the loop line, whether they need to cross any other trains or not. This means they’ll slow down unnecessarily.
- This method could potentially lead to impasses. If one train is able to enter the loop (on either line), but not exit the loop, then another can come from behind and use the other line in the loop. This means you have two trains waiting at a loop to proceed in the same direction. If a train in the next loop is waiting to proceed in the opposite direction, there may be nowhere for each train to go.
- This is probably the best unscripted method but probably the most time-consuming to arrange. It most likely relies on trains running to schedule, but if trains are off-schedule then it’s probably not much of an issue.
However, if you want overcome these problems, we can use scripts.
2 How to script it
2.1 Diagram
Let’s take a typical passing loop:
Each direction has “distant” and home signals, and each track in the loop has an exit signal at either end. I’ll be presenting this with colour light signals, but you could easily do the same with semaphores.
2.2 Objectives
To get it as close to reality as possible, we should keep the following points in mind:
- trains must not be sent into the Loop Line if there is no oncoming traffic
- if there is to be a cross, the first train to arrive at the loop should use the Loop Line (to reduce delay times to both trains)
- there must not be any impasses
Note: this solution does not allow for overtaking manoeuvres, whereby a slow train pulls into the loop to allow a fast one to overtake.
2.3 Layout setup
You should set up the loop as depicted in the diagram above, also noting that:
- all signals have the Controlled flag set
- signals 6 and 23 have the Lock Through flag set, and are therefore effectively distant signals because trains will never have to stop at them
- signals 8 and 21 have the Hold flag set
- the points at either end of the loop:
- have their default route set for the Main Line
- have “*” entered in the Free Route box
We also need to use some other hidden signals, which are depicted as dim/grey signals in the following diagram:
Additional things to take into account:
- Signals 4, 7, 24 and 27 have the Hidden and Repeater flags set — shown with “grey” aspect for this purpose)
- Signals 12, 14, 15, and 17 have the Hidden and Lock Through flags set — these are not repeaters, so they still function as normal signals, albeit hidden
Note: This passing loop is the only passing loop in this single line section. At either end of the single line section is normal double track (not shown in diagram). It is near impossible to script this for multiple passing loops on a single line within the current capabilities of the scripting system. However, should certain other features be added, this would become feasible.
2.4 The plan
This is how the scripts will work:
- Trains from the double track section at either end can enter the single line section and proceed towards the loop under the operation of the Rail 3D signalling system.
- Signals 12, 14, 15 and 17 are used purely for forcing trains to take one line through the loop instead of the other.
- Upon passing hidden repeaters 4 or 27, a script checks if any oncoming trains are in or approaching the loop.
- If there is no oncoming traffic, the hidden signal in the Loop Line has its Hold set, and the hidden signal on the Main Line has its Hold removed. The relevant home signal then has its Hold removed, allowing the signals to clear for the Main Line.
- If there is oncoming traffic:
- If the oncoming train has been signalled as far as (but no further than) the home signal at the start of the loop, then we should take the Loop Line in order to expedite the cross.
- If the oncoming train has already been routed into one of the loop lines, then we should take the other line.
- The hidden repeater has its Hold flag set, to signify a “Train In Loop Area” flag.
- Upon passing the home signal, a Hold is applied to the “distant” in rear to prevent other trains from following.
- Loop exit signals are controlled normally using the Rail 3D signalling system, and will clear as soon as possible.
- Upon leaving the loop on the other side, the Hold on the “distant” is removed to permit another train to follow. The “Train In Loop Area” Hold on the hidden repeater in rear of the “distant” is also removed.
2.5 Example of operation
The following sequence of diagrams shows two trains approaching the loop, where the eastbound train arrives first. Occupied track is red, and track with a route set over it is green.
Upon passing signal 4, a route for the eastbound train is set into the Loop Line, as an oncoming train was detected. (Note that signal 8 shows “Clear”, even the Caution aspect on signal 12 won’t ever be seen by the train because signal 12 is hidden. This problem will be addressed in the remainder of the article.)
The eastbound train pulls into the loop, and the westbound train approaches the loop. Normally signal 21 would have cleared by now, but in this case there is an additional interlock (not described in this example) between signals 8 and 21, as fouling protection:
The eastbound train is now safely in the loop, so the route is set for the westbound train to pass through the Main Line.
Both trains can now exit the loop:
2.6 Adding some scripts
Each direction has three scripted signals, making a total of six signals that need to be scripted. The scripts have been written such that it’s easy to apply them to the other direction, simply by changing the reference of the variables at the start of each script.
Shown here are all the scripts for the eastbound signals only. You can create the scripts for the westbound signals by simply changing the signal numbers in the variable declaration lines.
OnTrain()
{
int OpposingMovement=0;
signal RevDist=GetSigByID("23");
signal RevOuter=GetSigByID("27");
signal RevRouteMain=GetSigByID("15");
signal FwdHome=GetSigByID("8");
signal FwdRouteMain=GetSigByID("14");
signal FwdRouteLoop=GetSigByID("12");
// Check for oncoming traffic
if(RevDist.IsOn()==0)
{
OpposingMovement=1;
}
// Check for train in loop area
if(RevOuter.IsHold()!=0)
{
OpposingMovement=2;
}
// Choose loop road
if(OpposingMovement==0)
{
// No oncoming trains; set for main road
FwdRouteMain.SetHold(0);
FwdRouteLoop.SetHold(1);
}
if(OpposingMovement==1)
{
// Oncoming train yet to reach loop
FwdRouteMain.SetHold(1);
FwdRouteLoop.SetHold(0);
}
if(OpposingMovement==2)
{
// Oncoming train has already decided routing
if(RevRouteMain.IsHold()==0)
{
// Opposing movement is for main road
FwdRouteMain.SetHold(1);
FwdRouteLoop.SetHold(0);
}
else
{
// Opposing movement is for loop road
FwdRouteMain.SetHold(0);
FwdRouteLoop.SetHold(1);
}
}
FwdHome.SetHold(0);
// Set Train In Loop Area flag for this direction
Signal.SetHold(1);
}
OnTrain()
{
signal FwdDist=GetSigByID("6");
// Prevent trains from loop in rear from following
FwdDist.SetHold(1);
if(Signal.IsJct()<>0)
{
Train.SetLimit(11);
}
}
OnSetLamps()
{
signal FwdLoop=GetSigByID("18");
signal FwdMain=GetSigByID("20");
// Route-forcing signals can "steal" the caution aspect
if(Signal.IsOn()==0)
{
if(Signal.IsJct()==0))
{
if(FwdMain.IsOn()!=0)
{
Signal.SetLamp(1,255,110,0,0);
}
else
{
Signal.UnsetLamp(1);
}
}
else
{
if(FwdLoop.IsOn()!=0)
{
Signal.SetLamp(2,255,110,0,0);
}
else
{
Signal.UnsetLamp(2);
}
}
}
else
{
// Revert to normal state table once returned to danger
Signal.UnsetLamp(1);
Signal.UnsetLamp(2);
}
}
OnTrain()
{
signal FwdOuter=GetSigByID("4");
signal FwdDist=GetSigByID("6");
signal FwdHome=GetSigByID("8");
// Reset signals to allow another train in rear to follow
FwdOuter.SetHold(0);
FwdDist.SetHold(0);
FwdHome.SetHold(1);
}
2.7 Detailed explanation
This is a step-by-step explanation of what happens, so be prepared for some trickiness!
The first script run when a train is approaching the loop is the one on signal 4:
- Firstly, all the signal variables are defined, using Fwd to denote signals in the direction of the train activating the script, and Rev to denote signals in the opposite direction. They’re pretty self-explanatory.
- We check for oncoming traffic by:
- Seeing if the “distant” in the opposite direction (in this case, signal 23) has been cleared. If it has, then the variable OpposingMovement is set to 1.
- Seeing if the hidden repeater in the opposite direction (signal 27) has had its Hold flag set. This Hold flag is set by oncoming trains when they pass signal 27, and signifies “Train In Loop Area”. If this flag is set, it means that the oncoming train has already decided which line in the loop it will take. In this case, OpposingMovement is set to 2.
- Now we choose which line in the loop the train will take. This is determined by examining the variable OpposingMovement, which will have one of three values:
- OpposingMovement = 0 — no oncoming trains detected. The hidden signals in the loop (signals 12 and 14 in this instance) have their Holds set/unset so that when signal 8 has its Hold removed, the route will be set for the Main Line.
- OpposingMovement = 1 — oncoming train detected, but it isn’t near the loop yet. Because we are the first to arrive, we should take the Loop Line in order to expedite the cross. The hidden signals (signals 12 and 14) have their Holds unset/set so that the train is forced into the loop.
- OpposingMovement = 2 — oncoming train detected, in vicinity of loop. The other train has already decided which line it is going to take, which implicitly decides which line we are taking. The hidden loop signals in the other direction are then examined to see which line the oncoming train is taking, and then the hidden signals in our direction are set to force our train into the other line.
- The Hold on signal 8 is removed so the train will get a route into one of the loop lines.
- The Hold on this signal (signal 4) is set, denoting “Train In Loop Area”.
After all that, the train arrives at the home signal for the loop. Here’s what happens on signal 8:
- The distant in rear, signal 6, has its Hold flag set to stop trains from following this one. If we didn’t prevent other trains from following, then we could get a second train arrive at the loop potentially with have nowhere to go.
- A speed limit is imposed for the train if it is taking the Loop Line (determined by exmaining if the signal has the Junction state, which is why it is important that the points have the default route set for the Main Line).
You’ll see there is an OnSetLamps() procedure, which is necessary when using colour light signals. The hidden signals in the loop that force trains into one line or the other might be hidden, but they are still part of the signal sequence. Therefore, a train routed into the loop might see “All Clear” when in fact the signal at the other end shows “Stop”. This is because the hidden signal inbetween is showing “Caution”. To fix this, we use a OnSetLamps() script to set the lamps on the home signal manually.
- If the signal is not on (i.e. not showing “Stop”):
- If the relevant loop exit signal (which one that is depends on which line is being taken) is at “Stop”, we need to modify the lamps to show “Caution”.
- If the next signal is not at “Stop”, then the lamp’s control should return to the signalling system.
- If the signal is On, then control of the lamp should revert to the signalling system.
This example is for a signal model where a different lamp is used to signify diverging movements, so you will need to adapt the script as necessary for your own situation.
The last script is on a hidden repeater signal, signal 24, once a train has exited the loop. It removes the Holds from signals 4 and 6, thus removing the “Train In Loop Area” flag and also allowing another train to follow. A Hold is also applied to signal 8.
2.8 Resetting the loop
If the Holds become messed up for some reason, you should be able to reset the loop. This can be done by running a train in one direction through the entire single line section (including the passing loop), instructing it to pass signals at stop as required, and then running a train in the opposite direction over the whole section.
2.9 Notes
- As an alternative, you could apply the script from signals 7 and 24 to signals 11/13 and 18/20, respectively, thus eliminating the need to have signals 7 and 24.
- I designated the home signals as Four Aspect to account for the presence of the hidden repeaters in the loop. Without it, a route will not be set out of the other end of the loop until a train is just about to enter the loop, due to the presence of the hidden repeater and the way that Rail 3D clears signals only as far ahead as necessary to avoid having a train facing anything other than “All Clear”. It also means the Caution message gets passed to the train earlier so that it has time to stop if need be.
- Setting lamps is not necessary when semaphore signals are used.
- You may find that the hidden repeaters work better if placed on the first node inside the loop, as this ensures the Caution message is passed to the train with as much time as possible.
- Occasionally a train might end up waiting in the Main Line. This occurs when it sets for the Main Line, thinking that no other oncoming trains are present, however before reaching the loop, an oncoming train routes up to the loop on the other side. It does not result in an impass.
- You might want to interlock the two home signals to avoid simultaneous entry into the loop.
- The speed limits for the Loop Line can also be set by having a Speed Limit just inside the Loop Line. The train should see this from some distance back and slow in time for the loop. I have found, however, that trains taking the Main Line also see this speed limit for some reason, and erroneously slow down while approaching the loop, only to speed up again.
import