A level crossing is very dangerous as trains travel at a high speed and are often very quiet. They kill instantly whilst leaving no trace.
As such, a level crossing warning system is a very good idea.
To use this system, one needs a luacontroller, digilines, and an Andrew's Cross. When a train approaches, it causes a digiline signal and then this is interpreted by the luacontroller and sent to the Andrew's Cross.
This code is also compatible with traffic lights from the streets mod.
When using TSS interlocking, you can lock Andrew's crosses to a flashing state for as long as a route is held by punching them like any other passive component. This may not be suitable for 100% of cases, since you may only want the crossing activated for part of the route.
In the future, it is hoped Andrew's crosses will be linkable directly to section occupation, meaning any train will activate the cross without further setup, making the whole system safer
Track:
-- Andrew's cross activation code. Compatible with traffic signals. Use channel "a" using digilines connected to tracks. __approach_callback_mode = 2 if event.type == "approach" then digiline_send("a", "RED") end if event.type == "train" then digiline_send("a", "GREEN") end
Luacontroller:
-- Connect to rail with digilines. Turns ports on if there is a train. if event.type == "digiline" and event.msg == "RED" then port.a = true port.b = true port.c = true port.d = true end if event.type == "digiline" and event.msg == "GREEN" then port.a = false port.b = false port.c = false port.d = false end
Hereafter the generic term “Automatic Crossing Warning Device” (ACWD, or just warning device) will be used generically. This usually means Andrew's crosses but there may be others in future or in your special use case. In this implementation, name each warning device with the passive component naming tool according to this scheme:
and add entries to the S.acwd_crossings
table to let the environment know how many warning devices that crossing has. For instance the passive component names at one crossing location might be: Foo1
, Foo2
and Foo3
, and they will be registered with S.acwd_crossings.Foo = 3
Trains that approach a crossing will occupy it, and after they leave will free it. They occupy a specific approach to the crossing as well, so that, for instance, double track sections can be kept safe. They do this with calls to the environment code on LuaATC tracks on the approach and exit to the crossing.
The implementation can be unreliable in case a train misses the approach and/or exit tracks, so make sure all ways in and out of a crossing have tracks with appropriate LuaATC. It is also up to you to ensure the crossing does not go off too early or stay on too late; factor in the speed of the train, track speed limits and so on. Also, this implementation assumes that approaches are directly linked to exits, and that a train will clear the approach it used correctly. If you have a weird crossing like this, you may be able to bodge it by calling F.acwd_free
for both entrances that an exit has, but this code is untested so no guarantees :) I would add I don't usually recommend shunting over a level crossing :)
The code assumes your warning devices use the passive component states “on” and “off”. If they do not, it is left as an exercise to the reader to extend the code to be able to handle other states.
Example code for trains approaching crossing
if not event.train then return end F.acwd_occupy(atc_id, "garply", "west", "garply west approach")
Example code for trains leaving crossing
if not event.train then return end F.acwd_free(atc_id, "garply", "west", "garply west exit")
LuaATC environment code
-- ACWD automatic crossing warning devices -- (C) 2022 Blockhead licensed under the MIT license -- Based largely on code on LinuxForks server, but made without reading it -- recently and not fully tested. S.acwd_crossings = { -- Put the number of ACWDs at this crossing as an integer key e.g. foobaz = 1, garply = 2, schnitzel = 3, } -- locals for speed local acwd_occupation = S.acwd_occupation or {} --safe to re-run init code if next(acwd_occupation) == nil then for location, numcrossings in pairs(acwd_occupation) do -- Every occupation entry is a table of train IDs occupying it. acwd_occupation[location] = {} end end S.acwd_occupation = acwd_occupation -- Set occupation of a train with its train_id, at a crossing location, with -- an approach key to distinguish locations where trains can approach the -- crossing more than one way (e.g. a double track section will have 2 -- approaches). Give an optional debug_pos so bad calls can be debugged. function F.acwd_occupy(train, crossing, approach, debug_pos) if type(crossing) ~= "string" then error("ACWD: Crossing name is not a string! " .. "@"(debug_pos or "")) end n = acwd_crossings[crossing] or 0 if not n > 0 then error("ACWD: Crossing name not valid: '" .. tostring(crossing) .. "' @" .. (debug_pos or "")) end for wd=1,n do setstate(crossing .. tostring(wd), "on") end acwd_occupation[crossing][approach] = train end end -- Clear occupation of a train with its train_id, at a crossing location, with -- an approach key to distinguish locations where trains can approach the -- crossing more than one way (e.g. a double track section will have 2 -- approaches). Give an optional debug_pos so bad calls can be debugged. -- This will only set the warning devices back to off if there are no -- occupation entries left at all for the crossing! function F.acwd_free(train, crossing, approach, debug_pos) if type(crossing) ~= "string" then error("ACWD: Crossing name is not a string! " .. "@"(debug_pos or "")) end n = acwd_crossings[crossing] or 0 if not n > 0 then error("ACWD: Crossing name not valid: '" .. tostring(crossing) .. "' @" .. (debug_pos or "")) end -- Clear this approach acwd_occupation[crossing][approach] = nil -- There must be no trains on any approach to the crossing to -- be able to clear it and disable the warning devices. if next(acwd_occupation[crossing]) == nil then for wd=1,n do setstate(crossing .. tostring(wd), "off") end end end
-- The safest level crossing is no crossing. The path passes above or below the track and does not force people to cross the track. This track just keeps a train at mainline programming. atc_send("OCA1SM")
More information:
Network Rail Post (It basically says trains are going faster and there is more risk so they should close more rail crossings)