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}