3 * safety core algorithm
18 * New signalling/safety algorithm:
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.
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:
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.
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).
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
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.)
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.
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).
68 * Regarding moveable features, we continuously develop our plan,
69 * as represented in route_plan and route_reservation.
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).
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
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
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.
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
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. */
104 * Here is how we use the stuff in the Segment:
106 * owner train which owns this for signalling purposes
107 * no other train may interfere
109 * physical in an unowned segment, this has no particular
110 * feature relationship to anything
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
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
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
134 * det_ignore this is part of the body of a train which we've
135 * already detected here
137 * det_expected this is the next place we expect owner train
139 * (it is not valid to have both det_ignore
142 * VALID DURING PREDICTION ETC. ONLY
144 * pred_present in our prediction, the train physically occupies this
145 * until_here tells us when will be
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
154 * iff either of these is set, this train is going
155 * to own the segment when we're finished
157 * now_present train is in this segment now (in which we include
158 * the segment just before foredetect)
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
165 * Valid combinations during prediction:
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
182 * seg->tr_backwards records the sense of the _first_ passage of the train.
184 * invalid/impossible combination:
185 * train here now but never in prediction N--
187 * Meanings of moving, motion and motion_newplan:
188 * For segments NPV==000, no plan made yet:
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
195 * 1 0 ? always forbidden
196 * ? ? non-0 forbidden for not-yet-planned segments
198 * For segments with NPV!=0, some kind of planning done:
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
207 * 1 0 ? always forbidden
208 * 1 non-0 0 forbidden: need explicit motion to cancel
210 * (These are the values when not in the middle of nose_nextseg.)
216 * real stop dist >>>>>
217 * simul. stop dist >>>>>
218 * +++++++++++|+++++++++++|++++++++++|XXXXXX
220 * adv. tail back T FN
221 * adv. nose find T F N
222 * +++++++++++|+++++++++++|++++++++++|XXXXXX
223 * adv. fdet 1 seg T N F
226 * adv. fdet stopdist T N F
229 * +++++++++++|+++++++++++|++++++++++|XXXXXX
233 * real stop dist >>>>>
234 * +++++++++++|+++++++++++|++++++++++|XXXXXX
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
244 * real stop dist >>>>>
245 * +++++++++++|+++++++++++|++++++++++|XXXXXX
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
256 /*========== prediction machinery ==========*/
258 #define now_present mark0
259 #define pred_present mark1
260 #define pred_vacated mark2
261 #define will_polarise mark3
266 speed_info_specified: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;
282 Segment *hindmost, *furthest, *desire_move;
283 int desire_movposcomb;
284 int lookahead, reportcount;
286 PredictionProblemCallback *problem_callback;
287 void *problem_callback_u;
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) */
293 } PredictUserContext;
295 static int tail_length(Train *tra) {
296 return tra->backwards ? tra->head : tra->tail;
298 static int nose_length(Train *tra) {
299 return tra->backwards ? tra->tail : tra->head;
301 static void advance_elapsed(PredictUserContext *u, int advance_distance) {
302 u->elapsed += advance_distance / u->maxspeed;
304 static int calc_advanced(TrackAdvanceContext *c) {
305 PredictUserContext *u= c->u;
306 return u->was_distance - c->distance;
308 static void qprintf_position(Train *tra,
309 void (*qp)(const char *fmt,...) __attribute__((format(printf,1,2)))) {
311 tra->foredetect->tr_backwards?"-":"",
312 tra->foredetect->i->pname, tra->maxinto, tra->uncertainty);
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, \
320 /* for callers of predict_problem rather than predict */
322 /*---------- prediction problem reporting ----------*/
324 static ErrorCode predict_vproblem(PredictUserContext *u, Segment *seg,
325 const char *fmt, va_list al) {
329 l= vasprintf(&message, fmt, al); if (l <= 0) diem();
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);
337 u->problem_callback(u->train, seg, u->problem_callback_u, message);
340 return EC_SignallingPredictedProblem;
343 static ErrorCode predict_problem(PredictUserContext *u, Segment *seg,
344 const char *fmt, ...) {
348 ec= predict_vproblem(u,seg,fmt,al);
353 static const char *motion_pname(Segment *seg, MovPosChange *motion) {
354 if (!motion) return "";
355 return movpos_pname(seg, movpos_change_intent(motion));
358 static void pred_callback_debug(const char *what, TrackLocation *t,
359 struct TrackAdvanceContext *c,
360 const TrackLocation *before) {
361 PredictUserContext *u= c->u;
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"
375 "-N"[ t->seg->now_present ],
376 "-P"[ t->seg->pred_present ],
377 "-V"[ t->seg->pred_vacated ],
379 "-p"[ t->seg->will_polarise ],
380 "-i"[ t->seg->seg_inverted ],
382 before && before->backwards?"-":"",
383 before ? before->seg->i->pname : "-",
384 before ? before->remain : -1,
388 "-2"[ u->done_first_new_fdet ],
389 "-c"[ u->usecurrentposn ],
390 "-o"[ u->optimistic ],
392 "-w"[ u->walk_compute_polarise ],
393 "-n"[ u->need_polarise ],
394 "-i"[ u->train_polarity_inverted ],
395 "-b"[ u->know_best_polarity ],
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),
405 u->noninv_tally[0], u->noninv_tally[1]);
408 /*---------- prediction trivial callbacks ----------*/
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");
422 static int pred_trackend(TrackLocation *t, TrackAdvanceContext *c) {
423 PredictUserContext *u= c->u;
424 return predict_problem(u, t->seg, "end of track");
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");
432 static int initpresent_nextseg(TrackLocation *t, TrackAdvanceContext *c,
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;
440 if (!t->seg->i->invertible)
441 u->noninv_tally[!t->backwards]++; /* ! since going backwards along train */
445 /*---------- prediction nose advancement ----------*/
447 static int nose_nextseg(TrackLocation *t, TrackAdvanceContext *c,
448 MovPosComb *mpc_io, const TrackLocation *before) {
449 PredictUserContext *u= c->u;
451 MovPosComb route_plan;
455 pred_callback_debug(" nose_nextseg",t,c,before);
456 if (!before) return 0;
459 advance_elapsed(u,calc_advanced(c));
463 if (u->stopping && u->lookahead == u->train->plan_lookahead_nsegs)
464 return EC_SignallingHorizonReached;
467 if (t->seg->owner != u->train)
468 return predict_problem(u, t->seg, "impeded by %s",
469 t->seg->owner->pname);
471 if (t->seg->pred_present)
472 return predict_problem(u, t->seg, "will collide with itself!");
474 if (!u->optimistic) {
475 interferer= segment_interferes(c,t->seg);
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);
486 /* What about the route ? */
488 if (t->seg->i->n_poscombs==1)
491 if (t->seg->moving) {
492 assert(!t->seg->now_present);
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);
502 int autopoint= u->train->autopoint && t->seg->autopoint;
504 if (t->seg->motion_newplan) {
505 route_plan= movpos_change_intent(t->seg->motion);
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);
512 if (route_plan != u->desire_movposcomb)
513 return predict_problem(u, t->seg, " proposed route would be against"
514 " approaching train");
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;
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 */
530 if (route_plan == *mpc_io && !t->seg->moving) {
531 /* Well, that's all fine then. */
534 /* We can make it work but have to plan some motion: */
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;
549 return predict_problem(u,t->seg,"will not automatically set route");
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));
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;
567 /*---------- prediction tail advancement ----------*/
569 static int tail_nextseg(TrackLocation *t, TrackAdvanceContext *c,
570 MovPosComb *mpc_io, const TrackLocation *before) {
571 PredictUserContext *u= c->u;
573 pred_callback_debug(" tail_nextseg",t,c,before);
574 if (!before) return 0;
576 if (!before->seg->i->invertible)
577 u->noninv_tally[before->backwards]--;
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;
587 /*---------- prediction foredetect advancement ----------*/
589 static int fdet_nextseg(TrackLocation *t, TrackAdvanceContext *c,
590 MovPosComb *mpc_io, const TrackLocation *before) {
591 PredictUserContext *u= c->u;
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.
600 pred_callback_debug("fdet_nextseg",t,c,before);
601 if (!before) return 0;
603 advanced= calc_advanced(c);
604 advance_elapsed(u,advanced);
606 u->nosec.distance= advanced;
607 r= trackloc_advance(&u->nose,&u->nosec);
609 DPRINTF(safety,predictseg," fdet_nextseg r=%s\n",ec2str(r));
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.
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");
628 if (!u->done_first_new_fdet) {
629 t->seg->det_expected= 1;
630 u->done_first_new_fdet= 1;
633 if (u->elapsed < MARGIN_POLARISETIME) {
634 u->need_polarise |= (t->seg->seg_inverted ^ t->backwards ^
635 u->train_polarity_inverted);
637 u->walk_compute_polarise &= u->need_polarise;
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;
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;
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;
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;
668 if (u->walk_compute_polarise) {
669 t->seg->will_polarise= 1;
672 /* Advance the tail */
674 u->tailc.distance += advanced;
675 if (u->tailc.distance > 0) {
676 r= trackloc_advance(&u->tail,&u->tailc);
678 assert(!u->tailc.distance);
681 /* Final adjustments, prepare for next iteration */
683 u->was_distance= c->distance;
687 /*---------- backtracking the optimistic nose ----------*/
689 static int optunwind_nextseg(TrackLocation *t, TrackAdvanceContext *c,
690 MovPosComb *mpc_io, const TrackLocation *before) {
691 PredictUserContext *u= c->u;
693 pred_callback_debug("optunwind_nextseg",t,c,before);
695 if (t->seg == u->furthest ||
696 t->seg->motion_newplan)
697 return EC_SignallingHorizonReached;
699 t->seg->pred_present= 0;
705 /*========== prediction entrypoint ==========*/
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;
718 memset(&u,0,sizeof(u));
720 u.problem_callback= ppc;
721 u.problem_callback_u= ppcu;
722 u.neednewplan= !(flags & PREDF_OLDPLAN);
723 u.speed_info_specified= !!speed_info;
726 speedmanager_getinfo(tra,tnow,&speed_info_buf);
727 speed_info= &speed_info_buf;
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;
733 u.desire_move= desire_move;
734 u.desire_movposcomb= desire_movposcomb;
736 Distance detectable= ceil(tra->detectable * MARGIN_TRAINLENGTH);
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",
745 u.speed_info_specified ? "specified " : "",
746 u.maxspeed, u.stopping ? "(stopping)" : "",
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]
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;
765 foredet= tra->foredetect;
768 u.walk_compute_polarise= 1;
770 u.train_polarity_inverted= foredet->seg_inverted ^ foredet->tr_backwards;
772 /*----- find the train's foredetect (rearmost possibility) -----*/
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.
779 int effective_uncertainty= tra->uncertainty;
780 int effective_length= MARGIN_NOSE + tail_length(tra) + detectable;
782 if (effective_uncertainty > tra->maxinto) {
783 effective_length += tra->uncertainty - tra->maxinto;
784 effective_uncertainty= tra->maxinto;
787 int effective_into_fdet= tra->maxinto - effective_uncertainty;
788 int tail_advance_delaydist= MARGIN_TAIL;
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);
796 u.fdetc.getmovpos= pred_getmovpos;
798 trackloc_set_exactinto(&u.fdet, &u.fdetc, foredet, foredet->tr_backwards,
799 effective_into_fdet);
801 /*----- find the train's tail (rearmost possibility) -----*/
803 * We walk backwards, also marking the train present.
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;
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);
818 u.hindmost= u.tail.seg;
820 /*----- find the train's nose (rearmost possibility) -----*/
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);
831 ec= trackloc_advance(&u.nose,&u.nosec);
832 if (ec && ec != EC_SignallingHorizonReached) goto xproblem;
834 /*----- predict the future - (including the present uncertainty) -----*/
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;
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 */
848 } else { /* stopping, but we're mid-segment */
849 u.fdetc.distance= effective_uncertainty;
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;
855 ec= trackloc_advance(&u.fdet,&u.fdetc);
856 if (ec && ec != EC_SignallingHorizonReached) goto xproblem;
858 /*----- look ahead for autopoint -----*/
860 if (tra->autopoint) {
861 Distance min_autopoint_dist= MARGIN_AUTOPOINTTIME * u.maxspeed;
863 DPRINTF(safety,predict," optimism %d (min %d) nose %s\n",
864 u.autopoint_distance, min_autopoint_dist, u.nose.seg->i->pname);
866 if (u.autopoint_distance < min_autopoint_dist)
867 u.autopoint_distance= min_autopoint_dist;
869 u.furthest= u.nose.seg;
871 u.was_distance= u.nosec.distance= u.autopoint_distance;
872 trackloc_advance(&u.nose,&u.nosec);
874 /* But actually we don't want to end up owning
875 * segments unless we have to for motions. */
877 u.nose.backwards ^= 1;
879 u.nosec.nextseg= optunwind_nextseg;
880 u.nosec.distance= TL_DIST_INF;
882 ec= trackloc_advance(&u.nose,&u.nosec);
883 assert(ec == EC_SignallingHorizonReached);
886 /*----- commit to the plan -----*/
888 DPRINTF(safety,predict," committing la#%d %c%c%c\n",
890 "-n"[ u.need_polarise ],
891 "-i"[ u.train_polarity_inverted ],
892 "-b"[ u.know_best_polarity ]);
894 tra->plan_lookahead_nsegs= u.lookahead;
896 if (u.need_polarise) {
897 DPRINTF(safety,predict," polarising train_polarity=%d\n",
898 u.train_polarity_inverted),
899 actual_inversions_start();
902 DPRINTF1(safety,predictplan," ");
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));
918 /* set ownership and det_ignore */
919 if (seg->pred_present || seg->pred_vacated) {
921 seg->det_ignore= seg->now_present;
922 } else if (seg->owner == tra) {
925 /* garbage collect our old plan for now-irrelevant segments */
926 assert(!seg->motion_newplan);
928 movpos_unreserve(seg->motion);
933 /* segment not relevant to us at all */
937 /* now it's our segment: */
939 /* clear any irrelevant old plan */
940 if (seg->motion && !seg->moving) {
941 if (seg->motion != seg->motion_newplan)
942 movpos_unreserve(seg->motion);
945 /* now motion!=0 only if seg->moving */
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);
955 /* motion is updated by movpos_change */
957 /* we'll do it later, then */
958 seg->motion= seg->motion_newplan;
960 seg->motion_newplan= 0;
962 /* now the new plan is in motion and moving */
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);
976 assert(!seg->seg_inverted);
979 seg->now_present= seg->pred_present=
980 seg->pred_vacated= seg->will_polarise= 0;
985 actual_inversions_done();
987 report_train_ownerships(tra,u.hindmost,0);
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);
997 DPRINTF(safety,predict," returning %s\n", ec2str(ec));
1000 seg->now_present= seg->pred_present=
1001 seg->pred_vacated= seg->will_polarise= 0;
1003 if (seg->motion_newplan==seg->motion) {
1004 seg->motion_newplan= 0;
1006 if (seg->motion_newplan) {
1007 movpos_unreserve(seg->motion_newplan);
1008 seg->motion_newplan= 0;
1010 if (!seg->owner && !seg->moving && seg->motion) {
1011 movpos_unreserve(seg->motion);
1018 /*========== reporting position and ownership ==========*/
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");
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);
1038 static int report_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
1039 MovPosComb *mpc, const TrackLocation *before) {
1040 PredictUserContext *u= c->u;
1045 if (u->reportcount > NUM_SEGMENTS * 2) {
1046 ouprintf(" [... infinite loop!]\n");
1050 if (u->done_fdet) /* we've had foredetect */
1051 if (++u->lookahead > u->train->plan_lookahead_nsegs)
1056 if (t->seg->det_expected)
1058 if (t->seg->det_ignore)
1060 if (t->seg == u->train->foredetect && !u->done_fdet) {
1065 ouprintf(" %s%s", t->backwards?"-":"", t->seg->i->pname);
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);
1071 ouprintf("%s",flags);
1076 void report_train_ownerships(Train *tra, Segment *hindmost,
1077 int always_use_motions) {
1078 PredictUserContext u;
1080 memset(&u,0,sizeof(u));
1083 u.usecurrentposn= !always_use_motions;
1086 /* Walk along the train printing its segments: */
1087 ouprintf("train %s has", tra->pname);
1089 u.nose.seg= hindmost;
1091 u.nose.backwards= hindmost->tr_backwards;
1093 u.nosec.distance= TL_DIST_INF;;
1094 u.nosec.nextseg= report_nextseg;
1095 u.nosec.getmovpos= report_getmovpos;
1097 u.hindmost= hindmost;
1099 trackloc_advance(&u.nose,&u.nosec);
1104 /*========== reversing a train ==========*/
1106 static int reverse_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
1107 MovPosComb *mpc_io, const TrackLocation *before) {
1108 PredictUserContext *u= c->u;
1110 DPRINTF(safety,predictseg," reverse_nextseg "
1111 " %c%s dist=%-4d\n",
1117 if (*mpc_io==-1 || t->seg->motion)
1118 return predict_problem(u,t->seg, "segment under train is not stable");
1122 static int reverse_trackend(TrackLocation *t, struct TrackAdvanceContext *c) {
1126 ErrorCode safety_setdirection(Train *tra, int backwards,
1127 PredictionProblemCallback *ppc, void *ppcu) {
1129 * tra->foredetect, maxinto, uncertainty
1131 * tra->plan_lookahead_nsegs
1133 * The following remain unchanged as all their users have
1134 * regard to the train's direction:
1137 TrackLocation newfdet;
1138 TrackAdvanceContext c;
1139 PredictUserContext u;
1140 const SegPosCombInfo *pci;
1142 int oldmaxinto, oldfdet_trbackwards, old_planlookaheadnsegs;
1145 struct timeval tnow;
1147 SKELETON_PREDICT_USER_CONTEXT(u,tra);
1149 MUSTECR( safety_checktrain(tra,ppc,ppcu) );
1151 if (tra->backwards == backwards)
1154 if (!speedmanager_stopped(tra))
1155 return predict_problem(&u,0, "train is not stopped");
1157 newfdet.seg= tra->foredetect;
1158 newfdet.remain= tra->maxinto;
1159 newfdet.backwards= !tra->foredetect->tr_backwards;
1161 c.distance= ceil(tra->detectable * MARGIN_TRAINLENGTH) + tra->uncertainty;
1162 c.nextseg= reverse_nextseg;
1164 c.trackend= reverse_trackend;
1167 MUSTECR( trackloc_advance(&newfdet,&c) );
1169 mgettimeofday(&tnow);
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;
1177 r= trackloc_getlink(&newfdet,&c,&pci,0,-1); assert(!r);
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;
1185 ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,ppc,ppcu);
1188 report_train_position(tra);
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);
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;
1202 ec2= predict(tra,tnow, PREDF_OLDPLAN,0,0, 0,0,(char*)"abandon reverse");
1208 /*========== entrypoints from rest of the program ==========*/
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);
1216 void safety_notify_detection(Segment *seg) {
1219 Segment *interferer;
1221 struct timeval tnow;
1222 SpeedInfo speed_info;
1224 if (seg->det_ignore) return;
1226 debug_count_event("detection");
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);
1241 if (seg->movposcomb < 0)
1242 safety_panic(tra,seg, "track route not set and train has arrived");
1244 mgettimeofday(&tnow);
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;
1251 tra->foredetect= seg;
1252 tra->uncertainty= tra->maxinto= maxinto;
1253 tra->plan_lookahead_nsegs--;
1254 report_train_position(tra);
1256 ec= predict(tra,tnow, PREDF_JUSTDET|PREDF_OLDPLAN,0,0,
1257 &speed_info, detection_report_problem,0);
1260 assert(ec == EC_SignallingPredictedProblem);
1262 if (maxinto > speed_info.stopping_distance)
1263 maxinto= speed_info.stopping_distance;
1264 tra->maxinto= tra->uncertainty= maxinto;
1266 report_train_position(tra);
1267 speedmanager_safety_stop(tra,tnow);
1270 ErrorCode safety_movposchange(Segment *seg, MovPosComb comb,
1271 int allow_queueing, PredictionProblemCallback *ppc, void *ppcu) {
1272 PredictUserContext u;
1273 struct timeval tnow;
1276 SKELETON_PREDICT_USER_CONTEXT(u,seg->owner);
1279 return movpos_change(seg,comb,-1,0);
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");
1287 if (allow_queueing<1)
1288 return predict_problem(&u,seg, "route set for approaching train");
1290 mgettimeofday(&tnow);
1291 ec= predict(seg->owner,tnow, 0, seg,comb, 0, ppc,ppcu);
1294 ec2= predict(seg->owner,tnow, PREDF_OLDPLAN, 0,0, 0,
1295 0,(char*)"abandon movposchange");
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;
1310 void safety_abandon_run(void) {
1314 if (seg->moving) continue;
1315 movpos_unreserve(seg->motion);