Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Loco.hxx
Go to the documentation of this file.
1
35#ifndef _DCC_LOCO_HXX_
36#define _DCC_LOCO_HXX_
37
38#include "dcc/Defs.hxx"
39#include "dcc/Packet.hxx"
40#include "dcc/PacketSource.hxx"
41#include "dcc/UpdateLoop.hxx"
42#include "utils/constants.hxx"
43#include "utils/logging.h"
44
48DECLARE_CONST(dcc_virtual_f0_offset);
49
50namespace dcc
51{
52
65{
66 REFRESH = 0,
67 SPEED = 1,
68 FUNCTION0 = 2,
69 FUNCTION5 = 3,
70 FUNCTION9 = 4,
71 FUNCTION13 = 5,
72 FUNCTION21 = 6,
73 FUNCTION29 = 7,
74 FUNCTION37 = 8,
75 FUNCTION45 = 9,
76 FUNCTION53 = 10,
77 FUNCTION61 = 11,
78 MM_F1 = 2,
79 MM_F2,
80 MM_F3,
81 MM_F4,
82 MIN_REFRESH = SPEED,
85 MAX_REFRESH = FUNCTION9,
86 MM_MAX_REFRESH = 7,
87 ESTOP = 16,
88};
89
105template <class P> class AbstractTrain : public PacketSource
106{
107public:
109 {
110 }
111
116 {
117 float16_t new_speed = speed.get_wire();
118 if (p.lastSetSpeed_ == new_speed)
119 {
120 LOG(VERBOSE, "not updating speed: old speed %04x, new speed %04x",
121 p.lastSetSpeed_, new_speed);
122 return;
123 }
124 p.lastSetSpeed_ = new_speed;
125 p.isEstop_ = false;
126 unsigned previous_light = get_effective_f0();
127 if (speed.direction() != p.direction_)
128 {
129 p.directionChanged_ = 1;
130 p.direction_ = speed.direction();
132 }
133 float f_speed = speed.mph();
134 if (f_speed > 0)
135 {
136 f_speed *= ((p.get_speed_steps() * 1.0) / 126);
137 unsigned sp = f_speed + 0.5; // round
138 if (sp == 0) // ceiling between speed step 0 and 1
139 {
140 sp = 1;
141 }
142 if (sp > p.get_speed_steps())
143 {
144 sp = p.get_speed_steps();
145 }
146 LOG(VERBOSE, "set speed to step %u", sp);
147 p.speed_ = sp;
148 }
149 else
150 {
151 p.speed_ = 0;
152 }
153 unsigned light = get_effective_f0();
154 if (previous_light && !light)
155 {
156 // Turns off light first then sends speed packet.
157 packet_processor_notify_update(this, p.get_fn_update_code(0));
158 }
159 packet_processor_notify_update(this, SPEED);
160 if (light && !previous_light)
161 {
162 // Turns on light after sending speed packets.
163 packet_processor_notify_update(this, p.get_fn_update_code(0));
164 }
165 }
166
169 {
170 SpeedType v;
171 v.set_wire(p.lastSetSpeed_);
172 return v;
173 }
177 {
178 return get_speed();
179 }
182 {
183 p.speed_ = 0;
184 p.isEstop_ = true;
185 SpeedType dir0;
186 dir0.set_direction(p.direction_);
187 p.lastSetSpeed_ = dir0.get_wire();
188 p.directionChanged_ = 1;
192 packet_processor_notify_update(this, ESTOP);
193 }
196 {
197 return p.isEstop_;
198 }
201 void set_fn(uint32_t address, uint16_t value) OVERRIDE
202 {
203 const uint32_t virtf0 = config_dcc_virtual_f0_offset();
204 if (address == 0 && p.f0SetDirectional_)
205 {
206 if (p.direction_ == 0)
207 {
208 p.f0OnForward_ = value ? 1 : 0;
209 }
210 else
211 {
212 p.f0OnReverse_ = value ? 1 : 0;
213 }
214 // continue into the handling of f0.
215 }
216 else if (address == virtf0 + VIRTF0_DIRECTIONAL_ENABLE)
217 {
218 if (value)
219 {
220 p.f0SetDirectional_ = 1;
221 // Populates new state of separate f0 forward and f0
222 // reverse.
223 if (p.direction_ == 0)
224 {
225 p.f0OnForward_ = p.get_fn_store(0);
226 p.f0OnReverse_ = 0;
227 }
228 else
229 {
230 p.f0OnReverse_ = p.get_fn_store(0);
231 p.f0OnForward_ = 0;
232 }
233 }
234 else
235 {
236 p.f0SetDirectional_ = 0;
237 // whatever value we have in fn_[0] now is going to be the
238 // new state, so we don't change anything.
239 }
240 // This command never changes f0, so no packets need to be sent to
241 // the track.
242 return;
243 }
244 else if (address == virtf0 + VIRTF0_BLANK_FWD)
245 {
246 p.f0BlankForward_ = value ? 1 : 0;
247 packet_processor_notify_update(this, p.get_fn_update_code(0));
248 return;
249 }
250 else if (address == virtf0 + VIRTF0_BLANK_REV)
251 {
252 p.f0BlankReverse_ = value ? 1 : 0;
253 packet_processor_notify_update(this, p.get_fn_update_code(0));
254 return;
255 }
256 if (address > p.get_max_fn())
257 {
258 // Ignore.
259 return;
260 }
261 p.set_fn_store(address, value);
262 packet_processor_notify_update(this, p.get_fn_update_code(address));
263 }
266 uint16_t get_fn(uint32_t address) OVERRIDE
267 {
268 const uint32_t virtf0 = config_dcc_virtual_f0_offset();
269 if (address == virtf0 + VIRTF0_DIRECTIONAL_ENABLE)
270 {
271 return p.f0SetDirectional_;
272 }
273 else if (address == virtf0 + VIRTF0_BLANK_FWD)
274 {
275 return p.f0BlankForward_;
276 }
277 else if (address == virtf0 + VIRTF0_BLANK_REV)
278 {
279 return p.f0BlankReverse_;
280 }
281 if (address > p.get_max_fn())
282 {
283 // Unknown.
284 return 0;
285 }
286 return p.get_fn_store(address) ? 1 : 0;
287 }
290 {
291 return p.address_;
292 }
295 {
296 return p.get_address_type();
297 }
298
299protected:
303 static constexpr unsigned VIRTF0_DIRECTIONAL_ENABLE = 0;
308 static constexpr unsigned VIRTF0_BLANK_FWD = 1;
313 static constexpr unsigned VIRTF0_BLANK_REV = 2;
314
318 {
319 unsigned is_on = p.f0SetDirectional_ == 0 ? (p.fn_ & 1)
320 : p.direction_ == 0 ? p.f0OnForward_
321 : p.f0OnReverse_;
322 if (p.direction_ == 0 && p.f0BlankForward_)
323 {
324 is_on = 0;
325 }
326 if (p.direction_ == 1 && p.f0BlankReverse_)
327 {
328 is_on = 0;
329 }
330 return is_on;
331 }
332
335 {
336 if (p.f0SetDirectional_)
337 {
338 p.fn_ &= ~1;
339 if (p.direction_ == 0 && p.f0OnForward_)
340 {
341 p.fn_ |= 1;
342 }
343 if (p.direction_ == 1 && p.f0OnReverse_)
344 {
345 p.fn_ |= 1;
346 }
347 }
348 }
349
351 P p;
352};
353
356{
358 uint16_t address_ : 14;
360 uint16_t isShortAddress_ : 1;
362 uint16_t direction_ : 1;
364 uint16_t lastSetSpeed_ : 16;
365
366 // ==== 32-bit boundary ====
367
369 unsigned fn_ : 29;
371 uint8_t nextRefresh_ : 3;
372
373 // ==== 32-bit boundary ====
374
376 uint8_t isEstop_ : 1;
378 uint8_t directionChanged_ : 1;
380 uint8_t f0SetDirectional_ : 1;
382 uint8_t f0OnForward_ : 1;
384 uint8_t f0OnReverse_ : 1;
386 uint8_t f0BlankForward_ : 1;
388 uint8_t f0BlankReverse_ : 1;
390 uint8_t speed_ : 7;
391
393 uint8_t fhi_[5];
394
397 static unsigned get_max_fn()
398 {
399 return 68;
400 }
401
405 void set_fn_store(unsigned idx, bool value)
406 {
407 if (idx < 29)
408 {
409 if (value)
410 {
411 fn_ |= (1u << idx);
412 }
413 else
414 {
415 fn_ &= ~(1u << idx);
416 }
417 }
418 else
419 {
420 idx -= 29;
421 if (value)
422 {
423 fhi_[idx / 8] |= (1u << (idx & 7));
424 }
425 else
426 {
427 fhi_[idx / 8] &= ~(1u << (idx & 7));
428 }
429 }
430 }
431
435 bool get_fn_store(unsigned idx)
436 {
437 if (idx < 29)
438 {
439 return (fn_ & (1u << idx)) != 0;
440 }
441 else
442 {
443 idx -= 29;
444 return (fhi_[idx / 8] & (1u << (idx & 7))) != 0;
445 }
446 }
447
450 static unsigned get_fn_update_code(unsigned address);
451
454 {
455 return isShortAddress_ ? TrainAddressType::DCC_SHORT_ADDRESS
456 : TrainAddressType::DCC_LONG_ADDRESS;
457 }
458};
459
462{
464 {
465 memset(this, 0, sizeof(*this));
466 }
467
469 static unsigned get_speed_steps()
470 {
471 return 28;
472 }
473
480
487};
488
489static_assert(sizeof(Dcc28Payload) == 16, "size of dcc payload is wrong");
490
492template <class Payload> class DccTrain : public AbstractTrain<Payload>
493{
494public:
497 {
498 this->p.isShortAddress_ = 1;
499 this->p.address_ = a.value;
500 packet_processor_add_refresh_source(this);
501 }
502
505 {
506 this->p.isShortAddress_ = 0;
507 this->p.address_ = a.value;
508 packet_processor_add_refresh_source(this);
509 }
510
511 ~DccTrain();
512
516 void get_next_packet(unsigned code, Packet *packet) OVERRIDE;
517};
518
521
524{
526 {
527 memset(this, 0, sizeof(*this));
528 }
529
531 static unsigned get_speed_steps()
532 {
533 return 126;
534 }
535
542
549};
550
553
557{
559 {
560 memset(this, 0, sizeof(*this));
561 }
564 unsigned address_ : 8;
566 unsigned lastSetSpeed_ : 16;
568 unsigned fn_ : 1;
570 unsigned direction_ : 1;
572 unsigned directionChanged_ : 1;
574 unsigned speed_ : 4;
576 unsigned isEstop_ : 1;
577
579 unsigned f0SetDirectional_ : 1;
581 unsigned f0OnForward_ : 1;
583 unsigned f0OnReverse_ : 1;
585 unsigned f0BlankForward_ : 1;
587 unsigned f0BlankReverse_ : 1;
588
591 {
592 return 14;
593 }
594
596 unsigned get_max_fn()
597 {
598 return 0;
599 }
600
604 void set_fn_store(unsigned idx, bool value) {
605 if (value)
606 {
607 fn_ |= (1u << idx);
608 }
609 else
610 {
611 fn_ &= ~(1u << idx);
612 }
613 }
614
618 bool get_fn_store(unsigned idx)
619 {
620 return (fn_ & (1u << idx)) != 0;
621 }
622
625 unsigned get_fn_update_code(unsigned address)
626 {
627 return SPEED;
628 }
629
632 {
633 return TrainAddressType::MM;
634 }
635};
636
638class MMOldTrain : public AbstractTrain<MMOldPayload>
639{
640public:
643 ~MMOldTrain();
644
648 void get_next_packet(unsigned code, Packet *packet) OVERRIDE;
649};
650
654{
656 {
657 memset(this, 0, sizeof(*this));
658 }
661 unsigned address_ : 8;
663 unsigned lastSetSpeed_ : 16;
665 unsigned fn_ : 5;
667 unsigned direction_ : 1;
669 unsigned directionChanged_ : 1;
671 unsigned resvd1_ : 1;
673 unsigned speed_ : 4;
675 unsigned nextRefresh_ : 3;
677 unsigned isEstop_ : 1;
678
680 unsigned f0SetDirectional_ : 1;
682 unsigned f0OnForward_ : 1;
684 unsigned f0OnReverse_ : 1;
686 unsigned f0BlankForward_ : 1;
688 unsigned f0BlankReverse_ : 1;
689
692 {
693 return 14;
694 }
695
697 unsigned get_max_fn()
698 {
699 return 4;
700 }
701
705 void set_fn_store(unsigned idx, bool value) {
706 if (value)
707 {
708 fn_ |= (1u << idx);
709 }
710 else
711 {
712 fn_ &= ~(1u << idx);
713 }
714 }
715
719 bool get_fn_store(unsigned idx)
720 {
721 return (fn_ & (1u << idx)) != 0;
722 }
723
726 unsigned get_fn_update_code(unsigned address)
727 {
728 if (1 <= address && address <= 4)
729 {
730 return MM_F1 + address - 1;
731 }
732 else
733 {
734 return SPEED;
735 }
736 }
737
740 {
741 return TrainAddressType::MM;
742 }
743};
744
746class MMNewTrain : public AbstractTrain<MMNewPayload>
747{
748public:
751 ~MMNewTrain();
752
756 void get_next_packet(unsigned code, Packet *packet) OVERRIDE;
757};
758
759} // namespace dcc
760
761#endif // _DCC_LOCO_HXX_
DccTrainUpdateCode
Describes what sort of packet the dcc:PacketSource (usually a single train) should generate.
Definition Loco.hxx:65
@ MAX_REFRESH
@TODO(balazs.racz) choose adaptive max-refresh based on how many functions are actually in use for th...
Definition Loco.hxx:85
uint16_t float16_t
This type represents how velocity is seen on the wire (16 bit float).
Definition Velocity.hxx:55
AbstractTrain is a templated class for train implementations in a command station.
Definition Loco.hxx:106
static constexpr unsigned VIRTF0_BLANK_REV
Function number of "Blank F0 Reverse".
Definition Loco.hxx:313
uint32_t legacy_address() OVERRIDE
Definition Loco.hxx:289
SpeedType get_commanded_speed() OVERRIDE
Definition Loco.hxx:176
unsigned get_effective_f0()
Definition Loco.hxx:317
void set_emergencystop() OVERRIDE
Sets the train to ESTOP state, generating an emergency stop packet.
Definition Loco.hxx:181
SpeedType get_speed() OVERRIDE
Definition Loco.hxx:168
P p
Payload – actual data we know about the train.
Definition Loco.hxx:351
bool get_emergencystop() OVERRIDE
Gets the train's ESTOP state.
Definition Loco.hxx:195
TrainAddressType legacy_address_type() OVERRIDE
Definition Loco.hxx:294
void set_speed(SpeedType speed) OVERRIDE
Sets the train speed (asking for a high-priority outgoing update packet to be generated).
Definition Loco.hxx:115
void update_f0_direction_changed()
Updates the f0 states after a direction change occurred.
Definition Loco.hxx:334
uint16_t get_fn(uint32_t address) OVERRIDE
Definition Loco.hxx:266
static constexpr unsigned VIRTF0_BLANK_FWD
Function number of "Blank F0 Forward".
Definition Loco.hxx:308
static constexpr unsigned VIRTF0_DIRECTIONAL_ENABLE
Function number of "enable directional F0".
Definition Loco.hxx:303
void set_fn(uint32_t address, uint16_t value) OVERRIDE
Sets a function to a given value.
Definition Loco.hxx:201
TrainImpl class for a DCC locomotive.
Definition Loco.hxx:493
DccTrain(DccLongAddress a)
Constructor.
Definition Loco.hxx:504
DccTrain(DccShortAddress a)
Constructor.
Definition Loco.hxx:496
void get_next_packet(unsigned code, Packet *packet) OVERRIDE
Generates next outgoing packet.
Definition Loco.cxx:81
TrainImpl structure for Marklin-Motorola v2 protocol locomotives.
Definition Loco.hxx:747
void get_next_packet(unsigned code, Packet *packet) OVERRIDE
Generates next outgoing packet.
Definition Loco.cxx:217
TrainImpl structure for Marklin-Motorola v1 protocol locomotives.
Definition Loco.hxx:639
void get_next_packet(unsigned code, Packet *packet) OVERRIDE
Generates next outgoing packet.
Definition Loco.cxx:178
Abstract class for streams of DCC packets.
This class provides a mechanism for working with velocity in different forms.
Definition Velocity.hxx:73
void set_wire(float16_t value)
Set the value based on the wire version of velocity.
Definition Velocity.hxx:295
float16_t get_wire() const
Get a wire version of the velocity.
Definition Velocity.hxx:285
#define DECLARE_CONST(name)
Declares a constant value.
Definition constants.hxx:60
TrainAddressType
Which address type this legacy train node uses.
Definition dcc/Defs.hxx:46
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
Structure defining the volatile state for a 128-speed-step DCC locomotive.
Definition Loco.hxx:524
static unsigned get_speed_steps()
Definition Loco.hxx:531
void add_dcc_speed_to_packet(dcc::Packet *p)
Adds the speed payload to a DCC packet.
Definition Loco.hxx:538
void add_dcc_estop_to_packet(dcc::Packet *p)
Adds the speed payload to a DCC packet with value == EMERGENCY_STOP.
Definition Loco.hxx:545
Structure defining the volatile state for a 28-speed-step DCC locomotive.
Definition Loco.hxx:462
static unsigned get_speed_steps()
Definition Loco.hxx:469
void add_dcc_speed_to_packet(dcc::Packet *p)
Adds the speed payload to a DCC packet.
Definition Loco.hxx:476
void add_dcc_estop_to_packet(dcc::Packet *p)
Adds the speed payload to a DCC packet with value == EMERGENCY_STOP.
Definition Loco.hxx:483
Strongly typed wrapper representing a long DCC address.
Definition Address.hxx:66
uint16_t value
Address value.
Definition Address.hxx:70
Common storage variables for the different DCC Payload types.
Definition Loco.hxx:356
uint8_t speed_
Speed step we last set.
Definition Loco.hxx:390
uint8_t f0OnForward_
1 if directional f0 is used and f0 is on for F.
Definition Loco.hxx:382
bool get_fn_store(unsigned idx)
Get a given function bit in storage.
Definition Loco.hxx:435
uint16_t lastSetSpeed_
fp16 value of the last set speed.
Definition Loco.hxx:364
uint8_t f0BlankForward_
1 if F0 should be turned off when dir==forward.
Definition Loco.hxx:386
uint8_t nextRefresh_
Which refresh packet should go out next.
Definition Loco.hxx:371
uint16_t direction_
0: forward, 1: reverse
Definition Loco.hxx:362
uint16_t address_
Track address. largest address allowed is 10239.
Definition Loco.hxx:358
void set_fn_store(unsigned idx, bool value)
Set a given function bit in storage.
Definition Loco.hxx:405
static unsigned get_fn_update_code(unsigned address)
Definition Loco.cxx:58
unsigned fn_
functions f0-f28.
Definition Loco.hxx:369
uint8_t fhi_[5]
f29-f68 state.
Definition Loco.hxx:393
uint8_t isEstop_
1 if the last speed set was estop.
Definition Loco.hxx:376
uint8_t f0BlankReverse_
1 if F0 should be turned off when dir==reverse.
Definition Loco.hxx:388
static unsigned get_max_fn()
Definition Loco.hxx:397
TrainAddressType get_address_type()
Definition Loco.hxx:453
uint8_t f0OnReverse_
1 if directional f0 is used and f0 is on for R.
Definition Loco.hxx:384
uint16_t isShortAddress_
1 if this is a short address train.
Definition Loco.hxx:360
uint8_t f0SetDirectional_
1 if the F0 function should be set/get in a directional way.
Definition Loco.hxx:380
uint8_t directionChanged_
Whether the direction change packet still needs to go out.
Definition Loco.hxx:378
Strongly typed wrapper representing a short DCC address.
Definition Address.hxx:49
uint8_t value
Address value.
Definition Address.hxx:53
Strongly typed wrapper representing a marklin-motorola protocol address.
Definition Address.hxx:82
Structure defining the volatile state for a Marklin-Motorola v2 protocol locomotive (with 28 speed st...
Definition Loco.hxx:654
unsigned nextRefresh_
internal refresh cycle state machine
Definition Loco.hxx:675
unsigned speed_
Speed step we last set.
Definition Loco.hxx:673
unsigned isEstop_
1 if the last speed set was estop.
Definition Loco.hxx:677
unsigned f0BlankReverse_
1 if F0 should be turned off when dir==reverse.
Definition Loco.hxx:688
unsigned direction_
0: forward, 1: reverse
Definition Loco.hxx:667
unsigned f0OnReverse_
1 if directional f0 is used and f0 is on for R.
Definition Loco.hxx:684
unsigned f0SetDirectional_
1 if the F0 function should be set/get in a directional way.
Definition Loco.hxx:680
bool get_fn_store(unsigned idx)
Get a given function bit in storage.
Definition Loco.hxx:719
unsigned resvd1_
reserved
Definition Loco.hxx:671
void set_fn_store(unsigned idx, bool value)
Set a given function bit in storage.
Definition Loco.hxx:705
unsigned f0OnForward_
1 if directional f0 is used and f0 is on for F.
Definition Loco.hxx:682
unsigned get_fn_update_code(unsigned address)
Definition Loco.hxx:726
unsigned get_max_fn()
Definition Loco.hxx:697
unsigned directionChanged_
Whether the direction change packet still needs to go out.
Definition Loco.hxx:669
static TrainAddressType get_address_type()
Definition Loco.hxx:739
unsigned lastSetSpeed_
fp16 value of the last set speed.
Definition Loco.hxx:663
unsigned address_
largest address allowed is 80, but we keep a few more bits around to allow for an extension to arbitr...
Definition Loco.hxx:661
unsigned fn_
function f0-f4.
Definition Loco.hxx:665
unsigned f0BlankForward_
1 if F0 should be turned off when dir==forward.
Definition Loco.hxx:686
unsigned get_speed_steps()
Definition Loco.hxx:691
Structure defining the volatile state for a Marklin-Motorola v1 protocol locomotive (with 14 speed st...
Definition Loco.hxx:557
unsigned f0OnReverse_
1 if directional f0 is used and f0 is on for R.
Definition Loco.hxx:583
unsigned f0BlankReverse_
1 if F0 should be turned off when dir==reverse.
Definition Loco.hxx:587
unsigned address_
largest address allowed is 80, but we keep a few more bits around to allow for an extension to arbitr...
Definition Loco.hxx:564
unsigned directionChanged_
Whether the direction change packet still needs to go out.
Definition Loco.hxx:572
void set_fn_store(unsigned idx, bool value)
Set a given function bit in storage.
Definition Loco.hxx:604
static TrainAddressType get_address_type()
Definition Loco.hxx:631
unsigned f0OnForward_
1 if directional f0 is used and f0 is on for F.
Definition Loco.hxx:581
unsigned isEstop_
1 if the last speed set was estop.
Definition Loco.hxx:576
unsigned get_max_fn()
Definition Loco.hxx:596
unsigned speed_
Speed step we last set.
Definition Loco.hxx:574
unsigned f0SetDirectional_
1 if the F0 function should be set/get in a directional way.
Definition Loco.hxx:579
unsigned direction_
0: forward, 1: reverse
Definition Loco.hxx:570
unsigned get_fn_update_code(unsigned address)
Definition Loco.hxx:625
unsigned f0BlankForward_
1 if F0 should be turned off when dir==forward.
Definition Loco.hxx:585
unsigned get_speed_steps()
Definition Loco.hxx:590
unsigned lastSetSpeed_
fp16 value of the last set speed.
Definition Loco.hxx:566
bool get_fn_store(unsigned idx)
Get a given function bit in storage.
Definition Loco.hxx:618
unsigned fn_
function f0.
Definition Loco.hxx:568
Represents a command to be sent to the track driver.
Definition Packet.hxx:52
void add_dcc_speed28(bool is_fwd, unsigned speed)
Adds a speed-and-direction command (dcc baseline command) to the packet.
Definition Packet.cxx:150
static const unsigned EMERGENCY_STOP
Send this speed step to emergency-stop the locomotive.
Definition Packet.hxx:56
void add_dcc_speed128(bool is_fwd, unsigned speed)
Adds a speed-and-direction command (dcc extended command) for 128 speed steps to the packet.
Definition Packet.cxx:174