From 85585f3bc1aa93ba7dbfca1b3f977e92653108f1 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sat, 31 Oct 2020 18:51:23 +0100 Subject: [PATCH 1/2] Adds directional F0 functions to the DCC locomotives. These functions manipulate what is sent as F0 to the locomotive depending on the current direction: - directional F0 means that we store two bits for lights on forward and reverse. We send out always whichever direction is the loco facing. The user can set and get F0 state using the usual button 0. - Blank F0 forward will turn off F0 bit in the packet even if function 0 is on whenever the speed is facing forward. This does not change the reported f0 state when get_fn is invoked. - Blank F0 reverse is the dual thereof. By default these are mapped to F100-F102 in the OpenLCB function space. The mapping can be overridden by the application. --- src/dcc/Defs.hxx | 2 + src/dcc/Loco.cxx | 7 +- src/dcc/Loco.hxx | 167 +++++++++++++++++++++++++++++++++++++- src/dcc/dcc_constants.cxx | 37 +++++++++ 4 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 src/dcc/dcc_constants.cxx 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..10b4ee660 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 tihs function number there will be five 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); From 4d9bac66cfb05c1a5d1d336a6202f5050c1cfb1d Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sat, 31 Oct 2020 19:01:31 +0100 Subject: [PATCH 2/2] Fix comment --- src/dcc/Loco.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dcc/Loco.hxx b/src/dcc/Loco.hxx index 10b4ee660..07b18a0bf 100644 --- a/src/dcc/Loco.hxx +++ b/src/dcc/Loco.hxx @@ -42,7 +42,7 @@ #include "utils/constants.hxx" #include "utils/logging.h" -/// At tihs function number there will be five virtual functions available on +/// 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);