Part 1 — the plain-English version
Predicting when the barriers come down
In short: we watch the railway's own signalling to see trains approaching Mount Pleasant Road crossing, and we've learned from real data exactly how long each train takes to arrive — so we can tell you, to within about a minute, when the barriers will next close.
A level crossing is annoying mostly because it's unpredictable. You can see a barrier is up now, but that tells you nothing about whether you've got four clear minutes or you're about to wait for a train. The goal of this project is to turn "maybe" into "the barriers will come down at 18:53, for about 90 seconds."
How can a website possibly know that?
Every train on the national network is tracked by the signalling system. As a train moves, it steps from one short section of track to the next — think of it like beads sliding along a string. Each section has a name, and the signalling broadcasts "train 1W35 just moved into section 0352" in real time. We listen to that feed.
The crossing is protected by four signals. When the signaller sets a route for a train over the crossing, that's the moment the barriers drop. So if we can see a train a few sections away and know how fast it's moving, we can count down to that moment.
The hard part: not all trains, or track, are equal
Early on, our guesses were often wrong by several minutes. The reason turned out to be simple, and it's the heart of how this works now:
- Track sections are wildly different lengths. One might be a few hundred metres of fast plain line; the next a long, slow stretch where freight crawls. Assuming every section takes the same time was hopeless — some take 25 seconds, some take over ten minutes.
- Some trains stop at the station. St Denys station platforms sit right on the approach. A train booked to stop there dwells a minute or two longer than one running straight through — so we have to know, from the timetable, which kind each train is.
- Some routes look like approaches but aren't. A couple of tracks are technically two-way but in practice only ever used one way; the map said trains could cross from them, but real trains never do.
So instead of guessing, we measured. We watched thousands of real train movements and learned, for every section of track, how long a train actually takes through it — and separately for trains that stop at the station versus those that don't. The prediction is just adding those measured times together along the train's path.
Two ways of knowing, cross-checked
- Live tracking — where trains physically are right now, second by second. Best for the next few minutes.
- The timetable — what's scheduled to cross, adjusted for each train's live delay. Best for looking further ahead.
We keep ourselves honest
Every prediction is checked against what actually happened — did that train really cross, and how close was our timing? Those scores are public on the scoring page. And the system re-measures itself every night from the latest data, so as track, timetables and traffic change, the predictions stay calibrated.
What we genuinely can't know
Some things are simply invisible to us, and it's only fair to be upfront about them. A signaller can hold the barriers down by hand — through several trains in a row, or to manage road and rail traffic together. When that happens the crossing staying shut isn't a mistake on our part; we just can't see the signaller's intent. Trains can also divert or be cancelled after we've predicted them, so they never arrive.
One subtlety about what "barriers down" means. The closure happens through a sequence: the barriers lower, the crossing is confirmed clear, the signaller's route over the crossing is set, and that route "proves" — which is the step we see in the data. We used to worry this lagged the real barriers-down by an unknown amount. But standing at the crossing and timing it, the barrier sequence starts essentially the moment the route is set — so the signal we track is a faithful, near-immediate marker of the barriers coming down, not a delayed one. (If you can help us measure the last few seconds precisely, see below.)
That same fact shapes the forecast. The signaller sets the route ahead of the train — so the barriers start coming down before the train reaches the crossing, and by a different amount on each line (much further ahead on the faster Down line than the Up). We measure that per-direction lead from the data and subtract it, so the time we give you is when the barriers drop, not when the train arrives.
Part 2 — the technical version
Under the hood
The track layout — and why it's tricky
First, the ground truth, because none of the rest makes sense without it. Mount Pleasant Road crossing
sits just south of St Denys station, on the busy approaches to Southampton. The signalling divides the
railway into short sections called track circuits (in the data, "berths" — each has a 4-digit id like
0703); a train lights up one section at a time as it moves, and that's the signal we track.
A few features of this specific location are what made naïve prediction fail, and they shape the whole model:
- Several parallel lines. Trains approach on separate Up and Down lines. (Railway convention: "Up" is the direction towards London, "Down" is away from it — here, Up runs towards London Waterloo, Down towards Bournemouth.) Each is split into a Fast line (for non-stop expresses) and a Slow line (for stopping services), and there's also a goods line for freight. The crossing is protected by four signals — 707/709 on the Down, 708/710 on the Up — and which signal has a route "set" (a path locked in for a train to proceed) tells us which direction the closure is for.
- The station platforms are on the immediate approach. A train calling at St Denys sits on the platform track circuit right before the crossing. So a booked stopper takes a minute or two longer to arrive than a train running through — from the same section of track. We can't tell these apart from position alone; we need the timetable.
- The goods line has very long, slow sections. A freight can occupy a single track circuit for many minutes. The "one hop" that takes 25 seconds on fast plain line can take ten minutes or more here — which is exactly why assuming a fixed time per hop was hopeless.
- Bidirectional tunnel tracks. Just up from Southampton Central, a tunnel carries two tracks that are signalled as bidirectional but, in day-to-day operation, run one Up and one Down. The signalling topology therefore offers "valid" routes over the crossing from sections that, in practice, only ever carry traffic the other way — plus a genuine handful of wrong-direction moves (single digits per day).
- Sidings and divergences. Some sections lead into sidings or onto routes that never reach the crossing at all.
The last two points are the crux: the signalling topology tells us what's physically possible, not what actually happens. If we trusted it blindly we'd forecast closures from track that never feeds the crossing. So we learn the operational direction from observed movements (see "spurious-berth pruning" below), rather than from the wiring diagram alone.
Data sources
- Network Rail TD feed (Train Describer, EH signalling area) over STOMP — C-class messages are
berth steps (
from → towith a train describer / headcode); S-class messages are signalling bit states. This is the live position firehose. - CIF timetable — the full daily SCHEDULE extract, filtered to the corridor, STP-resolved (overlays/cancellations) per running day. Each schedule's St Denys calling point tells us pass vs stop.
- TRUST — live train running / cancellation / delay, joined by headcode to overlay the timetable.
Detecting a closure
Mount Pleasant is protected by signals 707 / 709 (down) and 708 / 710 (up). The barriers-down
proxy is a route set over the crossing from one of these signals — read from specific S-class route bits.
A train physically clearing the crossing (a step on a known exit edge, e.g. 0709→0713) marks
barriers-up and confirms which train actually crossed.
Tier 1 — live forecast (no timetable)
A reverse breadth-first search over the signalling stepping table, starting from the four crossing berths, labels every upstream berth with its path to the crossing. The key refinements:
- Measured per-edge transit. For every
berth→berthstep we measure the median transit time from captured movement data. A berth's ETA to the crossing is the sum of those measured medians along its path — not a flat per-hop constant. Track circuits vary ~16× in length/speed, so this is the single biggest accuracy gain. - Platform-stop dwell. The St Denys platform berths are bimodal — booked stoppers dwell ~1–2 min longer than trains passing through. We detect these berths by conditioning observed dwell on CIF stop-status (arrival/departure vs pass at the calling point), and add the measured extra dwell only for trains the timetable says will stop.
- Spurious-berth pruning. Some berths are topologically connected to the crossing but never reach it in practice (sidings, the de-facto-one-way tunnel tracks). We measure each berth's reach rate — what fraction of trains there actually go on to cross — and prune those that effectively never do, so we don't make doomed predictions.
Tier 2 — timetabled forecast
CIF schedules are STP-resolved for the day, projected to their booked St Denys time, shifted by each train's live TRUST delay, with cancelled trains dropped. Back-to-back trains merge into a single closure window.
Scoring
When a train crosses, its prediction is credited as a hit only if it crosses within a grace window of the predicted time — matched to the right prediction for that headcode (headcodes recur through the day, so naïve matching corrupts the metric). We report MAE (mean absolute timing error on hits), precision (of predictions, how many happened) and recall (of closures, how many we called). A train held on approach and re-predicted from a closer berth has its earlier prediction marked superseded, not missed.
| Metric | What it means |
|---|---|
| MAE | how far off our timing is, on average, for trains that crossed |
| Precision | of the predictions we made, how many resulted in a real closure |
| Recall | of the closures that happened, how many we predicted |
Known limitations
Several things bound the achievable accuracy, and they're worth stating plainly:
- We see the last step of the sequence, not the barriers. A closure actually unfolds as
lower barriers → confirm crossing clear → set route → route proves. Our barriers-down proxy is the final step — the route proving — because that's the only one published on the feed. So the physical barriers are down for some seconds before our detection, and our reported down-time is correspondingly late by a roughly fixed lead. Nothing in the data precedes route-proving, and the earlier steps are partly signaller-paced, so we can't derive this lead from the feed — we need external ground truth to measure it (see below). The code carries a ready offset (BARRIER_LEAD_SEC, currently 0) that will shift the public "barriers down" time the moment we have a measured value — without affecting the accuracy scores, which compare predicted vs actual route-proving and so are self-consistent regardless. - Manual / local signaller control. The barriers-up proxy is "a train has cleared the crossing". But a signaller can hold the barriers down manually — through a sequence of trains, or to manage road traffic — so a train clearing does not reliably mean the barriers lift. We have no feed for the signaller's intent or the physical barrier state itself.
- Back-to-back merging. Trains arriving close together are merged into one closure window; the exact moment the barriers rise within a tight sequence is the signalling's call, not something we observe.
- Post-prediction changes. A train can divert, terminate short or be cancelled after we've forecast it — it then never crosses. This is the dominant source of "missed" predictions and the hard ceiling on precision.
- Wrong-direction & non-corridor moves. We model normal running (Up on the Up line, Down on the Down). Rare bidirectional/wrong-line moves, light engines and shunts are pruned out and not forecast.
- Engineering possessions. With the line blocked for works there's no service to predict and the signalling behaves atypically.
- Signalling quirks. A route bit can occasionally stick on; we guard against it with a "wedge" heuristic (flagging a route still set long after a train cleared), but it remains a heuristic.
This is why the closure record — measured directly from the signalling — is more dependable than any single prediction, and why we publish honest, self-scored numbers rather than one headline figure.
Can you help us see the barriers? 📷
The one thing we can't get from the data feed is the real moment the barriers go down — and the lead between that and what we detect (the "route proves" step). To measure it we need ground truth from the crossing itself:
- A one-off observation — even standing at the crossing with a stopwatch for a dozen closures, noting when the barriers physically drop versus when this site flips, would give us a usable average offset.
- A hosted camera — if you have a window, balcony or premises with a clear view of Mount Pleasant Road crossing and would be willing to host a small camera, we could calibrate continuously and automatically.
Either would directly improve the times everyone sees. If you can help, please get in touch: [email protected].
Continuous recalibration
Each night the system re-measures per-edge transit times, platform dwells and spurious berths from a rolling window of recently captured movement data, writes them to disk, and the live model hot-reloads them — no restart, no human in the loop. Predictions improve as the dataset grows and adapt as the railway changes. Captured data is archived off-box and pruned locally so the calibration loop never fills the disk.
Soton Crossings currently covers Mount Pleasant Road at St Denys; once the technique is proven here, Adelaide Road (a simpler layout) will follow. Built on open Network Rail data feeds (NROD). Live view: sotoncrossings.co.uk · Scoring: /stats