-- lzb.lua-- Enforced and/or automatic train override control, obeying signals--[[Documentation of train.lzb tabletrain.lzb = { trav = Current index that the traverser has advanced so far travsht = boolean indicating whether the train will be a shunt move at "trav" travspd = speed restriction at end of traverser travwspd = warning speed res. oncoming = table containing oncoming signals, in order of appearance on the path { pos = position of the signal (not the IP!) idx = where this is on the path spd = speed allowed to pass (determined dynamically) }}each step, for every item in "oncoming", we need to determine the location to start braking (+ some safety margin)and, if we passed this point for at least one of the items, initiate brake.When speed has dropped below, say 3, decrease the margin to zero, so that trains actually stop at the signal IP.The spd variable and travsht need to be updated on every aspect change. it's probably best to reset everything when any aspect changesThe traverser stops at signals that result in spd==0, because changes beyond there are likely.]]local il = advtrains.interlocking
local BRAKE_SPACE =10local AWARE_ZONE =50local ADD_STAND =2.5local ADD_SLOW =1.5local ADD_FAST =7local ZONE_ROLL =2local ZONE_HOLD =5-- added on top of ZONE_ROLLlocal ZONE_VSLOW =3-- When speed is <2, still allow acceleratinglocal SHUNT_SPEED_MAX =4local functionlook_ahead(id, train)local acc = advtrains.get_acceleration(train,1)local vel = train.velocity
local brakedst = -(vel*vel) / (2*acc)local brake_i = advtrains.path_get_index_by_offset(train, train.index, brakedst + BRAKE_SPACE)--local aware_i = advtrains.path_get_index_by_offset(train, brake_i, AWARE_ZONE)local lzb = train.lzb
local trav = lzb.trav
local travspd = lzb.travspd
local travwspd = lzb.travwspd
local lspd
--train.debug = lspdwhile trav <= brake_i and(not lspd or lspd>0)do
trav = trav +1local pos = advtrains.path_get(train, trav)local pts = advtrains.roundfloorpts(pos)local cn = train.path_cn[trav]-- check offtrackif trav > train.path_trk_f then
lspd =0table.insert(lzb.oncoming, {
idx = trav-1,
spd =0,})else-- check for signallocal asp, spos = il.db.get_ip_signal_asp(pts, cn)--atdebug("trav: ",pos, cn, asp, spos, "travsht=", lzb.travsht)if asp thenlocal nspd =0--interpreting aspect and determining speed to proceedif lzb.travsht then--shunt moveif asp.shunt.free then
nspd = SHUNT_SPEED_MAX
elseif asp.shunt.proceed_as_main and asp.main.free then
nspd = asp.main.speed
lzb.travsht =falseendelse--train moveif asp.main.free then
nspd = asp.main.speed
elseif asp.shunt.free then
nspd = SHUNT_SPEED_MAX
lzb.travsht =trueendend-- nspd can now be: 1. !=0: new speed restriction, 2. =0: stop here or 3. nil: keep travspdif nspd thenif nspd == -1then
travspd =nilelse
travspd = nspd
endendlocal nwspd = asp.info.w_speed
if nwspd thenif nwspd == -1then
travwspd =nilelse
travwspd = nwspd
endend--atdebug("ns,wns,ts,wts", nspd, nwspd, travspd, travwspd)
lspd = travspd
if travwspd and(not lspd or lspd>travwspd)then
lspd = travwspd
endtable.insert(lzb.oncoming, {
pos = spos,
idx = trav,
spd = lspd,})endendend
lzb.trav = trav
lzb.travspd = travspd
lzb.travwspd = travwspd
--train.debug = dump(lzb)end--[[Distance needed to accelerate from v0 to v1 with constant acceleration a: v1 - v0 a / v1 - v0 \ 2s = v0 * ------- + - * | ------- | a 2 \ a /]]local functionapply_control(id, train)local lzb = train.lzb
local i =1while i<=#lzb.oncoming doif lzb.oncoming[i].idx < train.index then
train.speed_restriction = lzb.oncoming[i].spd
table.remove(lzb.oncoming, i)else
i = i +1endendfor i, it inipairs(lzb.oncoming)dolocal a = advtrains.get_acceleration(train,1)--should be negativelocal v0 = train.velocity
local v1 = it.spd
if v1 and v1 <= v0 thenlocal f = (v1-v0) / a
local s = v0*f + a*f*f/2local st = s + ADD_SLOW
if v0 >3then
st = s + ADD_FAST
endif v0<=0then
st = s + ADD_STAND
endlocal i = advtrains.path_get_index_by_offset(train, it.idx, -st)--train.debug = dump({v0f=v0*f, aff=a*f*f,v0=v0, v1=v1, f=f, a=a, s=s, st=st, i=i, idx=train.index})if i <= train.index then-- Gotcha! Braking...
train.ctrl.lzb =1--train.debug = train.debug .. "BRAKE!!!"returnend
i = advtrains.path_get_index_by_offset(train, i, -ZONE_ROLL)if i <= train.index and v0>1then-- roll control
train.ctrl.lzb =2returnend
i = advtrains.path_get_index_by_offset(train, i, -ZONE_HOLD)if i <= train.index and v0>1then-- hold speed
train.ctrl.lzb =3returnendendend
train.ctrl.lzb =nilendlocal functioninvalidate(train)
train.lzb = {
trav =atfloor(train.index),
travsht = train.is_shunt,
oncoming = {}}
train.ctrl.lzb =nilendfunction advtrains.interlocking.lzb_invalidate(train)invalidate(train)end
advtrains.te_register_on_new_path(function(id, train)invalidate(train)look_ahead(id, train)end)
advtrains.te_register_on_update(function(id, train)look_ahead(id, train)apply_control(id, train)end)