From 3fce1269879f4b4d676f249f91efd69286a606fd Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 15 Nov 2010 21:06:46 +0000 Subject: [PATCH] Add support for MM-27 protocol in Intellibox driver Use an enum instead of string for storing train protocol Fix an off-by-one error in Train --- source/libmarklin/intellibox.cpp | 78 ++++++++++++++++++++++++++++---- source/libmarklin/intellibox.h | 21 ++++++++- source/libmarklin/train.cpp | 2 +- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/source/libmarklin/intellibox.cpp b/source/libmarklin/intellibox.cpp index bbe6781..1dc2e70 100644 --- a/source/libmarklin/intellibox.cpp +++ b/source/libmarklin/intellibox.cpp @@ -96,21 +96,27 @@ void Intellibox::halt(bool h) const char *Intellibox::enumerate_protocols(unsigned i) const { - if(i==0) + if(i==MM) return "MM"; + else if(i==MM_27) + return "MM-27"; return 0; } -unsigned Intellibox::get_protocol_speed_steps(const string &proto) const +unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const { - if(proto=="MM") + Protocol proto = map_protocol(proto_name); + if(proto==MM) return 14; - else - throw InvalidParameterValue("Unknown protocol"); + else if(proto==MM_27) + return 27; + return 0; } -void Intellibox::add_loco(unsigned addr, const string &proto) +void Intellibox::add_loco(unsigned addr, const string &proto_name) { + Protocol proto = map_protocol(proto_name); + if(!locos.count(addr)) { locos[addr].protocol = proto; @@ -126,12 +132,47 @@ void Intellibox::set_loco_speed(unsigned addr, unsigned speed) { Locomotive &loco = locos[addr]; if(speed==loco.speed) + { + if(loco.pending_half_step) + { + loco.pending_half_step = 0; + loco.half_step_delay = Time::TimeStamp(); + signal_loco_speed.emit(addr, speed, loco.reverse); + } return; + } if(speed && halted) return; + if(loco.protocol==MM_27) + { + if(speed>27) + speed = 27; + + if(speed>loco.speed && !(speed&1)) + { + loco.pending_half_step = -1; + speed |= 1; + } + else if(speed14) + speed = 14; + + loco_command(addr, speed, loco.reverse, loco.funcs|0x100); + } loco.speed = speed; - loco_command(addr, speed, loco.reverse, loco.funcs|0x100); } void Intellibox::set_loco_reverse(unsigned addr, bool rev) @@ -216,6 +257,15 @@ void Intellibox::tick() command(CMD_EVENT); } + for(map::iterator i=locos.begin(); i!=locos.end(); ++i) + if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay) + { + i->second.speed += i->second.pending_half_step; + i->second.pending_half_step = 0; + i->second.half_step_delay = Time::TimeStamp(); + loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs|0x100); + } + for(map::iterator i=turnouts.begin(); i!=turnouts.end(); ++i) if(i->second.active && i->second.off_timeout && t>i->second.off_timeout) { @@ -284,6 +334,16 @@ void Intellibox::flush() command_sent = false; } +Intellibox::Protocol Intellibox::map_protocol(const string &name) const +{ + if(name=="MM") + return MM; + else if(name=="MM-27") + return MM_27; + else + throw InvalidParameterValue("Unknown protocol"); +} + void Intellibox::command(Command cmd) { command(cmd, 0, 0); @@ -438,7 +498,9 @@ void Intellibox::process_reply(const Time::TimeStamp &t) { unsigned addr = queue.front().addr; Locomotive &loco = locos[addr]; - signal_loco_speed.emit(addr, loco.speed, loco.reverse); + signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse); + if(loco.pending_half_step) + loco.half_step_delay = Time::now()+500*Time::msec; } else error(cmd, err); diff --git a/source/libmarklin/intellibox.h b/source/libmarklin/intellibox.h index 565d3cb..6d061a6 100644 --- a/source/libmarklin/intellibox.h +++ b/source/libmarklin/intellibox.h @@ -14,6 +14,16 @@ Distributed under the GPL namespace Marklin { +/** +Driver for Uhlenbrock Intellibox. Uses the P50X binary protocol over RS232. + +Motorola decoders with 27 speed steps are supported by manually generating the +commands necessary to reach the "half-steps". However, sending a rapid stream +of speed changes to the same locomotive seems to cause excessive lag, so we +cheat a bit; instead of sending the half-step command immediately, we send it +with a 500ms delay, but only if no new set_loco_speed calls have occurred. As +a downside from this accelerations and decelerations are still jerky. +*/ class Intellibox: public Driver { private: @@ -61,12 +71,20 @@ private: ERR_LOK_POWER_OFF, }; + enum Protocol + { + MM, + MM_27 + }; + struct Locomotive { - std::string protocol; + Protocol protocol; unsigned speed; bool reverse; unsigned funcs; + int pending_half_step; + Msp::Time::TimeStamp half_step_delay; Locomotive(); }; @@ -135,6 +153,7 @@ public: virtual void flush(); private: + Protocol map_protocol(const std::string &) const; void command(Command); void command(Command, const unsigned char *, unsigned); void command(Command, unsigned, const unsigned char *, unsigned); diff --git a/source/libmarklin/train.cpp b/source/libmarklin/train.cpp index 0084f38..859d3da 100644 --- a/source/libmarklin/train.cpp +++ b/source/libmarklin/train.cpp @@ -1109,7 +1109,7 @@ float Train::get_real_speed(unsigned i) const for(low=i; low>0; --low) if(real_speed[low].weight) break; - for(high=i; high