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;
{
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(speed<loco.speed && (speed&1))
+ {
+ loco.pending_half_step = 1;
+ speed &= ~1;
+ }
+ else
+ loco.pending_half_step = 0;
+ loco.half_step_delay = Time::TimeStamp();
+
+ loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs|0x100);
+ }
+ else
+ {
+ if(speed>14)
+ 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)
command(CMD_EVENT);
}
+ for(map<unsigned, Locomotive>::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<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
{
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);
{
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);
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:
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();
};
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);