Model Train-related Notes Blog -- these are personal notes and musings on the subject of model train control, automation, electronics, or whatever I find interesting. I also have more posts in a blog dedicated to the maintenance of the Randall Museum Model Railroad.
2023-04-03 - Conductor 2: Changing Blocks
Category Rtac
As noted above, during the first test running “live” with JMRI, all routes instantly became in error as soon as the engine moved and activated the next block for BL or PA.
This is due to the simulator being too naive: it instantly changed from one block to the next one, in perfect sync. This made it “perfect” when I was debugging the engine. But unfortunately, that’s not how reality works: when an engine crosses a block boundary, both blocks become temporarily active since the engine bridges both blocks. And depending on the speed of the engine, this can be anywhere from a second to a handful of seconds.
It’s actually even a bit worse than that as even non-powered car wheels can bridge two blocks for a fraction of a second.
That’s not exactly news and in the design of Conductor 2 I had clearly specified we should account for two blocks to be active.
However, the sequence route manager implementation is also too idealistic: I was trying to compensate for this, but I only compensate for the next block to be active. The problem is that as soon as an engine starts crossing a block boundary, that next block does become active (which is handled), and thus becomes the new “current” block. That means now that the old trailing block is active, and that’s not supported. Duh. I didn’t see that one coming:
- Train direction →
- “occupied” represents where the engine thinks the train is located.
- “active” represents the actual track sensor’ state. They should ideally match.
- Step 1: [ block 1 -- empty ] [ block 2 -- occupied active ] [ block 3 -- empty ] [ block 4 -- empty ]
- Train moves from block 2 to block 3, we temporarily have blocks 2+3 active:
- Step 2: [ block 1 -- empty ] [ block 2 -- occupied active ] [ block 3 -- active ] [ block 4 -- empty ]
- Block 3 becoming active is expected. The engine knows the train is moving to block 3:
- Step 3: [ block 1 -- empty ] [ block 2 -- active ] [ block 3 -- occupied active ] [ block 4 -- empty ]
- Error! We did not expect the previous block to be active too!
Here’s an easy solution: when we move an engine along the blocks’ sequence, we mark the old block as “trailing”. There can be only one “trailing” block in a sequence -- it’s the one we come from. That’s also part of the Conductor 2 design, so we're going to use this property here.
If we wanted to be cautious, we could filter and only take trailing blocks whose nodes have an outgoing link to the current one -- this way we know these are blocks “we come from”. In practice, this is supposed to be guaranteed by the fact we have only one active block, and it’s the last one that was active before we changed blocks.
That means that in order to fix the issue, we just need to allow:
- The “next block in the sequence” to be active.
- The “trailing block” (aka the last one we came from) to be active.
Now our state machine looks like this:
- Train direction →
- “occupied” represents where the engine thinks the train is located.
- “trailing” represents where the engine knows the train was before.
- “active” represents the actual track sensor’ state. They should ideally match.
- Step 1: [ block 1 -- empty ] [ block 2 -- occupied active ] [ block 3 -- empty ] [ block 4 -- empty ]
- Train moves from block 2 to block 3, we temporarily have blocks 2+3 active:
- Step 2: [ block 1 -- empty ] [ block 2 -- occupied active ] [ block 3 -- active ] [ block 4 -- empty ]
- Block 3 becoming active is expected. The engine knows the train is moving to block 3:
- Step 3: [ block 1 -- empty ] [ block 2 -- trialing active ] [ block 3 -- occupied active ] [ block 4 -- empty ]
- This state is accepted: the trailing block can be temporarily active while the train fully moves to block 3:
- Step 3: [ block 1 -- empty ] [ block 2 -- trialing ] [ block 3 -- occupied active ] [ block 4 -- empty ]
This requires updating the map to display the trailing blocks, as that’s useful debug information for me to have.
Update: later I will discover I forgot about reversing blocks. A reversing block is interesting because the “previous” trailing block (where we come from) is also the next outgoing block.