001package jmri.jmrix.loconet;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * A specialization of the LocoNet Throttle for Intellibox-I foibles.
008 *
009 * @author Bob Jacobsen Copyright (C) 2014
010 */
011public class Ib1Throttle extends LocoNetThrottle {
012
013    /**
014     * Constructor.
015     *
016     * @param memo system connection.
017     * @param slot The LocoNetSlot this throttle will talk on.
018     */
019    public Ib1Throttle(LocoNetSystemConnectionMemo memo, LocoNetSlot slot) {
020        super(memo, slot);
021        log.debug("Ib1Throttle created");
022    }
023
024    /**
025     * Convert a LocoNet speed integer to a float speed value
026     *
027     * @param lSpeed LocoNet style speed value
028     * @return floatSpeed as float 0.0-1.0, or -1.0 to indicate E-Stop
029     * The IB provides an integer 0-127
030     */
031     @Override
032     protected float floatSpeed(int lSpeed) {
033        log.debug("IB1 floatSpeed {}", lSpeed);
034        if (lSpeed == 0) {
035            return 0.f;    // stop
036        } else if (lSpeed == 1) {
037            return -1.f;   // estop
038        }
039        switch (this.getSpeedStepMode()) {
040            case NMRA_DCC_28:
041            case MOTOROLA_28:
042                /*
043                 * 28 speeds are not commensurate with the 126 speeds the IB1 puts on Loconet
044                 * Loconet speeds are 2-6, 7-10, 11-15. 16-19 ... 124-127
045                 * There are 5 Loconet speeds in first speed, 4 in the second,
046                 * 5 in the third, 4 in the fourth, etc.
047                 */
048                lSpeed -= 1;   // skip estop
049                int cycle = (lSpeed-1) / 9;
050                int ispeed = (cycle*2) + 1;  // ispeed contains the speed in 1-28 steps
051                if ((lSpeed - (cycle * 9)) > 5) {
052                    ispeed++;  // add 1 if even step
053                }
054                return ispeed / 28.f;
055           case NMRA_DCC_14:
056                /*
057                 * The implementation for 14 speed should be simple, but the IB1 did not
058                 * implement equally spaced steps
059                 */
060                cycle = lSpeed / 19;
061                ispeed = (cycle*2) + 1;
062                if ((lSpeed - (cycle * 19)) > 9) {
063                    ispeed++;
064                }
065                return ispeed / 14.f;  // 9/126 <= speed <= 1 (126 is divisible by 14)
066            case NMRA_DCC_128:
067                return (lSpeed-1) / 126.f;  // 1/126 <= speed <= 1
068            default:
069                log.warn("Unhandled speed step: {}", this.getSpeedStepMode());
070                break;
071        }
072        return 0.f;
073    }
074
075    /**
076     * Computes the integer speed value from a float.
077     * @param speed is the floating-point speed value to be converted
078     * @return intSpeed an integer which represents the speed step value
079     */
080    @Override
081    protected int intSpeed(float speed) {
082        log.debug("IB1 intSpeed {}", speed);
083
084        int lSpeed = jmri.jmrix.AbstractThrottle.intSpeed(speed,127);
085        switch (this.getSpeedStepMode()) {
086            case NMRA_DCC_14:
087                if (lSpeed > 2) {
088                    lSpeed--;   // speed from JMRI throttle 1 higher than IB1
089                }
090                break;
091            case NMRA_DCC_28:
092            case MOTOROLA_28:
093            case NMRA_DCC_128:
094                /*
095                 * The IB1 works appropriately for these speed step modes
096                 */
097                break;
098            default:
099                log.warn("Unhandled speed step: {}", this.getSpeedStepMode());
100                break;
101        }
102        // log.debug("loconet speed is {}", lSpeed);
103        return lSpeed;
104    }
105
106    @Override
107    protected void sendFunctionGroup3() {
108        // Special LocoNet messages for Uhlenbrock Intellibox-I version 2.x implementation
109        // Intellibox-II uses another implementation for these functions
110        // Functions F9 to F11
111        log.debug("IB1 sendFunctionGroup3");
112        int new_IB1_F9_F11 = ((getFunction(11) ? LnConstants.RE_IB1_F11_MASK : 0)
113                | (getFunction(10) ? LnConstants.RE_IB1_F10_MASK : 0)
114                | (getFunction(9) ? LnConstants.RE_IB1_F9_MASK : 0));
115        LocoNetMessage msg1 = new LocoNetMessage(6);
116        msg1.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
117        msg1.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
118        msg1.setElement(2, slot.getSlot());
119        msg1.setElement(3, LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN);
120        msg1.setElement(4, new_IB1_F9_F11);
121        network.sendLocoNetMessage(msg1);
122
123        // Function F12 (and F20 and F28)
124        int new_IB2_F20_F28 = ((getFunction(12) ? LnConstants.RE_IB2_SPECIAL_F12_MASK : 0)
125                | (getFunction(20) ? LnConstants.RE_IB2_SPECIAL_F20_MASK : 0)
126                | (getFunction(28) ? LnConstants.RE_IB2_SPECIAL_F28_MASK : 0));
127        LocoNetMessage msg2 = new LocoNetMessage(6);
128        msg2.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
129        msg2.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
130        msg2.setElement(2, slot.getSlot());
131        msg2.setElement(3, LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN);
132        msg2.setElement(4, new_IB2_F20_F28);
133        network.sendLocoNetMessage(msg2);
134    }
135
136    @Override
137    protected void sendFunctionGroup4() {
138        // Special LocoNet message for Uhlenbrock (IB-I and IB-II) implementation
139        // Functions F13 to F19
140        log.debug("IB1 sendFunctionGroup4");
141        int new_IB2_F13_F19 = ((getFunction(19) ? LnConstants.RE_IB2_F19_MASK : 0)
142                | (getFunction(18) ? LnConstants.RE_IB2_F18_MASK : 0)
143                | (getFunction(17) ? LnConstants.RE_IB2_F17_MASK : 0)
144                | (getFunction(16) ? LnConstants.RE_IB2_F16_MASK : 0)
145                | (getFunction(15) ? LnConstants.RE_IB2_F15_MASK : 0)
146                | (getFunction(14) ? LnConstants.RE_IB2_F14_MASK : 0)
147                | (getFunction(13) ? LnConstants.RE_IB2_F13_MASK : 0));
148        LocoNetMessage msg = new LocoNetMessage(6);
149        msg.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
150        msg.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
151        msg.setElement(2, slot.getSlot());
152        msg.setElement(3, LnConstants.RE_IB2_SPECIAL_F13_F19_TOKEN);
153        msg.setElement(4, new_IB2_F13_F19);
154        network.sendLocoNetMessage(msg);
155
156        // Function F20 (and F28)
157        // F12 is also controlled from this message though IB-II uses RE_OPC_IB2_F9_F12 OPS code for F12 - needed to avoid overridding F12 value
158        int new_IB2_F20_F28 = ((getFunction(12) ? LnConstants.RE_IB2_SPECIAL_F12_MASK : 0)
159                | (getFunction(20) ? LnConstants.RE_IB2_SPECIAL_F20_MASK : 0)
160                | (getFunction(28) ? LnConstants.RE_IB2_SPECIAL_F28_MASK : 0));
161        LocoNetMessage msg2 = new LocoNetMessage(6);
162        msg2.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
163        msg2.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
164        msg2.setElement(2, slot.getSlot());
165        msg2.setElement(3, LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN);
166        msg2.setElement(4, new_IB2_F20_F28);
167        network.sendLocoNetMessage(msg2);
168    }
169
170    @Override
171    protected void sendFunctionGroup5() {
172        // Special LocoNet message for Uhlenbrock (IB-I and IB-II) implementation
173        // Functions F21 to F27
174        log.debug("IB1 sendFunctionGroup5");
175        int new_IB2_F21_F27 = ((getFunction(27) ? LnConstants.RE_IB2_F27_MASK : 0)
176                | (getFunction(26) ? LnConstants.RE_IB2_F26_MASK : 0)
177                | (getFunction(25) ? LnConstants.RE_IB2_F25_MASK : 0)
178                | (getFunction(24) ? LnConstants.RE_IB2_F24_MASK : 0)
179                | (getFunction(23) ? LnConstants.RE_IB2_F23_MASK : 0)
180                | (getFunction(22) ? LnConstants.RE_IB2_F22_MASK : 0)
181                | (getFunction(21) ? LnConstants.RE_IB2_F21_MASK : 0));
182        LocoNetMessage msg = new LocoNetMessage(6);
183        msg.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
184        msg.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
185        msg.setElement(2, slot.getSlot());
186        msg.setElement(3, LnConstants.RE_IB2_SPECIAL_F21_F27_TOKEN);
187        msg.setElement(4, new_IB2_F21_F27);
188        network.sendLocoNetMessage(msg);
189
190        // Function F28 (and F20)
191        // F12 is also controlled from this message though IB-II uses RE_OPC_IB2_F9_F12 OPS code for F12 - needed to avoid overridding F12 value
192        int new_IB2_F20_F28 = ((getFunction(12) ? LnConstants.RE_IB2_SPECIAL_F12_MASK : 0)
193                | (getFunction(20) ? LnConstants.RE_IB2_SPECIAL_F20_MASK : 0)
194                | (getFunction(28) ? LnConstants.RE_IB2_SPECIAL_F28_MASK : 0));
195        LocoNetMessage msg2 = new LocoNetMessage(6);
196        msg2.setOpCode(LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL);
197        msg2.setElement(1, LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN);
198        msg2.setElement(2, slot.getSlot());
199        msg2.setElement(3, LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN);
200        msg2.setElement(4, new_IB2_F20_F28);
201        network.sendLocoNetMessage(msg2);
202    }
203
204    // initialize logging
205    private final static Logger log = LoggerFactory.getLogger(Ib1Throttle.class);
206
207}