Skip to content

Commit

Permalink
Adds directional F0 functions to the DCC locomotives. (#459)
Browse files Browse the repository at this point in the history
* 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.

* Fix comment
  • Loading branch information
balazsracz authored Nov 1, 2020
1 parent b58eec9 commit 7635ce2
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/dcc/Defs.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#ifndef _DCC_DEFS_HXX_
#define _DCC_DEFS_HXX_

#include <stdint.h>

namespace dcc {

/// Which address type this legacy train node uses. These address types
Expand Down
7 changes: 4 additions & 3 deletions src/dcc/Loco.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ void DccTrain<Payload>::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:
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down
167 changes: 166 additions & 1 deletion src/dcc/Loco.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
{

Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand Down
37 changes: 37 additions & 0 deletions src/dcc/dcc_constants.cxx
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 7635ce2

Please sign in to comment.