diff --git a/src/dcc/Defs.hxx b/src/dcc/Defs.hxx index 375c1b70b..f7ddc1754 100644 --- a/src/dcc/Defs.hxx +++ b/src/dcc/Defs.hxx @@ -35,6 +35,8 @@ #ifndef _DCC_DEFS_HXX_ #define _DCC_DEFS_HXX_ +#include + namespace dcc { /// Which address type this legacy train node uses. These address types diff --git a/src/dcc/Loco.cxx b/src/dcc/Loco.cxx index cad1533b7..78462a1b1 100644 --- a/src/dcc/Loco.cxx +++ b/src/dcc/Loco.cxx @@ -110,7 +110,8 @@ void DccTrain::get_next_packet(unsigned code, Packet *packet) { case FUNCTION0: { - packet->add_dcc_function0_4(this->p.fn_ & 0x1F); + packet->add_dcc_function0_4( + (this->p.fn_ & 0x1E) | this->get_effective_f0()); return; } case FUNCTION5: @@ -171,7 +172,7 @@ MMOldTrain::~MMOldTrain() void MMOldTrain::get_next_packet(unsigned code, Packet *packet) { packet->start_mm_packet(); - packet->add_mm_address(MMAddress(p.address_), p.fn_ & 1); + packet->add_mm_address(MMAddress(p.address_), get_effective_f0()); if (code == ESTOP) { @@ -210,7 +211,7 @@ MMNewTrain::~MMNewTrain() void MMNewTrain::get_next_packet(unsigned code, Packet *packet) { packet->start_mm_packet(); - packet->add_mm_address(MMAddress(p.address_), p.fn_ & 1); + packet->add_mm_address(MMAddress(p.address_), get_effective_f0()); if (code == REFRESH) { diff --git a/src/dcc/Loco.hxx b/src/dcc/Loco.hxx index 425f13329..07b18a0bf 100644 --- a/src/dcc/Loco.hxx +++ b/src/dcc/Loco.hxx @@ -35,12 +35,18 @@ #ifndef _DCC_LOCO_HXX_ #define _DCC_LOCO_HXX_ +#include "dcc/Defs.hxx" #include "dcc/Packet.hxx" #include "dcc/PacketSource.hxx" #include "dcc/UpdateLoop.hxx" -#include "dcc/Defs.hxx" +#include "utils/constants.hxx" #include "utils/logging.h" +/// At this function number there will be three virtual functions available on +/// the OpenLCB TrainImpl, controlling advanced functions related to the light +/// (f0) function of trains. +DECLARE_CONST(dcc_virtual_f0_offset); + namespace dcc { @@ -111,10 +117,12 @@ public: return; } p.lastSetSpeed_ = new_speed; + unsigned previous_light = get_effective_f0(); if (speed.direction() != p.direction_) { p.directionChanged_ = 1; p.direction_ = speed.direction(); + update_f0_direction_changed(); } float f_speed = speed.mph(); if (f_speed > 0) @@ -131,7 +139,18 @@ public: { p.speed_ = 0; } + unsigned light = get_effective_f0(); + if (previous_light && !light) + { + // Turns off light first then sends speed packet. + packet_processor_notify_update(this, p.get_fn_update_code(0)); + } packet_processor_notify_update(this, SPEED); + if (light && !previous_light) + { + // Turns on light after sending speed packets. + packet_processor_notify_update(this, p.get_fn_update_code(0)); + } } /// @return the last set speed. @@ -169,6 +188,59 @@ public: /// (0..28), @param value is 0 for funciton OFF, 1 for function ON. void set_fn(uint32_t address, uint16_t value) OVERRIDE { + const uint32_t virtf0 = config_dcc_virtual_f0_offset(); + if (address == 0 && p.f0SetDirectional_) + { + if (p.direction_ == 0) + { + p.f0OnForward_ = value ? 1 : 0; + } + else + { + p.f0OnReverse_ = value ? 1 : 0; + } + // continue into the handling of f0. + } + else if (address == virtf0 + VIRTF0_DIRECTIONAL_ENABLE) + { + if (value) + { + p.f0SetDirectional_ = 1; + // Populates new state of separate f0 forward and f0 + // reverse. + if (p.direction_ == 0) + { + p.f0OnForward_ = p.fn_ & 1; + p.f0OnReverse_ = 0; + } + else + { + p.f0OnReverse_ = p.fn_ & 1; + p.f0OnForward_ = 0; + } + } + else + { + p.f0SetDirectional_ = 0; + // whatever value we have in fn_[0] now is going to be the + // new state, so we don't change anything. + } + // This command never changes f0, so no packets need to be sent to + // the track. + return; + } + else if (address == virtf0 + VIRTF0_BLANK_FWD) + { + p.f0BlankForward_ = value ? 1 : 0; + packet_processor_notify_update(this, p.get_fn_update_code(0)); + return; + } + else if (address == virtf0 + VIRTF0_BLANK_REV) + { + p.f0BlankReverse_ = value ? 1 : 0; + packet_processor_notify_update(this, p.get_fn_update_code(0)); + return; + } if (address > p.get_max_fn()) { // Ignore. @@ -208,6 +280,56 @@ public: } protected: + /// Function number of "enable directional F0". Offset from config option + /// dcc_virtual_f0_offset. When this function is enabled, F0 is set and + /// cleared separately for forward and reverse drive. + static constexpr unsigned VIRTF0_DIRECTIONAL_ENABLE = 0; + /// Function number of "Blank F0 Forward". Offset from config option + /// dcc_virtual_f0_offset. When this function is enabled, F0 on the track + /// packet will turn off when direction==forward, even if function 0 is + /// set. + static constexpr unsigned VIRTF0_BLANK_FWD = 1; + /// Function number of "Blank F0 Reverse". Offset from config option + /// dcc_virtual_f0_offset. When this function is enabled, F0 on the track + /// packet will turn off when direction==reverse, even if function 0 is + /// set. + static constexpr unsigned VIRTF0_BLANK_REV = 2; + + /// @return the currently applicable value of F0 to be sent out to the + /// packets (1 if on, 0 if off). + unsigned get_effective_f0() + { + unsigned is_on = p.f0SetDirectional_ == 0 ? (p.fn_ & 1) + : p.direction_ == 0 ? p.f0OnForward_ + : p.f0OnReverse_; + if (p.direction_ == 0 && p.f0BlankForward_) + { + is_on = 0; + } + if (p.direction_ == 1 && p.f0BlankReverse_) + { + is_on = 0; + } + return is_on; + } + + /// Updates the f0 states after a direction change occurred. + void update_f0_direction_changed() + { + if (p.f0SetDirectional_) + { + p.fn_ &= ~1; + if (p.direction_ == 0 && p.f0OnForward_) + { + p.fn_ |= 1; + } + if (p.direction_ == 1 && p.f0OnReverse_) + { + p.fn_ |= 1; + } + } + } + /// Payload -- actual data we know about the train. P p; }; @@ -235,6 +357,16 @@ struct Dcc28Payload unsigned speed_ : 5; /// Whether the direction change packet still needs to go out. unsigned directionChanged_ : 1; + /// 1 if the F0 function should be set/get in a directional way. + unsigned f0SetDirectional_ : 1; + /// 1 if directional f0 is used and f0 is on for F. + unsigned f0OnForward_ : 1; + /// 1 if directional f0 is used and f0 is on for R. + unsigned f0OnReverse_ : 1; + /// 1 if F0 should be turned off when dir==forward. + unsigned f0BlankForward_ : 1; + /// 1 if F0 should be turned off when dir==reverse. + unsigned f0BlankReverse_ : 1; /** @return the number of speed steps (in float). */ static unsigned get_speed_steps() @@ -328,6 +460,17 @@ struct Dcc128Payload /// Whether the direction change packet still needs to go out. unsigned directionChanged_ : 1; + /// 1 if the F0 function should be set/get in a directional way. + unsigned f0SetDirectional_ : 1; + /// 1 if directional f0 is used and f0 is on for F. + unsigned f0OnForward_ : 1; + /// 1 if directional f0 is used and f0 is on for R. + unsigned f0OnReverse_ : 1; + /// 1 if F0 should be turned off when dir==forward. + unsigned f0BlankForward_ : 1; + /// 1 if F0 should be turned off when dir==reverse. + unsigned f0BlankReverse_ : 1; + /** @return the number of speed steps (the largest valid speed step). */ static unsigned get_speed_steps() { @@ -393,6 +536,17 @@ struct MMOldPayload /// Speed step we last set. unsigned speed_ : 4; + /// 1 if the F0 function should be set/get in a directional way. + unsigned f0SetDirectional_ : 1; + /// 1 if directional f0 is used and f0 is on for F. + unsigned f0OnForward_ : 1; + /// 1 if directional f0 is used and f0 is on for R. + unsigned f0OnReverse_ : 1; + /// 1 if F0 should be turned off when dir==forward. + unsigned f0BlankForward_ : 1; + /// 1 if F0 should be turned off when dir==reverse. + unsigned f0BlankReverse_ : 1; + /** @return the number of speed steps (in float). */ unsigned get_speed_steps() { @@ -459,6 +613,17 @@ struct MMNewPayload /// internal refresh cycle state machine unsigned nextRefresh_ : 3; + /// 1 if the F0 function should be set/get in a directional way. + unsigned f0SetDirectional_ : 1; + /// 1 if directional f0 is used and f0 is on for F. + unsigned f0OnForward_ : 1; + /// 1 if directional f0 is used and f0 is on for R. + unsigned f0OnReverse_ : 1; + /// 1 if F0 should be turned off when dir==forward. + unsigned f0BlankForward_ : 1; + /// 1 if F0 should be turned off when dir==reverse. + unsigned f0BlankReverse_ : 1; + /** @return the number of speed steps (in float). */ unsigned get_speed_steps() { diff --git a/src/dcc/dcc_constants.cxx b/src/dcc/dcc_constants.cxx new file mode 100644 index 000000000..e4070dd28 --- /dev/null +++ b/src/dcc/dcc_constants.cxx @@ -0,0 +1,37 @@ +/** \copyright + * Copyright (c) 2014, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file dcc_constants.cxx + * + * Default values of constants from the dcc package. + * + * @author Balazs Racz + * @date 10 May 2014 + */ + +#include "utils/constants.hxx" + +DEFAULT_CONST(dcc_virtual_f0_offset, 100);