chiark / gitweb /
can fit 139 lines on an atp -B page
[trains.git] / hostside / safety.c
1 /*
2  * realtime
3  * safety core algorithm
4  */
5
6 #include <stdio.h>
7 #include <assert.h>
8
9 #include "realtime.h"
10
11 int n_trains;
12 Train *trains;
13 Segment *segments;
14
15
16
17 /*
18  * New signalling/safety algorithm:
19  *
20  * When we detect a train at the relevant det_expected we firstly
21  * update our knowledge of its current position.  We don't change
22  * any ownerships just yet, but we do set pred_present for the parts
23  * where the train is right now.
24  *
25  * We then check to see that when we reach the _next_ block boundary,
26  * we'll still be able to stop safely (unless we're already doing an
27  * emergency stop).  If this fails we issue the emergency stop command
28  * now.  This prediction is done as follows:
29  *
30  * We use three tracklocs which move roughly together - one for the
31  * foredetect and one for each end of our train.  This represents a
32  * predicted train position.  (We use `predicted' to refer to these
33  * future situations and `current' to refer to that prevailing at the
34  * time we are making the prediction.)  The advancement is driven
35  * using the foredetect; when it enters a new segment we advance first
36  * the head and then the tail by the same distance.
37  *
38  * We advance foredetect by the total of the uncertainty (which takes
39  * it into the next segment) and the current stopping distance.  At
40  * each advancement we record the earliest time from now at which this
41  * prediction will happen (computed from the current speed).  (We
42  * don't take into account any already commanded but not necessarily
43  * happened deceleration; however if we are attempting to increase our
44  * speed, current speed refers to the new speed.)  If we're already
45  * stopping then we advance by the stopping distance only (since we
46  * don't need to worry about not being told to stop until the next
47  * detection); also we stop advancing when we run out of segments we
48  * own (this deals with segment-sized rounding errors which might
49  * otherwise make us think we will overrun).
50  *
51  * At each new track location for our front end we hope to find a
52  * segment which no-one currently owns and which is currently set for
53  * us.  (We check this before updating our tail trackloc as below.)
54  * In that case we update the segment's until.)  (We use the value
55  * `elapsed' which is the minimum time until the current nose
56  * prediction.)
57  *
58  * However it may be a segment which we currently own, or perhaps it
59  * may be set against us.  (If it's owned by anyone else or is not
60  * owned but moving then we are definitely doomed and must stop.)
61  *
62  * If it's owned by us but we haven't claimed it yet in this
63  * prediction then that's fine we can just claim it.
64  *
65  * If we _have_ claimed it then it had better be predicted to be
66  * vacant by the time we get there (pred_present clear, see below).
67  *
68  * Regarding moveable features, we continuously develop our plan,
69  * as represented in route_plan and route_reservation.
70  *
71  * If the feature is currently moving and owned by us then that must
72  * be OK because we checked it before (except that if we're trying to
73  * increase our speed that may not be OK and we must replan the speed
74  * increase to check whether the movement will happen in time).
75  *
76  * If it is not currently moving and is set against us, then we plan
77  * to set it.  If there is already a plan (ie, a reservation) and
78  * we're not accelerating then that's fine.  Otherwise we make one: we
79  * determine the desired movposcomb make a reservation to move it in
80  * time.  (If the segment is already owned by us that means a
81  * reservation to move it between when the tail leaves and the nose
82  * enters.)
83  *
84  * When predicted foredetect enters a new segment we add the new
85  * segment to the tally of segments of the particular kind and
86  * train-relative orientation (for polarity purposes), and check that
87  * a solution is possible.  For the first such solution (ie, the one
88  * for the immediately-forthcoming detection) we actually implement
89  * it.
90  *
91  * And, when predicted foredetect enters a new segment we advance the
92  * predicted tail (as described above).  We set pred_vacated and
93  * until, and remove the departing segments from tallies.
94  *
95  * If all of this works then pred_present and pred_vacated constitute
96  * the new ownership and motion_newplan constitutes the new plan.  We
97  * commit to it by rewriting owner and det_* and actually requesting
98  * any movfeat changes which we can do now, and replacing motion with
99  * motion_newplan.
100  *
101  * If not then we issue an signal stop command (we'd better not be
102  * doing that already!) or countermand the speed increase or whatever.  */
103 /*
104  * Here is how we use the stuff in the Segment:
105  *
106  *  owner               train which owns this for signalling purposes
107  *                              no other train may interfere
108  *
109  *  physical            in an unowned segment, this has no particular
110  *  feature                     relationship to anything
111  *   &
112  *  movposcomb          in an owned segment, if moving, means that
113  *   &                          we have a promise that the feature
114  *  moving                      will be OK by the time we get there
115  *   &                          at our current speed
116  *  motion              if not moving then motion is our reservation which
117  *                              allows us to know that we are going to
118  *                              be able to change this segment between
119  *                              the tail leaving it and the head arriving
120  *                      in each case movpos_change_intent tells us the plan
121  *
122  *  motion_newplan      while we are making a plan this is the new value
123  *                              for motion.  If identical to motion it
124  *                              means we should keep it; if 0 we cancel
125  *                              or unreserve; if a different value we
126  *                              commit to the new plan instead
127  *
128  *                      during prediction, motion_newplan can be non-0 for
129  *                              unowned segments as we make our plan.
130  *                              If we succeed we will end up
131  *                              owning the segment, and if we fail
132  *                              we garbage collect it
133  *
134  *  det_ignore          this is part of the body of a train which we've
135  *                              already detected here
136  *
137  *  det_expected        this is the next place we expect owner train
138  *                              to arrive
139  *                      (it is not valid to have both det_ignore
140  *                       and _expected)
141  *
142  * VALID DURING PREDICTION ETC. ONLY
143  *
144  *  pred_present        in our prediction, the train physically occupies this
145  *                              until_here tells us when will be
146  *
147  *  pred_vacated        in our prediction, the train does not occupy this
148  *                              although it did in previous predictions
149  *                      until_here tells us when it will have been vacated
150  *                      if set together with pred_present, means was vacated
151  *                              and then reentered; do not record vacation
152  *                              again
153  *
154  *                      iff either of these is set, this train is going
155  *                              to own the segment when we're finished
156  *
157  *  now_present         train is in this segment now (in which we include
158  *                              the segment just before foredetect)
159  *
160  *  until               time until the we will arrive (pred_present), or
161  *                              depart (pred_vacated), valid only if
162  *                              one of those is set
163  *                              value is as for u->elapsed
164  *
165  * Valid combinations during prediction:
166  *
167  *                                         now_present
168  *                                               | pred_present
169  *                                               |/ vacated
170  *                                               ||/
171  *                                               |||
172  *                                               NPV  until
173  *
174  *   has never been here as far as we know       ---  undef
175  *   here now, prediction has it still here      NP-  arrival = 0
176  *   here now, but predicted to have departed    N-V  departure
177  *   pred. to have arrived here                  -P-  arrival
178  *   pred. to have arrived and departed          --V  departure
179  *   here now, pred. to have dep'd and ret'd     NPV  re-arrival
180  *   now absent, pred. to have arr, dep & ret'd  -PV  re-arrival
181  *
182  *  seg->tr_backwards records the sense of the _first_ passage of the train.
183  *
184  *  invalid/impossible combination:
185  *   train here now but never in prediction      N--
186  *
187  * Meanings of moving, motion and motion_newplan:
188  * For segments NPV==000, no plan made yet:
189  *
190  *     moving  motion  _newplan
191  *      0       0       0        segment is entirely stable
192  *      0       non-0   0        existing plan has deferred motion
193  *      1       non-0   0        currently moving - as planned if owned
194  *
195  *      1       0       ?        always forbidden
196  *      ?       ?       non-0    forbidden for not-yet-planned segments
197  *
198  * For segments with NPV!=0, some kind of planning done:
199  * 
200  *     moving  motion  _newplan  new plan involves
201  *      0       0       0         no motion, segment is entirely stable
202  *      0       non-0   0         abandoning planned deferred motion
203  *      0       0       non-0     new motion, perhaps deferred
204  *      any     non-0   =motion   same motion as before
205  *      any     non-0  !=motion   different motion
206  *
207  *      1       0       ?         always forbidden
208  *      1       non-0   0         forbidden: need explicit motion to cancel
209  *
210  * (These are the values when not in the middle of nose_nextseg.)
211  */
212 /*
213  * Simulation:
214  *
215  *                                DETECT
216  *  real stop dist                >>>>>
217  *  simul. stop dist                          >>>>>
218  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
219  *  set_exactinto                 FNT
220  *  adv. tail back        T       FN
221  *  adv. nose find        T       F N
222  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
223  *  adv. fdet 1 seg       T         N         F                
224  *   adv. nose            T                   F N
225  *   adv. tail                        T       F N
226  *  adv. fdet stopdist                T         N  F
227  *   adv. nose                        T            F N
228  *   adv. tail                             T       F N
229  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
230  *
231  * At next detection:
232  *                                            DETECT
233  *  real stop dist                            >>>>>
234  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
235  *  set_exactinto                             FNT
236  *  adv. tail back                    T       FN
237  *  adv. nose find                    T       F N
238  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
239  *  adv. fdet 1 seg                   T         N        F     
240  *   adv. nose                        T                  N     ProblemPredicted
241  *
242  * Oops, so stop:
243  *                                            DETECT
244  *  real stop dist                            >>>>>
245  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
246  *  set_exactinto                             FNT
247  *   adv. tail back                   T       FN
248  *   adv. nose find                   T       F N
249  *                    +++++++++++|+++++++++++|++++++++++|XXXXXX
250  *  adv. fdet stopdist                T         N  F        } may give
251  *   adv. nose                        T            F N      }  HorizonReached
252  *   adv. tail                             T       F N             instead
253  *
254  */
255
256 /*========== prediction machinery ==========*/
257
258 #define now_present mark0
259 #define pred_present mark1
260 #define pred_vacated mark2
261 #define will_polarise mark3
262
263 typedef struct {
264   Train *train;
265   unsigned
266     speed_info_specified:1,
267     neednewplan:1,
268     stopping:1,
269     done_first_new_fdet:1,
270     usecurrentposn:1, /* for {pred,report}_getmovpos */
271     optimistic:1, /* for autopoint */ 
272     walk_compute_polarise:1, /* nose_nextseg still needs to worry */
273     need_polarise:1, /* when we commit */
274     train_polarity_inverted:1, /* right now, or if know_best, the best */
275     know_best_polarity:1, /* longest-lasting into the future */
276     done_fdet:1; /* for report_nextseg */
277   TrackLocation nose, fdet, tail;
278   TrackAdvanceContext nosec, tailc, fdetc;
279   TimeInterval elapsed; /* from now, minimum */
280   Distance was_distance, autopoint_distance, stopping_distance;
281   double maxspeed;
282   Segment *hindmost, *furthest, *desire_move;
283   int desire_movposcomb;
284   int lookahead, reportcount;
285
286   PredictionProblemCallback *problem_callback;
287   void *problem_callback_u;
288
289   /* tally counts segments which have been entered in fdet_nextseg but
290    * not left in tail_nextseg (ie not the same ones as pred_present),
291    * in each direction (according to tr_backwards) */
292   int noninv_tally[2];
293 } PredictUserContext;
294
295 static int tail_length(Train *tra) {
296   return tra->backwards ? tra->head : tra->tail;
297 }
298 static int nose_length(Train *tra) {
299   return tra->backwards ? tra->tail : tra->head;
300 }
301 static void advance_elapsed(PredictUserContext *u, int advance_distance) {
302   u->elapsed += advance_distance / u->maxspeed;
303 }
304 static int calc_advanced(TrackAdvanceContext *c) {
305   PredictUserContext *u= c->u;
306   return u->was_distance - c->distance;
307 }
308 static void qprintf_position(Train *tra,
309    void (*qp)(const char *fmt,...) __attribute__((format(printf,1,2)))) {
310   qp("%s%s:%d+-%d",
311      tra->foredetect->tr_backwards?"-":"",
312      tra->foredetect->i->pname, tra->maxinto, tra->uncertainty);
313 }
314
315 #define SKELETON_PREDICT_USER_CONTEXT(u,tra)    \
316   (memset(&(u),0,sizeof((u))),                  \
317    (u).problem_callback= ppc,                   \
318    (u).problem_callback_u= ppcu,                \
319    (u).train= tra)
320  /* for callers of predict_problem rather than predict */
321
322 /*---------- prediction problem reporting ----------*/
323
324 static ErrorCode predict_vproblem(PredictUserContext *u, Segment *seg,
325                                   const char *fmt, va_list al) {
326   int l;
327   char *message;
328
329   l= vasprintf(&message, fmt, al);  if (l <= 0) diem();
330
331   if (u->optimistic)
332     DPRINTF(safety,predict,"   optimistic problem %s\n", message);
333   else if (!u->problem_callback)
334     safety_panic(u->train, seg, "unexpected problem predicted"
335                  " (context: %s): %s", (char*)u->problem_callback_u, message);
336   else
337     u->problem_callback(u->train, seg, u->problem_callback_u, message);
338
339   free(message);
340   return EC_SignallingPredictedProblem;
341 }
342
343 static ErrorCode predict_problem(PredictUserContext *u, Segment *seg,
344                                   const char *fmt, ...) {
345   ErrorCode ec;
346   va_list al;
347   va_start(al,fmt);
348   ec= predict_vproblem(u,seg,fmt,al);
349   va_end(al);
350   return ec;
351 }
352
353 static const char *motion_pname(Segment *seg, MovPosChange *motion) {
354   if (!motion) return "";
355   return movpos_pname(seg, movpos_change_intent(motion));
356 }
357
358 static void pred_callback_debug(const char *what, TrackLocation *t,
359                                 struct TrackAdvanceContext *c,
360                                 const TrackLocation *before) {
361   PredictUserContext *u= c->u;
362   
363   DPRINTF(safety,predictseg,"   %s"
364           " %c%s dist=%-4d until=%-4ld %c%c%c.%c%c (was %s%s..%d dist=%-4d)"
365           "  %c%c%c.%c%c%c%c elapsed=%ld la#%d"
366           " %s/%s %c%s >%s"
367           " nit=%d,%d\n",
368           what,
369
370           " -"[t->backwards],
371           t->seg->i->pname,
372           c->distance,
373           (long)t->seg->until,
374
375           "-N"[ t->seg->now_present ],
376           "-P"[ t->seg->pred_present ],
377           "-V"[ t->seg->pred_vacated ],
378           
379           "-p"[ t->seg->will_polarise ],
380           "-i"[ t->seg->seg_inverted ],
381
382           before && before->backwards?"-":"",
383           before ? before->seg->i->pname : "-",
384           before ? before->remain : -1,
385
386           u->was_distance,
387           
388           "-2"[ u->done_first_new_fdet ],
389           "-c"[ u->usecurrentposn ],
390           "-o"[ u->optimistic ],
391           
392           "-w"[ u->walk_compute_polarise ],
393           "-n"[ u->need_polarise ],
394           "-i"[ u->train_polarity_inverted ],
395           "-b"[ u->know_best_polarity ],
396           (long)u->elapsed,
397           u->lookahead,
398
399           t->seg->i->pname,
400           movpos_pname(t->seg, t->seg->movposcomb),
401           "~!"[ t->seg->moving ],
402           motion_pname(t->seg, t->seg->motion),
403           motion_pname(t->seg, t->seg->motion_newplan),
404
405           u->noninv_tally[0], u->noninv_tally[1]);
406 }
407
408 /*---------- prediction trivial callbacks ----------*/
409
410 static int pred_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
411                           MovPosComb *use_io) {
412   PredictUserContext *u= c->u;
413   if (!u->usecurrentposn &&
414       !t->seg->now_present &&
415       t->seg->motion_newplan)
416     *use_io= movpos_change_intent(t->seg->motion_newplan);
417   if (*use_io<0) safety_panic(u->train, t->seg,
418                               "track route unexpectedly not known");
419   return 0;
420 }
421
422 static int pred_trackend(TrackLocation *t, TrackAdvanceContext *c) {
423   PredictUserContext *u= c->u;
424   return predict_problem(u, t->seg, "end of track");
425 }
426
427 static int pred_trackend_panic(TrackLocation *t, TrackAdvanceContext *c) {
428   PredictUserContext *u= c->u;
429   safety_panic(u->train,t->seg,"unexpected end of track");
430 }
431
432 static int initpresent_nextseg(TrackLocation *t, TrackAdvanceContext *c,
433                                MovPosComb *mpc_io,
434                                const TrackLocation *before) {
435   PredictUserContext *u= c->u;
436   pred_callback_debug(" initpresent_nextseg",t,c,before);
437   t->seg->now_present= t->seg->pred_present= t->seg->will_polarise= 1;
438   t->seg->tr_backwards= !t->backwards;
439   t->seg->until= 0;
440   if (!t->seg->i->invertible)
441     u->noninv_tally[!t->backwards]++; /* ! since going backwards along train */
442   return 0;
443 }
444
445 /*---------- prediction nose advancement ----------*/
446
447 static int nose_nextseg(TrackLocation *t, TrackAdvanceContext *c,
448                         MovPosComb *mpc_io, const TrackLocation *before) {
449   PredictUserContext *u= c->u;
450   Segment *interferer;
451   MovPosComb route_plan;
452   TimeInterval max_ms;
453   ErrorCode ec;
454
455   pred_callback_debug(" nose_nextseg",t,c,before);
456   if (!before) return 0;
457
458   if (u->optimistic)
459     advance_elapsed(u,calc_advanced(c));
460
461   /* Is it empty ? */
462
463   if (u->stopping && u->lookahead == u->train->plan_lookahead_nsegs)
464     return EC_SignallingHorizonReached;
465
466   if (t->seg->owner) {
467     if (t->seg->owner != u->train)
468       return predict_problem(u, t->seg, "impeded by %s",
469                              t->seg->owner->pname);
470   }
471   if (t->seg->pred_present)
472     return predict_problem(u, t->seg, "will collide with itself!");
473
474   if (!u->optimistic) {
475     interferer= segment_interferes(c,t->seg);
476     if (interferer) {
477       if (interferer->owner && interferer->owner != u->train)
478         return predict_problem(u, t->seg, "impeded by %s at %s",
479                                interferer->owner->pname, interferer->i->pname);
480       if (interferer->pred_present)
481         return predict_problem(u, t->seg, "will collide with itself at %s!",
482                                interferer->i->pname);
483     }
484   }
485
486   /* What about the route ? */
487
488   if (t->seg->i->n_poscombs==1)
489     goto movement_ok;
490
491   if (t->seg->moving) {
492     assert(!t->seg->now_present);
493     if (!t->seg->owner)
494       return predict_problem(u, t->seg, "route not yet set");
495     if (!u->neednewplan) {
496       t->seg->motion_newplan= t->seg->motion;
497       *mpc_io= movpos_change_intent(t->seg->motion);
498       goto movement_ok;
499     }
500   }
501
502   int autopoint= u->train->autopoint && t->seg->autopoint;
503
504   if (t->seg->motion_newplan) {
505     route_plan= movpos_change_intent(t->seg->motion);
506     *mpc_io= route_plan;
507     goto movement_ok;
508   } else if (t->seg == u->desire_move && !t->seg->now_present) {
509     ec= movpos_findcomb_bysegs(before->seg,t->seg,0,
510                                u->desire_movposcomb, &route_plan);
511     assert(!ec);
512     if (route_plan != u->desire_movposcomb)
513       return predict_problem(u, t->seg, " proposed route would be against"
514                              " approaching train");
515     autopoint= 1;
516   } else if (t->seg->motion) {
517     /* We already have a plan. */
518     route_plan= movpos_change_intent(t->seg->motion);
519     if (!u->neednewplan) {
520       t->seg->motion_newplan= t->seg->motion;
521       *mpc_io= route_plan;
522       goto movement_ok;
523     }
524   } else {
525     /* Extend the plan. */
526     ec= movpos_findcomb_bysegs(before->seg,t->seg,0,*mpc_io, &route_plan);
527     assert(!ec); /* there must be _some_ route since
528                     we're moving into t->seg */
529   }
530   if (route_plan == *mpc_io && !t->seg->moving) {
531     /* Well, that's all fine then. */
532     goto movement_ok;
533   }
534   /* We can make it work but have to plan some motion: */
535   if (u->elapsed < 0)
536     return predict_problem(u, t->seg, "train arriving but route not yet set");
537   if (t->seg->pred_vacated) {
538     if (!t->seg->now_present)
539       /* And we're not even there yet!  This is too hard because we
540        * are too stupid not to change it straight away. */
541       return predict_problem(u, t->seg, "dynamic route too complicated"
542                              " - would need multiple queued changes");
543     max_ms= u->elapsed - t->seg->until;
544   } else {
545     max_ms= u->elapsed;
546   }
547
548   if (!autopoint)
549     return predict_problem(u,t->seg,"will not automatically set route");
550
551   ec= movpos_reserve(t->seg, max_ms, &t->seg->motion_newplan,
552                      route_plan, *mpc_io);
553   if (ec) return predict_problem(u, t->seg, "insufficient time to"
554                                  " set route: %s", ec2str(ec));
555   *mpc_io= route_plan;
556
557  movement_ok:
558   /* Now we definitely have a plan which sets a good route at t->seg. */
559   if (!t->seg->pred_vacated)
560     t->seg->tr_backwards= t->backwards;
561   t->seg->pred_present= 1;
562   t->seg->until= u->elapsed < 0 ? 0 : u->elapsed;
563   u->lookahead++;
564   return 0; /* yay! */
565 }
566
567 /*---------- prediction tail advancement ----------*/
568
569 static int tail_nextseg(TrackLocation *t, TrackAdvanceContext *c,
570                         MovPosComb *mpc_io, const TrackLocation *before) {
571   PredictUserContext *u= c->u;
572
573   pred_callback_debug(" tail_nextseg",t,c,before);
574   if (!before) return 0;
575
576   if (!before->seg->i->invertible)
577     u->noninv_tally[before->backwards]--;
578
579   if (before->seg->pred_vacated) return 0; /* only vacate once */
580   before->seg->pred_present= 0;
581   before->seg->pred_vacated= 1;
582   before->seg->until= u->elapsed < 0 ? 0 : u->elapsed;
583
584   return 0;
585 }
586
587 /*---------- prediction foredetect advancement ----------*/
588
589 static int fdet_nextseg(TrackLocation *t, TrackAdvanceContext *c,
590                         MovPosComb *mpc_io, const TrackLocation *before) {
591   PredictUserContext *u= c->u;
592   int advanced, r;
593
594   /* NB, on entry the movposcomb of the segment we're entering (and
595    * other similar details) may not be known or sufficient because
596    * when fdet has only just entered a new segment, nose is still in
597    * the previous segment.
598    */
599
600   pred_callback_debug("fdet_nextseg",t,c,before);
601   if (!before) return 0;
602
603   advanced= calc_advanced(c);
604   advance_elapsed(u,advanced);
605
606   u->nosec.distance= advanced;
607   r= trackloc_advance(&u->nose,&u->nosec);
608   if (r) {
609     DPRINTF(safety,predictseg,"   fdet_nextseg  r=%s\n",ec2str(r));
610     return r;
611   }
612
613   /* Now we have advanced the nose and have recorded any appropriate
614    * motion(s).  Advancing the nose has updated the segment's
615    * notion of the motion but not trackloc_advance's cached version of
616    * the movposcomb in *mpc_io - but trackloc_advance will call
617    * pred_getmovpos right after we return so we do not need to worry.
618    */
619
620   /* Check polarity */
621
622   if (!t->seg->i->invertible) {
623     u->noninv_tally[t->backwards]++;
624     if (u->noninv_tally[0] && u->noninv_tally[1])
625       return predict_problem(u, t->seg, "cannot set track polarity");
626   }
627
628   if (!u->done_first_new_fdet) {
629     t->seg->det_expected= 1;
630     u->done_first_new_fdet= 1;
631   }
632
633   if (u->elapsed < MARGIN_POLARISETIME) {
634     u->need_polarise |= (t->seg->seg_inverted ^ t->backwards ^
635                          u->train_polarity_inverted);
636   } else {
637     u->walk_compute_polarise &= u->need_polarise;
638   }
639
640   if (u->train->autopoint && !u->autopoint_distance) {
641     const SegPosCombInfo *pci;
642     r= trackloc_getlink(t,c,&pci,0,-1);  assert(!r);
643     u->autopoint_distance= pci->dist;
644   }    
645
646   if (u->walk_compute_polarise) {
647     if (t->seg->will_polarise)
648       /* this is our 2nd visit, stop now */
649       u->walk_compute_polarise= 0;
650   }
651   if (u->walk_compute_polarise) {
652     if (!u->know_best_polarity) {
653       if (u->noninv_tally[0]) {
654         u->train_polarity_inverted= 0;
655         u->know_best_polarity= 1;
656       } else if (u->noninv_tally[1]) {
657         u->train_polarity_inverted= 1;
658         u->know_best_polarity= 1;
659       }
660     }
661     if (u->know_best_polarity &&
662         !t->seg->i->invertible &&
663         u->train_polarity_inverted != t->backwards) {
664       /* incompatible, stop now */
665       u->walk_compute_polarise= 0;
666     }
667   }
668   if (u->walk_compute_polarise) {
669     t->seg->will_polarise= 1;
670   }
671
672   /* Advance the tail */
673
674   u->tailc.distance += advanced;
675   if (u->tailc.distance > 0) {
676     r= trackloc_advance(&u->tail,&u->tailc);
677     assert(!r);
678     assert(!u->tailc.distance);
679   }
680
681   /* Final adjustments, prepare for next iteration */
682
683   u->was_distance= c->distance;
684   return 0;
685 }
686
687 /*---------- backtracking the optimistic nose ----------*/
688
689 static int optunwind_nextseg(TrackLocation *t, TrackAdvanceContext *c,
690                              MovPosComb *mpc_io, const TrackLocation *before) {
691   PredictUserContext *u= c->u;
692
693   pred_callback_debug("optunwind_nextseg",t,c,before);
694
695   if (t->seg == u->furthest ||
696       t->seg->motion_newplan)
697     return EC_SignallingHorizonReached;
698
699   t->seg->pred_present= 0;
700   t->seg->until= 0;
701   u->lookahead--;
702   return 0;
703 }
704
705 /*========== prediction entrypoint ==========*/
706
707 ErrorCode predict(Train *tra, struct timeval tnow, unsigned flags,
708                   Segment *desire_move, int desire_movposcomb,
709                   const SpeedInfo *speed_info,
710                   PredictionProblemCallback *ppc, void *ppcu) {
711   PredictUserContext u;
712   SpeedInfo speed_info_buf;
713   Segment *foredet;
714   SEG_IV;
715   ErrorCode ec;
716   int DP;
717
718   memset(&u,0,sizeof(u));
719   u.train= tra;
720   u.problem_callback= ppc;
721   u.problem_callback_u= ppcu;
722   u.neednewplan= !(flags & PREDF_OLDPLAN);
723   u.speed_info_specified= !!speed_info;
724
725   if (!speed_info) {
726     speedmanager_getinfo(tra,tnow,&speed_info_buf);
727     speed_info= &speed_info_buf;
728   }
729   u.stopping= (flags & PREDF_OLDPLAN) && speed_info->stopping;
730   u.maxspeed= speed_info->max_speed_estimate;
731   u.stopping_distance= speed_info->stopping_distance;
732
733   u.desire_move= desire_move;
734   u.desire_movposcomb= desire_movposcomb;
735
736   Distance detectable= ceil(tra->detectable * MARGIN_TRAINLENGTH);
737
738   if (DEBUGP(safety,core)) {
739     DPRINTF(safety,core," ***predicting*** %s%s ",
740             tra->backwards?"-":"",tra->pname);
741     qprintf_position(tra,DPRINTFA);
742     DPRINTFA(" det.able=%d %smaxspeed=%f%s stopdist=%d (speed %f, step %d%s)"
743              " flags=%c%c%s desire=%s/%s\n",
744              detectable,
745              u.speed_info_specified ? "specified " : "",
746              u.maxspeed, u.stopping ? "(stopping)" : "",
747              u.stopping_distance,
748              tra->speed.speed, tra->speed.step,
749              tra->speed.decel.running ? " decel" : "",
750              "-j"[ !!(flags & PREDF_JUSTDET) ],
751              "no"[ !!(flags & PREDF_OLDPLAN) ],
752              u.stopping ? " stopping" : "",
753              u.desire_move ? u.desire_move->i->pname : "",
754              u.desire_move ? u.desire_move->i->poscombs[u.desire_movposcomb]
755                             .pname : "");
756   }
757
758   FOR_SEG {
759     if (!seg->owner || seg->owner == u.train)
760       seg->det_expected= 0;
761     seg->now_present= seg->pred_present=
762       seg->pred_vacated= seg->will_polarise= 0;
763   }
764
765   foredet= tra->foredetect;
766   assert(foredet);
767
768   u.walk_compute_polarise= 1;
769   u.usecurrentposn= 1;
770   u.train_polarity_inverted= foredet->seg_inverted ^ foredet->tr_backwards;
771
772   /*----- find the train's foredetect (rearmost possibility) -----*/
773   /*
774    * If tra->uncertainty > tra->maxinto then in theory the foredetect
775    * might be in the previous segment.  We don't take that into account,
776    * so we must instead extend our idea of the length of the train.
777    */
778
779   int effective_uncertainty= tra->uncertainty;
780   int effective_length= MARGIN_NOSE + tail_length(tra) + detectable;
781
782   if (effective_uncertainty > tra->maxinto) {
783     effective_length += tra->uncertainty - tra->maxinto;
784     effective_uncertainty= tra->maxinto;
785   }
786
787   int effective_into_fdet= tra->maxinto - effective_uncertainty;
788   int tail_advance_delaydist= MARGIN_TAIL;
789
790   DPRINTF(safety,core,"  predict eff.uncert=%d eff.len=%d"
791           " eff.into_fdet=%d tail.adv.delay=%d\n",
792           effective_uncertainty, effective_length, 
793           effective_into_fdet, tail_advance_delaydist);
794
795   u.fdetc.u= &u;
796   u.fdetc.getmovpos= pred_getmovpos;
797
798   trackloc_set_exactinto(&u.fdet, &u.fdetc, foredet, foredet->tr_backwards,
799                          effective_into_fdet);
800
801   /*----- find the train's tail (rearmost possibility) -----*/
802   /*
803    * We walk backwards, also marking the train present.
804    */
805
806   u.tailc.u= &u;
807   u.tailc.getmovpos= pred_getmovpos;
808   u.tailc.nextseg= initpresent_nextseg;
809   u.tailc.trackend= pred_trackend_panic;
810   u.tailc.distance= effective_length;
811
812   u.tail= u.fdet;
813
814   ec= trackloc_reverse_exact(&u.tail,&u.tailc);  assert(!ec);
815   ec= trackloc_advance(&u.tail,&u.tailc);         assert(!ec);
816   ec= trackloc_reverse_exact(&u.tail,&u.tailc);  assert(!ec);
817
818   u.hindmost= u.tail.seg;
819
820   /*----- find the train's nose (rearmost possibility) -----*/
821
822   u.usecurrentposn= 0;
823   u.nosec.u= &u;
824   u.nosec.getmovpos= pred_getmovpos;
825   u.nosec.nextseg= nose_nextseg;
826   u.nosec.trackend= pred_trackend;
827   u.nosec.distance= nose_length(tra);
828
829   u.nose= u.fdet;
830
831   ec= trackloc_advance(&u.nose,&u.nosec);
832   if (ec && ec != EC_SignallingHorizonReached) goto xproblem;
833
834   /*----- predict the future - (including the present uncertainty) -----*/
835
836   u.fdetc.nextseg= fdet_nextseg;
837   u.fdetc.getmovpos= pred_getmovpos;
838   u.fdetc.trackend= pred_trackend;
839   u.tailc.nextseg= tail_nextseg;
840   u.tailc.distance= -tail_advance_delaydist;
841
842   if (!u.stopping) {
843     /* we're carrying on until the next segment */
844     u.fdetc.distance= u.fdet.remain;
845   } else if (flags & PREDF_JUSTDET) {
846     /* we actually know exactly where we are */
847     u.fdetc.distance= 0;
848   } else { /* stopping, but we're mid-segment */
849     u.fdetc.distance= effective_uncertainty;
850   }
851   u.elapsed= -u.fdetc.distance / u.maxspeed; /* imagine we're already there */
852   u.fdetc.distance += u.stopping_distance + MARGIN_NOSE;
853   u.was_distance= u.fdetc.distance;
854
855   ec= trackloc_advance(&u.fdet,&u.fdetc);
856   if (ec && ec != EC_SignallingHorizonReached) goto xproblem;
857
858   /*----- look ahead for autopoint -----*/
859
860   if (tra->autopoint) {
861     Distance min_autopoint_dist= MARGIN_AUTOPOINTTIME * u.maxspeed;
862
863     DPRINTF(safety,predict,"  optimism %d (min %d) nose %s\n",
864             u.autopoint_distance, min_autopoint_dist, u.nose.seg->i->pname);
865
866     if (u.autopoint_distance < min_autopoint_dist)
867       u.autopoint_distance= min_autopoint_dist;
868
869     u.furthest= u.nose.seg;
870     u.optimistic= 1;
871     u.was_distance= u.nosec.distance= u.autopoint_distance;
872     trackloc_advance(&u.nose,&u.nosec);
873
874     /* But actually we don't want to end up owning
875      * segments unless we have to for motions. */
876
877     u.nose.backwards ^= 1;
878     u.nose.remain= 0;
879     u.nosec.nextseg= optunwind_nextseg;
880     u.nosec.distance= TL_DIST_INF;
881
882     ec= trackloc_advance(&u.nose,&u.nosec);
883     assert(ec == EC_SignallingHorizonReached);
884   }
885
886   /*----- commit to the plan -----*/
887
888   DPRINTF(safety,predict,"  committing la#%d %c%c%c\n",
889           u.lookahead,
890           "-n"[ u.need_polarise ],
891           "-i"[ u.train_polarity_inverted ],
892           "-b"[ u.know_best_polarity ]);
893
894   tra->plan_lookahead_nsegs= u.lookahead;
895
896   if (u.need_polarise) {
897     DPRINTF(safety,predict,"  polarising train_polarity=%d\n",
898             u.train_polarity_inverted),
899     actual_inversions_start();
900   }
901
902   DPRINTF1(safety,predictplan,"  ");
903   FOR_SEG {
904     DPRINTF2(" %s%s%s%c%c%c%c/%s%s%s%s%s",
905              seg->tr_backwards?"-":"", seg->i->pname,
906              seg->owner == tra ? "=" : seg->owner ? "#" : "-",
907              "-N"[ seg->now_present ],
908              "-P"[ seg->pred_present ],
909              "-V"[ seg->pred_vacated ],
910              "-p"[ seg->will_polarise ],
911              movpos_pname(seg,seg->movposcomb),
912              seg->motion ? (seg->moving ? "!" : "~") : "",
913              seg->motion ? motion_pname(seg,seg->motion) : "",
914              seg->motion_newplan ? ">" : "",
915              seg->motion && seg->motion_newplan==seg->motion ? "=" :
916              motion_pname(seg,seg->motion_newplan));
917
918     /* set ownership and det_ignore */
919     if (seg->pred_present || seg->pred_vacated) {
920       seg->owner= tra;
921       seg->det_ignore= seg->now_present;
922     } else if (seg->owner == tra) {
923       seg->det_ignore= 0;
924       seg->owner= 0;
925       /* garbage collect our old plan for now-irrelevant segments */
926       assert(!seg->motion_newplan);
927       if (!seg->moving) {
928         movpos_unreserve(seg->motion);
929         seg->motion= 0;
930       }
931       continue;
932     } else {
933       /* segment not relevant to us at all */
934       continue;
935     }
936
937     /* now it's our segment: */
938
939     /* clear any irrelevant old plan */
940     if (seg->motion && !seg->moving) {
941       if (seg->motion != seg->motion_newplan)
942         movpos_unreserve(seg->motion);
943       seg->motion= 0;
944     }
945     /* now motion!=0 only if seg->moving */
946
947     /* install the new plan, if any */
948     if (seg->motion_newplan) {
949       if (seg->motion_newplan == seg->motion) {
950         /* excellent, already doing it */
951       } else if (!seg->now_present) {
952         MovPosComb target= movpos_change_intent(seg->motion_newplan);
953         ec= movpos_change(seg, target, -1, seg->motion_newplan);
954         assert(!ec);
955         /* motion is updated by movpos_change */
956       } else {
957         /* we'll do it later, then */
958         seg->motion= seg->motion_newplan;
959       }
960       seg->motion_newplan= 0;
961     }
962     /* now the new plan is in motion and moving */
963
964     if (u.need_polarise && seg->will_polarise) {
965       seg->seg_inverted= seg->tr_backwards ^ u.train_polarity_inverted;
966       if (seg->i->invertible) {
967         actual_inversions_segment(seg);
968         Segment *interferer= segment_interferes(0,seg);
969         if (interferer && !interferer->will_polarise &&
970             interferer->i->invertible) {
971           interferer->seg_inverted= seg->seg_inverted
972             ^ seg->i->interferes_polarity_opposed;
973           actual_inversions_segment(interferer);
974         }
975       } else {
976         assert(!seg->seg_inverted);
977       }
978     }
979     seg->now_present= seg->pred_present=
980       seg->pred_vacated= seg->will_polarise= 0;
981   }
982   DPRINTF2("\n");
983
984   if (u.need_polarise)
985     actual_inversions_done();
986
987   report_train_ownerships(tra,u.hindmost,0);
988
989   if (u.desire_move && !u.desire_move->owner)
990     /* Oh!  Train must be slower and less far advanced than expected.
991      * Well, then we can move it right away. */
992     return movpos_change(u.desire_move,u.desire_movposcomb,-1,0);
993
994   return 0;
995
996  xproblem:
997   DPRINTF(safety,predict,"  returning %s\n", ec2str(ec));
998
999   FOR_SEG {
1000     seg->now_present= seg->pred_present=
1001       seg->pred_vacated= seg->will_polarise= 0;
1002
1003     if (seg->motion_newplan==seg->motion) {
1004       seg->motion_newplan= 0;
1005     }
1006     if (seg->motion_newplan) {
1007       movpos_unreserve(seg->motion_newplan);
1008       seg->motion_newplan= 0;
1009     }
1010     if (!seg->owner && !seg->moving && seg->motion) {
1011       movpos_unreserve(seg->motion);
1012       seg->motion= 0;
1013     }
1014   }
1015   return ec;
1016 }
1017
1018 /*========== reporting position and ownership ==========*/
1019
1020 void report_train_position(Train *tra) {
1021   ouprintf("train %s at ",tra->pname);
1022   qprintf_position(tra,ouprintf);
1023   ouprintf(" %s\n", tra->backwards ? "backwards" : "forwards");
1024 }  
1025   
1026 static int report_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
1027                             MovPosComb *use_io) {
1028   PredictUserContext *u= c->u;
1029   if (u->done_fdet && t->seg != u->train->foredetect)
1030     /* we must use current posn for foredetect itself */
1031     u->usecurrentposn= 0;
1032   if (!u->usecurrentposn && t->seg->motion)
1033     *use_io= movpos_change_intent(t->seg->motion);
1034   assert(*use_io>=0);
1035   return 0;
1036 }
1037
1038 static int report_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
1039                           MovPosComb *mpc, const TrackLocation *before) {
1040   PredictUserContext *u= c->u;
1041   char flags[6];
1042   int r;
1043
1044   u->reportcount++;
1045   if (u->reportcount > NUM_SEGMENTS * 2) {
1046     ouprintf(" [... infinite loop!]\n");
1047     abort();
1048   }
1049
1050   if (u->done_fdet) /* we've had foredetect */
1051     if (++u->lookahead > u->train->plan_lookahead_nsegs)
1052       return -1;
1053
1054   flags[0]= 0;
1055
1056   if (t->seg->det_expected)
1057     strcat(flags,"*");
1058   if (t->seg->det_ignore)
1059     strcat(flags,".");
1060   if (t->seg == u->train->foredetect && !u->done_fdet) {
1061     strcat(flags,"!");
1062     u->done_fdet= 1;
1063   }
1064
1065   ouprintf(" %s%s", t->backwards?"-":"", t->seg->i->pname);
1066
1067   if (t->seg->i->n_poscombs > 1) {
1068     r= report_getmovpos(t,c,mpc);  assert(!r);  assert(*mpc>=0);
1069     ouprintf("/%s", t->seg->i->poscombs[*mpc].pname);
1070   }
1071   ouprintf("%s",flags);
1072
1073   return 0;
1074 }
1075
1076 void report_train_ownerships(Train *tra, Segment *hindmost,
1077                              int always_use_motions) {
1078   PredictUserContext u;
1079
1080   memset(&u,0,sizeof(u));
1081   u.train= tra;
1082   u.hindmost= 0;
1083   u.usecurrentposn= !always_use_motions;
1084   u.done_fdet= 0;
1085   
1086   /* Walk along the train printing its segments: */
1087   ouprintf("train %s has", tra->pname);
1088   
1089   u.nose.seg= hindmost;
1090   u.nose.remain= 0;
1091   u.nose.backwards= hindmost->tr_backwards;
1092
1093   u.nosec.distance= TL_DIST_INF;;
1094   u.nosec.nextseg= report_nextseg;
1095   u.nosec.getmovpos= report_getmovpos;
1096   u.nosec.u= &u;
1097   u.hindmost= hindmost;
1098
1099   trackloc_advance(&u.nose,&u.nosec);
1100
1101   ouprintf("\n");
1102 }
1103
1104 /*========== reversing a train ==========*/
1105
1106 static int reverse_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
1107                            MovPosComb *mpc_io, const TrackLocation *before) {
1108   PredictUserContext *u= c->u;
1109
1110   DPRINTF(safety,predictseg,"   reverse_nextseg "
1111           " %c%s dist=%-4d\n",
1112
1113           " -"[t->backwards],
1114           t->seg->i->pname,
1115           c->distance);
1116
1117   if (*mpc_io==-1 || t->seg->motion)
1118     return predict_problem(u,t->seg, "segment under train is not stable");
1119   return 0;
1120 }
1121
1122 static int reverse_trackend(TrackLocation *t, struct TrackAdvanceContext *c) {
1123   abort();
1124 }
1125
1126 ErrorCode safety_setdirection(Train *tra, int backwards,
1127                               PredictionProblemCallback *ppc, void *ppcu) {
1128   /* We adjust:
1129    *   tra->foredetect, maxinto, uncertainty
1130    *   tra->backwards
1131    *   tra->plan_lookahead_nsegs
1132    *   seg->tr_backwards
1133    * The following remain unchanged as all their users have
1134    * regard to the train's direction:
1135    *   tra->head, tail
1136    */
1137   TrackLocation newfdet;
1138   TrackAdvanceContext c;
1139   PredictUserContext u;
1140   const SegPosCombInfo *pci;
1141   Segment *oldfdet;
1142   int oldmaxinto, oldfdet_trbackwards, old_planlookaheadnsegs;
1143   ErrorCode ec, ec2;
1144   int r;
1145   struct timeval tnow;
1146
1147   SKELETON_PREDICT_USER_CONTEXT(u,tra);
1148
1149   MUSTECR( safety_checktrain(tra,ppc,ppcu) );
1150
1151   if (tra->backwards == backwards)
1152     return 0;
1153
1154   if (!speedmanager_stopped(tra))
1155     return predict_problem(&u,0, "train is not stopped");
1156   
1157   newfdet.seg= tra->foredetect;
1158   newfdet.remain= tra->maxinto;
1159   newfdet.backwards= !tra->foredetect->tr_backwards;
1160
1161   c.distance= ceil(tra->detectable * MARGIN_TRAINLENGTH) + tra->uncertainty;
1162   c.nextseg= reverse_nextseg;
1163   c.getmovpos= 0;
1164   c.trackend= reverse_trackend;
1165   c.u= &u;
1166
1167   MUSTECR( trackloc_advance(&newfdet,&c) );
1168
1169   mgettimeofday(&tnow);
1170
1171   /* right, now let's start fiddling */
1172   oldfdet= tra->foredetect;
1173   oldmaxinto= tra->maxinto;
1174   oldfdet_trbackwards= tra->foredetect->tr_backwards;
1175   old_planlookaheadnsegs= tra->plan_lookahead_nsegs;
1176
1177   r= trackloc_getlink(&newfdet,&c,&pci,0,-1);  assert(!r);
1178
1179   tra->foredetect= newfdet.seg;
1180   tra->maxinto= pci->dist - newfdet.remain;
1181   tra->backwards ^= 1;
1182   newfdet.seg->tr_backwards ^= 1;
1183   tra->plan_lookahead_nsegs= INT_MAX;
1184   
1185   ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,ppc,ppcu);
1186   if (!ec) {
1187     /* yay! */
1188     report_train_position(tra);
1189     return 0;
1190   }
1191
1192   /* It can happen eg that the uncertainty is more troublesome when
1193    * considered one way than the other.  In which case just put it back now. */
1194   assert(ec == EC_SignallingPredictedProblem);
1195
1196   tra->foredetect= oldfdet;
1197   tra->maxinto= oldmaxinto;
1198   tra->backwards ^= 1;
1199   tra->foredetect->tr_backwards= oldfdet_trbackwards;
1200   tra->plan_lookahead_nsegs= old_planlookaheadnsegs;
1201
1202   ec2= predict(tra,tnow, PREDF_OLDPLAN,0,0, 0,0,(char*)"abandon reverse");
1203   assert(!ec2);
1204
1205   return ec;
1206 }
1207
1208 /*========== entrypoints from rest of the program ==========*/
1209
1210 static void detection_report_problem(Train *tra, Segment *seg,
1211                                      void *pu, const char *message) {
1212   ouprintf("train %s signalling-problem %s : %s\n",
1213            tra->pname, seg ? seg->i->pname : "-", message);
1214 }
1215
1216 void safety_notify_detection(Segment *seg) {
1217   Train *tra;
1218   ErrorCode ec;
1219   Segment *interferer;
1220   int maxinto;
1221   struct timeval tnow;
1222   SpeedInfo speed_info;
1223
1224   if (seg->det_ignore) return;
1225
1226   debug_count_event("detection");
1227
1228   if (!seg->det_expected) {
1229     interferer= segment_interferes(0,seg);
1230     if (!interferer) safety_panic(0,seg, "unexpected detection");
1231     if (interferer->det_ignore) return;
1232     if (!interferer->det_expected)
1233       safety_panic(0,seg, "unexpected detection, perhaps really at %s",
1234                    interferer->i->pname);
1235     DPRINTF(safety,core, " detection %s using interferer %s",
1236             seg->i->pname, interferer->i->pname);
1237     seg= interferer;
1238   }
1239
1240   tra= seg->owner;
1241   if (seg->movposcomb < 0)
1242     safety_panic(tra,seg, "track route not set and train has arrived");
1243
1244   mgettimeofday(&tnow);
1245
1246   speedmanager_getinfo(tra,tnow,&speed_info);
1247   maxinto= seg->i->poscombs[seg->movposcomb].dist;
1248   if (speed_info.stopping && maxinto > speed_info.stopping_distance)
1249     maxinto= speed_info.stopping_distance;
1250
1251   tra->foredetect= seg;
1252   tra->uncertainty= tra->maxinto= maxinto;
1253   tra->plan_lookahead_nsegs--;
1254   report_train_position(tra);
1255
1256   ec= predict(tra,tnow, PREDF_JUSTDET|PREDF_OLDPLAN,0,0,
1257               &speed_info, detection_report_problem,0);
1258   if (!ec) return;
1259
1260   assert(ec == EC_SignallingPredictedProblem);
1261
1262   if (maxinto > speed_info.stopping_distance)
1263     maxinto= speed_info.stopping_distance;
1264   tra->maxinto= tra->uncertainty= maxinto;
1265
1266   report_train_position(tra);
1267   speedmanager_safety_stop(tra,tnow);
1268 }
1269
1270 ErrorCode safety_movposchange(Segment *seg, MovPosComb comb,
1271      int allow_queueing, PredictionProblemCallback *ppc, void *ppcu) {
1272   PredictUserContext u;
1273   struct timeval tnow;
1274   ErrorCode ec, ec2;
1275
1276   SKELETON_PREDICT_USER_CONTEXT(u,seg->owner);
1277
1278   if (!seg->owner)
1279     return movpos_change(seg,comb,-1,0);
1280
1281   if (allow_queueing<2) {
1282     if (seg->det_ignore)
1283       return predict_problem(&u,seg, "route set for train");
1284     if (seg->det_expected)
1285       return predict_problem(&u,seg, "route set for immiment train");
1286   }
1287   if (allow_queueing<1)
1288     return predict_problem(&u,seg, "route set for approaching train");
1289
1290   mgettimeofday(&tnow);
1291   ec= predict(seg->owner,tnow, 0, seg,comb, 0, ppc,ppcu);
1292   if (!ec) return 0;
1293
1294   ec2= predict(seg->owner,tnow, PREDF_OLDPLAN, 0,0, 0,
1295                0,(char*)"abandon movposchange");
1296   assert(!ec2);
1297
1298   return ec;
1299 }
1300
1301 ErrorCode safety_checktrain(Train *tra,
1302                             PredictionProblemCallback *ppc, void *ppcu) {
1303   if (!tra->foredetect) {
1304     ppc(tra,0,ppcu,"train is not on layout");
1305     return EC_SignallingPredictedProblem;
1306   }
1307   return 0;
1308 }
1309
1310 void safety_abandon_run(void) {
1311   SEG_IV;
1312
1313   FOR_SEG {
1314     if (seg->moving) continue;
1315     movpos_unreserve(seg->motion);
1316     seg->motion= 0;
1317   }
1318 }