]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/train.cpp
Remove diversion logic
[r2c2.git] / source / libr2c2 / train.cpp
index b2b4899f9aa6eb7cc1ad83cee23039d073c4b5f4..eb5724b762a9777494ec71ec23acccf8bf81a787 100644 (file)
@@ -1,12 +1,7 @@
-/* $Id$
-
-This file is part of R²C²
-Copyright © 2006-2010  Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
+#include <algorithm>
 #include <cmath>
 #include <cmath>
-#include <msp/strings/formatter.h>
+#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
 #include <msp/time/units.h>
 #include <msp/time/utils.h>
 #include "aicontrol.h"
 #include <msp/time/units.h>
 #include <msp/time/utils.h>
 #include "aicontrol.h"
@@ -15,6 +10,7 @@ Distributed under the GPL
 #include "layout.h"
 #include "route.h"
 #include "simplecontroller.h"
 #include "layout.h"
 #include "route.h"
 #include "simplecontroller.h"
+#include "speedquantizer.h"
 #include "timetable.h"
 #include "trackiter.h"
 #include "tracktype.h"
 #include "timetable.h"
 #include "trackiter.h"
 #include "tracktype.h"
@@ -54,23 +50,25 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
        pending_block(0),
        reserving(false),
        advancing(false),
        pending_block(0),
        reserving(false),
        advancing(false),
-       controller(new AIControl(*this, new SimpleController)),
-       timetable(0),
+       controller(new SimpleController),
        active(false),
        current_speed_step(0),
        speed_changing(false),
        reverse(false),
        functions(0),
        end_of_route(false),
        active(false),
        current_speed_step(0),
        speed_changing(false),
        reverse(false),
        functions(0),
        end_of_route(false),
-       status("Unplaced"),
        travel_dist(0),
        pure_speed(false),
        travel_dist(0),
        pure_speed(false),
-       real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1),
+       speed_quantizer(0),
        accurate_position(false),
        overshoot_dist(false)
 {
        if(!loco_type.is_locomotive())
        accurate_position(false),
        overshoot_dist(false)
 {
        if(!loco_type.is_locomotive())
-               throw InvalidParameterValue("Initial vehicle must be a locomotive");
+               throw invalid_argument("Train::Train");
+
+       unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol);
+       if(speed_steps)
+               speed_quantizer = new SpeedQuantizer(speed_steps);
 
        vehicles.push_back(new Vehicle(layout, loco_type));
 
 
        vehicles.push_back(new Vehicle(layout, loco_type));
 
@@ -81,7 +79,7 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
        layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
 
        layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
        layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
 
        layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
-       layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
+       layout.signal_block_state_changed.connect(sigc::mem_fun(this, &Train::block_state_changed));
 
        layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
 
 
        layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
 
@@ -96,7 +94,6 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
 Train::~Train()
 {
        delete controller;
 Train::~Train()
 {
        delete controller;
-       delete timetable;
        for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
                delete *i;
        layout.remove_train(*this);
        for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
                delete *i;
        layout.remove_train(*this);
@@ -129,9 +126,9 @@ void Train::add_vehicle(const VehicleType &vt)
 void Train::remove_vehicle(unsigned i)
 {
        if(i>=vehicles.size())
 void Train::remove_vehicle(unsigned i)
 {
        if(i>=vehicles.size())
-               throw InvalidParameterValue("Vehicle index out of range");
+               throw out_of_range("Train::remove_vehicle");
        if(i==0)
        if(i==0)
-               throw InvalidParameterValue("Can't remove the locomotive");
+               throw logic_error("can't remove locomotive");
        delete vehicles[i];
        vehicles.erase(vehicles.begin()+i);
        if(i<vehicles.size())
        delete vehicles[i];
        vehicles.erase(vehicles.begin()+i);
        if(i<vehicles.size())
@@ -146,14 +143,14 @@ unsigned Train::get_n_vehicles() const
 Vehicle &Train::get_vehicle(unsigned i)
 {
        if(i>=vehicles.size())
 Vehicle &Train::get_vehicle(unsigned i)
 {
        if(i>=vehicles.size())
-               throw InvalidParameterValue("Vehicle index out of range");
+               throw out_of_range("Train::get_vehicle");
        return *vehicles[i];
 }
 
 const Vehicle &Train::get_vehicle(unsigned i) const
 {
        if(i>=vehicles.size())
        return *vehicles[i];
 }
 
 const Vehicle &Train::get_vehicle(unsigned i) const
 {
        if(i>=vehicles.size())
-               throw InvalidParameterValue("Vehicle index out of range");
+               throw out_of_range("Train::get_vehicle");
        return *vehicles[i];
 }
 
        return *vehicles[i];
 }
 
@@ -167,7 +164,7 @@ void Train::set_active(bool a)
        if(a==active)
                return;
        if(!a && controller->get_speed())
        if(a==active)
                return;
        if(!a && controller->get_speed())
-               throw InvalidState("Can't deactivate while moving");
+               throw logic_error("moving");
 
        active = a;
        if(active)
 
        active = a;
        if(active)
@@ -176,16 +173,13 @@ void Train::set_active(bool a)
                reserve_more();
        }
        else
                reserve_more();
        }
        else
-       {
                stop_timeout = Time::now()+2*Time::sec;
                stop_timeout = Time::now()+2*Time::sec;
-               set_status("Stopped");
-       }
 }
 
 void Train::set_function(unsigned func, bool state)
 {
        if(!loco_type.get_functions().count(func))
 }
 
 void Train::set_function(unsigned func, bool state)
 {
        if(!loco_type.get_functions().count(func))
-               throw InvalidParameterValue("Invalid function");
+               throw invalid_argument("Train::set_function");
        layout.get_driver().set_loco_function(address, func, state);
 }
 
        layout.get_driver().set_loco_function(address, func, state);
 }
 
@@ -199,15 +193,45 @@ float Train::get_speed() const
        return controller->get_speed();
 }
 
        return controller->get_speed();
 }
 
+float Train::get_quantized_speed() const
+{
+       if(speed_quantizer)
+               return speed_quantizer->quantize_speed(controller->get_speed());
+       else
+               return controller->get_speed();
+}
+
 bool Train::get_function(unsigned func) const
 {
        return (functions>>func)&1;
 }
 
 bool Train::get_function(unsigned func) const
 {
        return (functions>>func)&1;
 }
 
-void Train::set_timetable(Timetable *tt)
+void Train::add_ai(TrainAI &ai)
+{
+       ais.push_back(&ai);
+       ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai)));
+}
+
+void Train::remove_ai(TrainAI &ai)
 {
 {
-       delete timetable;
-       timetable = tt;
+       list<TrainAI *>::iterator i = find(ais.begin(), ais.end(), &ai);
+       if(i!=ais.end())
+               ais.erase(i);
+}
+
+TrainAI *Train::get_tagged_ai(const string &tag) const
+{
+       for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
+               if((*i)->get_tag()==tag)
+                       return *i;
+
+       return 0;
+}
+
+void Train::ai_message(const TrainAI::Message &msg)
+{
+       for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
+               (*i)->message(msg);
 }
 
 bool Train::set_route(const Route *r)
 }
 
 bool Train::set_route(const Route *r)
@@ -294,125 +318,30 @@ bool Train::go_to(const Zone &to)
        return set_route(route);
 }
 
        return set_route(route);
 }
 
-bool Train::divert(Track &from)
-{
-       if(!from.get_turnout_id())
-               throw InvalidParameterValue("Can't divert from a non-turnout");
-       if(routes.empty())
-               return false;
-
-       unsigned path = 0;
-       unsigned entry = 0;
-       list<RouteRef>::iterator route = routes.begin();
-
-       // Follow our routes to find out where we're entering the turnout
-       for(TrackLoopIter track = blocks.front().track_iter();;)
-       {
-               if(!advance_route(route, *track))
-                       return false;
-
-               if(&*track==&from)
-               {
-                       Block &block = track->get_block();
-                       if(block.get_train()==this && !free_block(block))
-                               return false;
-
-                       int route_path = route->route->get_turnout(from.get_turnout_id());
-
-                       // Check that more than one path is available
-                       unsigned ep_paths = track.endpoint().paths;
-                       if(!(ep_paths&(ep_paths-1)))
-                               return false;
-
-                       // Choose some other path
-                       for(int i=0; ep_paths>>i; ++i)
-                               if((ep_paths&(1<<i)) && i!=route_path)
-                               {
-                                       path = i;
-                                       break;
-                               }
-
-                       entry = track.entry();
-                       break;
-               }
-
-               track = track.next(route->route->get_path(*track));
-
-               if(!track || track.looped())
-                       return false;
-       }
-
-       TrackIter track = TrackIter(&from, entry).next(path);
-       if(!track)
-               return false;
-
-       set<Track *> tracks;
-       for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
-               tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
-       RefPtr<Route> diversion = Route::find(track, tracks);
-       if(!diversion)
-               return false;
-
-       diversion->set_name("Diversion");
-       diversion->add_track(from);
-       diversion->set_turnout(from.get_turnout_id(), path);
-
-       if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
-               return false;
-
-       // Follow the diversion route until we get back to the original route
-       list<RouteRef>::iterator end = routes.end();
-       while(1)
-       {
-               for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
-                       if(i->route->has_track(*track))
-                               end = i;
-
-               if(end!=routes.end())
-                       break;
-               else if(!diversion->has_track(*track))
-                       throw LogicError("Pathfinder returned a bad route");
-
-               track = track.next(diversion->get_path(*track));
-       }
-
-       if(route==end)
-               // We are rejoining the same route we diverted from, duplicate it
-               routes.insert(end, *route);
-       else
-       {
-               ++route;
-               routes.erase(route, end);
-       }
-       routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
-
-       return true;
-}
-
 const Route *Train::get_route() const
 {
        if(routes.empty())
                return 0;
 const Route *Train::get_route() const
 {
        if(routes.empty())
                return 0;
-       return routes.front().route;
+       return routes.front();
 }
 
 void Train::place(Block &block, unsigned entry)
 {
        if(controller->get_speed())
 }
 
 void Train::place(Block &block, unsigned entry)
 {
        if(controller->get_speed())
-               throw InvalidState("Must be stopped before placing");
+               throw logic_error("moving");
 
        release_blocks();
 
        set_active(false);
        accurate_position = false;
 
 
        release_blocks();
 
        set_active(false);
        accurate_position = false;
 
+       blocks.push_back(BlockIter(&block, entry));
        if(!block.reserve(this))
        {
        if(!block.reserve(this))
        {
-               set_status("Unplaced");
+               blocks.pop_back();
                return;
        }
 
                return;
        }
 
-       blocks.push_back(BlockIter(&block, entry));
        if(reverse)
        {
                TrackIter track = BlockIter(&block, entry).reverse().track_iter();
        if(reverse)
        {
                TrackIter track = BlockIter(&block, entry).reverse().track_iter();
@@ -428,7 +357,7 @@ void Train::place(Block &block, unsigned entry)
 void Train::unplace()
 {
        if(controller->get_speed())
 void Train::unplace()
 {
        if(controller->get_speed())
-               throw InvalidState("Must be stopped before unplacing");
+               throw logic_error("moving");
 
        release_blocks();
 
 
        release_blocks();
 
@@ -437,14 +366,11 @@ void Train::unplace()
 
        for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
                (*i)->unplace();
 
        for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
                (*i)->unplace();
-
-       set_status("Unplaced");
 }
 
 bool Train::free_block(Block &block)
 {
 }
 
 bool Train::free_block(Block &block)
 {
-       float margin = 10*layout.get_catalogue().get_scale();
-       if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
+       if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3)
                return false;
 
        unsigned nsens = 0;
                return false;
 
        unsigned nsens = 0;
@@ -524,7 +450,7 @@ void Train::free_noncritical_blocks()
        }
 }
 
        }
 }
 
-int Train::get_entry_to_block(Block &block) const
+int Train::get_entry_to_block(const Block &block) const
 {
        for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
                if(i->block()==&block)
 {
        for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
                if(i->block()==&block)
@@ -534,7 +460,15 @@ int Train::get_entry_to_block(Block &block) const
 
 float Train::get_reserved_distance() const
 {
 
 float Train::get_reserved_distance() const
 {
-       return get_reserved_distance_until(0, false);
+       if(blocks.empty())
+               return 0;
+
+       float margin = 0;
+       TrackIter next = blocks.back().next().track_iter();
+       if(next && next->get_type().is_turnout())
+               margin = 15*layout.get_catalogue().get_scale();
+
+       return max(get_reserved_distance_until(0, false)-margin, 0.0f);
 }
 
 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
 }
 
 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
@@ -547,36 +481,41 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
 
        Driver &driver = layout.get_driver();
 
 
        Driver &driver = layout.get_driver();
 
-       if(timetable)
-               timetable->tick(t);
+       for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
+               (*i)->tick(t, dt);
        controller->tick(dt);
        float speed = controller->get_speed();
        controller->tick(dt);
        float speed = controller->get_speed();
-       unsigned speed_step = find_speed_step(speed);
+       bool moving = speed>0;
 
        if(controller->get_reverse()!=reverse)
        {
                reverse = controller->get_reverse();
 
        if(controller->get_reverse()!=reverse)
        {
                reverse = controller->get_reverse();
-               driver.set_loco_reverse(address, reverse);
+               bool r = reverse;
+               if(loco_type.get_swap_direction())
+                       r = !r;
+               driver.set_loco_reverse(address, r);
 
                release_blocks(cur_blocks_end, blocks.end());
                reverse_blocks(blocks);
 
                reserve_more();
        }
 
                release_blocks(cur_blocks_end, blocks.end());
                reverse_blocks(blocks);
 
                reserve_more();
        }
-       if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
+
+       if(speed_quantizer)
        {
        {
-               speed_changing = true;
-               driver.set_loco_speed(address, speed_step);
+               unsigned speed_step = speed_quantizer->find_speed_step(speed);
+               if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
+               {
+                       speed_changing = true;
+                       driver.set_loco_speed(address, speed_step);
 
 
-               pure_speed = false;
+                       pure_speed = false;
+               }
 
 
-               if(speed_step)
-                       set_status(format("Traveling %d kmh", get_travel_speed()));
-               else
-                       set_status("Waiting");
+               speed = speed_quantizer->get_speed(current_speed_step);
        }
 
        }
 
-       if(speed)
+       if(moving)
        {
                if(!active)
                        set_active(true);
        {
                if(!active)
                        set_active(true);
@@ -588,11 +527,7 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
                        ok = (*i)->has_track(*track);
 
                for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
                        ok = (*i)->has_track(*track);
 
-               float d;
-               if(real_speed.size()>1)
-                       d = get_real_speed(current_speed_step)*(dt/Time::sec);
-               else
-                       d = speed*(dt/Time::sec);
+               float d = speed*(dt/Time::sec);
                if(ok)
                {
                        SetFlag setf(advancing);
                if(ok)
                {
                        SetFlag setf(advancing);
@@ -621,8 +556,9 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
 
                if(dist>10*layout.get_catalogue().get_scale())
                {
 
                if(dist>10*layout.get_catalogue().get_scale())
                {
-                       blocks.front()->reserve(0);
+                       Block &block = *blocks.front();
                        blocks.pop_front();
                        blocks.pop_front();
+                       block.reserve(0);
                }
        }
 }
                }
        }
 }
@@ -637,9 +573,12 @@ void Train::save(list<DataFile::Statement> &st) const
                if(i!=vehicles.begin())
                        st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
 
                if(i!=vehicles.begin())
                        st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
 
-       for(unsigned i=0; i<real_speed.size(); ++i)
-               if(real_speed[i].weight)
-                       st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
+       if(speed_quantizer)
+       {
+               DataFile::Statement ss("quantized_speed");
+               speed_quantizer->save(ss.sub);
+               st.push_back(ss);
+       }
 
        if(!blocks.empty() && cur_blocks_end!=blocks.begin())
        {
 
        if(!blocks.empty() && cur_blocks_end!=blocks.begin())
        {
@@ -656,18 +595,20 @@ void Train::save(list<DataFile::Statement> &st) const
 
        if(!routes.empty())
        {
 
        if(!routes.empty())
        {
-               list<RouteRef>::const_iterator i = routes.begin();
-               for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
+               list<const Route *>::const_iterator i = routes.begin();
+               for(; (i!=routes.end() && (*i)->is_temporary()); ++i) ;
                if(i!=routes.end())
                if(i!=routes.end())
-                       st.push_back((DataFile::Statement("route"), i->route->get_name()));
+                       st.push_back((DataFile::Statement("route"), (*i)->get_name()));
        }
 
        }
 
-       if(timetable)
-       {
-               DataFile::Statement ss("timetable");
-               timetable->save(ss.sub);
-               st.push_back(ss);
-       }
+       // XXX Need more generic way of saving AI state
+       for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
+               if(Timetable *timetable = dynamic_cast<Timetable *>(*i))
+               {
+                       DataFile::Statement ss("timetable");
+                       timetable->save(ss.sub);
+                       st.push_back(ss);
+               }
 }
 
 void Train::control_changed(const Controller::Control &ctrl)
 }
 
 void Train::control_changed(const Controller::Control &ctrl)
@@ -680,8 +621,11 @@ void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
        if(addr==address)
        {
                current_speed_step = speed;
        if(addr==address)
        {
                current_speed_step = speed;
-               if(rev!=reverse)
-                       layout.get_driver().set_loco_reverse(address, reverse);
+               bool r = reverse;
+               if(loco_type.get_swap_direction())
+                       r = !r;
+               if(rev!=r)
+                       layout.get_driver().set_loco_reverse(address, r);
                speed_changing = false;
                pure_speed = false;
        }
                speed_changing = false;
                pure_speed = false;
        }
@@ -700,9 +644,9 @@ void Train::loco_func_event(unsigned addr, unsigned func, bool state)
        }
 }
 
        }
 }
 
-void Train::sensor_event(unsigned addr, bool state)
+void Train::block_state_changed(Block &block, Block::State state)
 {
 {
-       if(state)
+       if(state==Block::MAYBE_ACTIVE)
        {
                // Find the first sensor block from our reserved blocks that isn't this sensor
                BlockList::iterator end;
        {
                // Find the first sensor block from our reserved blocks that isn't this sensor
                BlockList::iterator end;
@@ -710,7 +654,7 @@ void Train::sensor_event(unsigned addr, bool state)
                for(end=cur_blocks_end; end!=blocks.end(); ++end)
                        if((*end)->get_sensor_id())
                        {
                for(end=cur_blocks_end; end!=blocks.end(); ++end)
                        if((*end)->get_sensor_id())
                        {
-                               if((*end)->get_sensor_id()!=addr)
+                               if(&**end!=&block)
                                {
                                        if(result==0)
                                                result = 2;
                                {
                                        if(result==0)
                                                result = 2;
@@ -728,22 +672,15 @@ void Train::sensor_event(unsigned addr, bool state)
                        // Compute speed and update related state
                        float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
 
                        // Compute speed and update related state
                        float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
 
-                       if(pure_speed)
-                       {
-                               if(current_speed_step>0)
-                               {
-                                       RealSpeed &rs = real_speed[current_speed_step];
-                                       rs.add(travel_dist/travel_time_secs, travel_time_secs);
-                               }
-                               set_status(format("Traveling %d kmh", get_travel_speed()));
-                       }
+                       if(pure_speed && speed_quantizer && current_speed_step>0 && travel_time_secs>=2)
+                               speed_quantizer->learn(current_speed_step, travel_dist/travel_time_secs, travel_time_secs);
 
                        travel_dist = 0;
                        for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
                        {
                                travel_dist += (*j)->get_path_length(j->entry());
 
 
                        travel_dist = 0;
                        for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
                        {
                                travel_dist += (*j)->get_path_length(j->entry());
 
-                               if((*j)->get_sensor_id()==addr && !advancing)
+                               if(&**j==&block && !advancing)
                                {
                                        TrackIter track = j->track_iter();
                                        if(reverse)
                                {
                                        TrackIter track = j->track_iter();
                                        if(reverse)
@@ -763,18 +700,20 @@ void Train::sensor_event(unsigned addr, bool state)
                        // Check if we've reached the next route
                        if(routes.size()>1)
                        {
                        // Check if we've reached the next route
                        if(routes.size()>1)
                        {
-                               const Route &route = *(++routes.begin())->route;
+                               const Route &route = **(++routes.begin());
                                for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
                                        if(route.has_track(*j->track_iter()))
                                        {
                                                routes.pop_front();
                                                // XXX Exceptions?
                                for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
                                        if(route.has_track(*j->track_iter()))
                                        {
                                                routes.pop_front();
                                                // XXX Exceptions?
-                                               signal_route_changed.emit(routes.front().route);
+                                               signal_route_changed.emit(routes.front());
                                                break;
                                        }
                        }
 
                        // Move blocks up to the next sensor to our current blocks
                                                break;
                                        }
                        }
 
                        // Move blocks up to the next sensor to our current blocks
+                       for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
+                               signal_advanced.emit(**j);
                        cur_blocks_end = end;
 
                        // Try to get more blocks if we're moving
                        cur_blocks_end = end;
 
                        // Try to get more blocks if we're moving
@@ -784,7 +723,7 @@ void Train::sensor_event(unsigned addr, bool state)
                else if(result==3)
                        layout.emergency("Sensor for "+name+" triggered out of order");
        }
                else if(result==3)
                        layout.emergency("Sensor for "+name+" triggered out of order");
        }
-       else
+       else if(state==Block::INACTIVE)
        {
                const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
 
        {
                const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
 
@@ -852,7 +791,7 @@ void Train::reserve_more()
                        dist += (*i)->get_path_length(i->entry());
        }
 
                        dist += (*i)->get_path_length(i->entry());
        }
 
-       list<RouteRef>::iterator cur_route = routes.begin();
+       list<const Route *>::iterator cur_route = routes.begin();
        advance_route(cur_route, *start.track_iter());
 
        float approach_margin = 50*layout.get_catalogue().get_scale();
        advance_route(cur_route, *start.track_iter());
 
        float approach_margin = 50*layout.get_catalogue().get_scale();
@@ -860,8 +799,6 @@ void Train::reserve_more()
 
        BlockIter block = start;
        list<BlockIter>::iterator good_end = blocks.end();
 
        BlockIter block = start;
        list<BlockIter>::iterator good_end = blocks.end();
-       Track *divert_track = 0;
-       bool try_divert = false;
        Train *blocking_train = 0;
        BlockList contested_blocks;
 
        Train *blocking_train = 0;
        BlockList contested_blocks;
 
@@ -870,7 +807,7 @@ void Train::reserve_more()
        while(1)
        {
                BlockIter last = block;
        while(1)
        {
                BlockIter last = block;
-               block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
+               block = block.next(cur_route!=routes.end() ? *cur_route : 0);
                if(!block || block->get_endpoints().size()<2)
                {
                        if(!blocking_train)
                if(!block || block->get_endpoints().size()<2)
                {
                        if(!blocking_train)
@@ -926,7 +863,6 @@ void Train::reserve_more()
                                {
                                        yield_to(*blocking_train);
                                        pending_block = contested_blocks.front().block();
                                {
                                        yield_to(*blocking_train);
                                        pending_block = contested_blocks.front().block();
-                                       try_divert = divert_track;
                                        break;
                                }
                        }
                                        break;
                                }
                        }
@@ -937,9 +873,11 @@ void Train::reserve_more()
                        }
                }
 
                        }
                }
 
+               blocks.push_back(block);
                bool reserved = block->reserve(this);
                if(!reserved)
                {
                bool reserved = block->reserve(this);
                if(!reserved)
                {
+                       blocks.pop_back();
                        /* We've found another train.  If it wants to exit the block from the
                        same endpoint we're trying to enter from or the other way around,
                        treat it as coming towards us.  Otherwise treat it as going in the
                        /* We've found another train.  If it wants to exit the block from the
                        same endpoint we're trying to enter from or the other way around,
                        treat it as coming towards us.  Otherwise treat it as going in the
@@ -947,7 +885,7 @@ void Train::reserve_more()
                        Train *other_train = block->get_train();
                        int other_entry = other_train->get_entry_to_block(*block);
                        if(other_entry<0)
                        Train *other_train = block->get_train();
                        int other_entry = other_train->get_entry_to_block(*block);
                        if(other_entry<0)
-                               throw LogicError("Block reservation inconsistency");
+                               throw logic_error("block reservation inconsistency");
 
                        unsigned exit = block.reverse().entry();
                        unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
 
                        unsigned exit = block.reverse().entry();
                        unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
@@ -970,7 +908,11 @@ void Train::reserve_more()
                                /* Ask a lesser priority train going to the same direction to free
                                the block for us */
                                if(other_train->free_block(*block))
                                /* Ask a lesser priority train going to the same direction to free
                                the block for us */
                                if(other_train->free_block(*block))
-                                       reserved = block->reserve(this);
+                               {
+                                       blocks.push_back(block);
+                                       if(!(reserved = block->reserve(this)))
+                                               blocks.pop_back();
+                               }
                        }
                        else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
                        {
                        }
                        else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
                        {
@@ -981,9 +923,6 @@ void Train::reserve_more()
                                contested_blocks.push_back(block);
                                continue;
                        }
                                contested_blocks.push_back(block);
                                continue;
                        }
-                       else if(divert_track && (entry_conflict || exit_conflict || !other_train->is_active()))
-                               // We are blocked, but there's a diversion possibility
-                               try_divert = true;
 
                        if(!reserved)
                        {
 
                        if(!reserved)
                        {
@@ -992,22 +931,9 @@ void Train::reserve_more()
                        }
                }
 
                        }
                }
 
-               if(block->get_turnout_id())
-               {
-                       const TrackType::Endpoint &track_ep = track.endpoint();
-                       bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
-
-                       if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
-                               /* There's multiple paths to be taken and we are on a route - take
-                               note of the diversion possibility */
-                               divert_track = &*track;
-               }
-
                if(!contested_blocks.empty() && contested_blocks.front()==block)
                        contested_blocks.pop_front();
 
                if(!contested_blocks.empty() && contested_blocks.front()==block)
                        contested_blocks.pop_front();
 
-               blocks.push_back(block);
-
                if(cur_blocks_end==blocks.end())
                        --cur_blocks_end;
                if(clear_blocks_end==blocks.end())
                if(cur_blocks_end==blocks.end())
                        --cur_blocks_end;
                if(clear_blocks_end==blocks.end())
@@ -1033,9 +959,6 @@ void Train::reserve_more()
        // Make any sensorless blocks at the beginning immediately current
        while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
                ++cur_blocks_end;
        // Make any sensorless blocks at the beginning immediately current
        while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
                ++cur_blocks_end;
-
-       if(try_divert && divert(*divert_track))
-               reserve_more();
 }
 
 void Train::check_turnout_paths(bool set)
 }
 
 void Train::check_turnout_paths(bool set)
@@ -1075,6 +998,8 @@ void Train::check_turnout_paths(bool set)
 
                if(i==clear_blocks_end)
                        ++clear_blocks_end;
 
                if(i==clear_blocks_end)
                        ++clear_blocks_end;
+               if(i==cur_blocks_end && !(*i)->get_sensor_id())
+                       ++cur_blocks_end;
        }
 }
 
        }
 }
 
@@ -1134,87 +1059,6 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
        return result;
 }
 
        return result;
 }
 
-float Train::get_real_speed(unsigned i) const
-{
-       if(i==0)
-               return 0;
-       if(real_speed[i].weight)
-               return real_speed[i].speed;
-
-       unsigned low;
-       unsigned high;
-       for(low=i; low>0; --low)
-               if(real_speed[low].weight)
-                       break;
-       for(high=i; high+1<real_speed.size(); ++high)
-               if(real_speed[high].weight)
-                       break;
-
-       if(real_speed[high].weight)
-       {
-               if(real_speed[low].weight)
-               {
-                       float f = float(i-low)/(high-low);
-                       return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
-               }
-               else
-                       return real_speed[high].speed*float(i)/high;
-       }
-       else if(real_speed[low].weight)
-               return real_speed[low].speed*float(i)/low;
-       else
-               return 0;
-}
-
-unsigned Train::find_speed_step(float real) const
-{
-       if(real_speed.size()<=1)
-               return 0;
-       if(real<=real_speed[1].speed*0.5)
-               return 0;
-
-       unsigned low = 0;
-       unsigned high = 0;
-       unsigned last = 0;
-       for(unsigned i=0; (!high && i<real_speed.size()); ++i)
-               if(real_speed[i].weight)
-               {
-                       last = i;
-                       if(real_speed[i].speed<real)
-                               low = i;
-                       else
-                               high = i;
-               }
-       if(!high)
-       {
-               unsigned limit = real_speed.size()/5;
-               if(!low)
-               {
-                       if(real)
-                               return limit;
-                       else
-                               return 0;
-               }
-               return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), real_speed.size()-1), last+limit);
-       }
-
-       float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
-       return static_cast<unsigned>(low*(1-f)+high*f+0.5);
-}
-
-float Train::get_travel_speed() const
-{
-       float speed = get_real_speed(current_speed_step);
-       float scale = layout.get_catalogue().get_scale();
-       return static_cast<int>(round(speed/scale*3.6/5))*5;
-}
-
-void Train::set_status(const string &s)
-{
-       status = s;
-       signal_status_changed.emit(s);
-}
-
 void Train::release_blocks()
 {
        release_blocks(blocks.begin(), blocks.end());
 void Train::release_blocks()
 {
        release_blocks(blocks.begin(), blocks.end());
@@ -1245,18 +1089,13 @@ void Train::reverse_blocks(BlockList &blks) const
                *i = i->reverse();
 }
 
                *i = i->reverse();
 }
 
-bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
+bool Train::advance_route(list<const Route *>::iterator &iter, Track &track)
 {
 {
-       while(iter!=routes.end() && !iter->route->has_track(track))
+       while(iter!=routes.end() && !(*iter)->has_track(track))
                ++iter;
        if(iter==routes.end())
                return false;
 
                ++iter;
        if(iter==routes.end())
                return false;
 
-       list<RouteRef>::iterator next = iter;
-       ++next;
-       if(next!=routes.end() && next->diversion && next->route->has_track(track))
-               iter = next;
-
        return true;
 }
 
        return true;
 }
 
@@ -1283,74 +1122,9 @@ Route *Train::create_lead_route(Route *lead, const Route *target)
        return lead;
 }
 
        return lead;
 }
 
-bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
-{
-       float diversion_len = 0;
-       TrackLoopIter track1 = from;
-       while(diversion.has_track(*track1))
-       {
-               unsigned path = diversion.get_path(*track1);
-               diversion_len += track1->get_type().get_path_length(path);
-
-               track1 = track1.next(path);
-
-               if(track1.looped())
-                       return false;
-       }
-
-       list<RouteRef>::iterator route = routes.begin();
-       if(!advance_route(route, *from))
-               return false;
-
-       float route_len = 0;
-       TrackLoopIter track2 = from;
-       while(1)
-       {
-               unsigned path = route->route->get_path(*track2);
-               route_len += track2->get_type().get_path_length(path);
-
-               bool ok = (track2!=from && diversion.has_track(*track2));
-
-               track2 = track2.next(path);
-
-               if(ok)
-                       break;
-
-               if(track2.looped())
-                       return false;
-
-               if(!advance_route(route, *track2))
-                       return false;
-       }
-
-       // Must end up at the same place through both routes
-       if(track2!=track1)
-               return false;
-
-       return diversion_len<route_len*1.2;
-}
-
-
-Train::RouteRef::RouteRef(const Route *r, unsigned d):
-       route(r),
-       diversion(d)
-{ }
-
-
-Train::RealSpeed::RealSpeed():
-       speed(0),
-       weight(0)
-{ }
-
-void Train::RealSpeed::add(float s, float w)
-{
-       speed = (speed*weight+s*w)/(weight+w);
-       weight = min(weight+w, 300.0f);
-}
-
 
 Train::Loader::Loader(Train &t):
 
 Train::Loader::Loader(Train &t):
-       DataFile::BasicLoader<Train>(t),
+       DataFile::ObjectLoader<Train>(t),
        prev_block(0),
        blocks_valid(true)
 {
        prev_block(0),
        blocks_valid(true)
 {
@@ -1358,7 +1132,7 @@ Train::Loader::Loader(Train &t):
        add("block_hint",  &Loader::block_hint);
        add("name",        &Loader::name);
        add("priority",    &Train::priority);
        add("block_hint",  &Loader::block_hint);
        add("name",        &Loader::name);
        add("priority",    &Train::priority);
-       add("real_speed",  &Loader::real_speed);
+       add("quantized_speed",  &Loader::quantized_speed);
        add("route",       &Loader::route);
        add("timetable",   &Loader::timetable);
        add("vehicle",     &Loader::vehicle);
        add("route",       &Loader::route);
        add("timetable",   &Loader::timetable);
        add("vehicle",     &Loader::vehicle);
@@ -1371,8 +1145,6 @@ void Train::Loader::finish()
                TrackIter track = obj.blocks.front().track_iter();
                float offset = 2*obj.layout.get_catalogue().get_scale();
                obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
                TrackIter track = obj.blocks.front().track_iter();
                float offset = 2*obj.layout.get_catalogue().get_scale();
                obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
-
-               obj.set_status("Stopped");
        }
 }
 
        }
 }
 
@@ -1386,7 +1158,7 @@ void Train::Loader::block(unsigned id)
        {
                blk = &obj.layout.get_block(id);
        }
        {
                blk = &obj.layout.get_block(id);
        }
-       catch(const KeyError &)
+       catch(const key_error &)
        {
                blocks_valid = false;
                return;
        {
                blocks_valid = false;
                return;
@@ -1398,8 +1170,8 @@ void Train::Loader::block(unsigned id)
        if(entry<0)
                entry = 0;
 
        if(entry<0)
                entry = 0;
 
-       blk->reserve(&obj);
        obj.blocks.push_back(BlockIter(blk, entry));
        obj.blocks.push_back(BlockIter(blk, entry));
+       blk->reserve(&obj);
 
        if(blk->get_sensor_id())
                obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
 
        if(blk->get_sensor_id())
                obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
@@ -1413,7 +1185,7 @@ void Train::Loader::block_hint(unsigned id)
        {
                prev_block = &obj.layout.get_block(id);
        }
        {
                prev_block = &obj.layout.get_block(id);
        }
-       catch(const KeyError &)
+       catch(const key_error &)
        {
                blocks_valid = false;
        }
        {
                blocks_valid = false;
        }
@@ -1424,12 +1196,10 @@ void Train::Loader::name(const string &n)
        obj.set_name(n);
 }
 
        obj.set_name(n);
 }
 
-void Train::Loader::real_speed(unsigned i, float speed, float weight)
+void Train::Loader::quantized_speed()
 {
 {
-       if(i>=obj.real_speed.size())
-               return;
-       obj.real_speed[i].speed = speed;
-       obj.real_speed[i].weight = weight;
+       if(obj.speed_quantizer)
+               load_sub(*obj.speed_quantizer);
 }
 
 void Train::Loader::route(const string &n)
 }
 
 void Train::Loader::route(const string &n)
@@ -1439,11 +1209,8 @@ void Train::Loader::route(const string &n)
 
 void Train::Loader::timetable()
 {
 
 void Train::Loader::timetable()
 {
-       if(obj.timetable)
-               throw InvalidState("A timetable has already been loaded");
-
-       obj.timetable = new Timetable(obj);
-       load_sub(*obj.timetable);
+       Timetable *ttbl = new Timetable(obj);
+       load_sub(*ttbl);
 }
 
 void Train::Loader::vehicle(ArticleNumber art_nr)
 }
 
 void Train::Loader::vehicle(ArticleNumber art_nr)