Skip to content

Commit 367ac63

Browse files
committed
Merge pull request #66 from rhauch/issue-65
Issue 65 - Added support for Victor SP hardware motor controllers
2 parents 34a7b44 + 3855bd7 commit 367ac63

File tree

4 files changed

+213
-13
lines changed

4 files changed

+213
-13
lines changed

strongback-examples/src/org/strongback/example/simple/SimpleTankDriveRobot.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.strongback.components.ui.FlightStick;
2323
import org.strongback.drive.TankDrive;
2424
import org.strongback.hardware.Hardware;
25+
import org.strongback.util.Values;
2526

2627
import edu.wpi.first.wpilibj.IterativeRobot;
2728

@@ -59,7 +60,7 @@ public void robotInit() {
5960
// "sensitivity" input to scale the drive speed and throttle, so we'll map it from it's native [-1,1] to a simple scale
6061
// factor of [0,1] ...
6162
FlightStick joystick = Hardware.HumanInterfaceDevices.logitechAttack3D(JOYSTICK_PORT);
62-
ContinuousRange sensitivity = joystick.getThrottle().map(t -> (t + 1.0) / 2.0);
63+
ContinuousRange sensitivity = joystick.getThrottle().map(Values.mapRange(-1.0,1.0).toRange(0.0, 1.0));
6364
driveSpeed = joystick.getPitch().scale(sensitivity::read); // scaled
6465
turnSpeed = joystick.getRoll().scale(sensitivity::read).invert(); // scaled and inverted
6566

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Strongback
3+
* Copyright 2015, Strongback and individual contributors by the @authors tag.
4+
* See the COPYRIGHT.txt in the distribution for a full listing of individual
5+
* contributors.
6+
*
7+
* Licensed under the MIT License; you may not use this file except in
8+
* compliance with the License. You may obtain a copy of the License at
9+
* http://opensource.org/licenses/MIT
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.strongback.util;
18+
19+
import static org.fest.assertions.Assertions.assertThat;
20+
21+
import org.fest.assertions.Delta;
22+
import org.junit.Test;
23+
import org.strongback.function.DoubleToDoubleFunction;
24+
25+
public class ValuesTest {
26+
27+
private static final Delta TOLERANCE = Delta.delta(0.00001);
28+
29+
@Test
30+
public void shouldMapRangeFromZeroCenteredToPositiveOne() {
31+
DoubleToDoubleFunction func = Values.mapRange(-1.0,1.0,0.0,1.0);
32+
33+
// Verify minimum range is properly limited ...
34+
assertThat(func.applyAsDouble(-1.0)).isEqualTo(0.0, TOLERANCE);
35+
assertThat(func.applyAsDouble(-1.01)).isEqualTo(0.0, TOLERANCE);
36+
assertThat(func.applyAsDouble(-2.0)).isEqualTo(0.0, TOLERANCE);
37+
38+
// Verify maximum range is properly limited ...
39+
assertThat(func.applyAsDouble(1.0)).isEqualTo(1.0, TOLERANCE);
40+
assertThat(func.applyAsDouble(1.01)).isEqualTo(1.0, TOLERANCE);
41+
assertThat(func.applyAsDouble(2.0)).isEqualTo(1.0, TOLERANCE);
42+
43+
// Verify mid-range
44+
assertThat(func.applyAsDouble(0.0)).isEqualTo(0.5, TOLERANCE);
45+
46+
// Verify other values within the range ...
47+
assertThat(func.applyAsDouble(-0.75)).isEqualTo(0.125, TOLERANCE);
48+
assertThat(func.applyAsDouble(-0.5)).isEqualTo(0.25, TOLERANCE);
49+
assertThat(func.applyAsDouble(-0.25)).isEqualTo(0.375, TOLERANCE);
50+
assertThat(func.applyAsDouble(0.25)).isEqualTo(0.625, TOLERANCE);
51+
assertThat(func.applyAsDouble(0.5)).isEqualTo(0.75, TOLERANCE);
52+
assertThat(func.applyAsDouble(0.75)).isEqualTo(0.875, TOLERANCE);
53+
}
54+
55+
@Test
56+
public void shouldMapRangeUsingTranslationOnly() {
57+
DoubleToDoubleFunction func = Values.mapRange(0.0,4.0,10.0,14.0);
58+
59+
// Verify minimum range is properly limited ...
60+
assertThat(func.applyAsDouble(0.0)).isEqualTo(10.0, TOLERANCE);
61+
assertThat(func.applyAsDouble(-0.01)).isEqualTo(10.0, TOLERANCE);
62+
assertThat(func.applyAsDouble(-2.0)).isEqualTo(10.0, TOLERANCE);
63+
64+
// Verify maximum range is properly limited ...
65+
assertThat(func.applyAsDouble(4.0)).isEqualTo(14.0, TOLERANCE);
66+
assertThat(func.applyAsDouble(4.01)).isEqualTo(14.0, TOLERANCE);
67+
assertThat(func.applyAsDouble(6.0)).isEqualTo(14.0, TOLERANCE);
68+
69+
// Verify mid-range
70+
assertThat(func.applyAsDouble(2.0)).isEqualTo(12.0, TOLERANCE);
71+
72+
// Verify other values within the range ...
73+
assertThat(func.applyAsDouble(0.5)).isEqualTo(10.5, TOLERANCE);
74+
assertThat(func.applyAsDouble(1.0)).isEqualTo(11.0, TOLERANCE);
75+
assertThat(func.applyAsDouble(1.5)).isEqualTo(11.5, TOLERANCE);
76+
assertThat(func.applyAsDouble(2.0)).isEqualTo(12.0, TOLERANCE);
77+
assertThat(func.applyAsDouble(2.5)).isEqualTo(12.5, TOLERANCE);
78+
assertThat(func.applyAsDouble(3.0)).isEqualTo(13.0, TOLERANCE);
79+
assertThat(func.applyAsDouble(3.5)).isEqualTo(13.5, TOLERANCE);
80+
assertThat(func.applyAsDouble(4.0)).isEqualTo(14.0, TOLERANCE);
81+
}
82+
83+
@Test
84+
public void shouldMapRangeUsingScaleOnly() {
85+
DoubleToDoubleFunction func = Values.mapRange(1.0,5.0).toRange(1.0,2.0);
86+
87+
// Verify minimum range is properly limited ...
88+
assertThat(func.applyAsDouble(0.0)).isEqualTo(1.0, TOLERANCE);
89+
assertThat(func.applyAsDouble(-0.01)).isEqualTo(1.0, TOLERANCE);
90+
assertThat(func.applyAsDouble(-2.0)).isEqualTo(1.0, TOLERANCE);
91+
92+
// Verify maximum range is properly limited ...
93+
assertThat(func.applyAsDouble(5.0)).isEqualTo(2.0, TOLERANCE);
94+
assertThat(func.applyAsDouble(5.01)).isEqualTo(2.0, TOLERANCE);
95+
assertThat(func.applyAsDouble(5.0)).isEqualTo(2.0, TOLERANCE);
96+
97+
// Verify mid-range
98+
assertThat(func.applyAsDouble(3.0)).isEqualTo(1.5, TOLERANCE);
99+
100+
// Verify other values within the range ...
101+
assertThat(func.applyAsDouble(1.04)).isEqualTo(1.01, TOLERANCE);
102+
assertThat(func.applyAsDouble(1.8)).isEqualTo(1.2, TOLERANCE);
103+
assertThat(func.applyAsDouble(2.0)).isEqualTo(1.25, TOLERANCE);
104+
assertThat(func.applyAsDouble(2.6)).isEqualTo(1.4, TOLERANCE);
105+
assertThat(func.applyAsDouble(3.4)).isEqualTo(1.6, TOLERANCE);
106+
assertThat(func.applyAsDouble(3.8)).isEqualTo(1.7, TOLERANCE);
107+
assertThat(func.applyAsDouble(4.2)).isEqualTo(1.8, TOLERANCE);
108+
assertThat(func.applyAsDouble(4.6)).isEqualTo(1.9, TOLERANCE);
109+
}
110+
111+
}

strongback/src/org/strongback/hardware/Hardware.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import edu.wpi.first.wpilibj.Talon;
6161
import edu.wpi.first.wpilibj.Ultrasonic;
6262
import edu.wpi.first.wpilibj.Victor;
63+
import edu.wpi.first.wpilibj.VictorSP;
6364
import edu.wpi.first.wpilibj.interfaces.Accelerometer.Range;
6465
import edu.wpi.first.wpilibj.interfaces.Gyro;
6566

@@ -545,6 +546,29 @@ public static Motor victor(int channel, DoubleToDoubleFunction speedLimiter) {
545546
return new HardwareMotor(new Victor(channel), SPEED_LIMITER);
546547
}
547548

549+
/**
550+
* Create a motor driven by a VEX Robotics Victor SP speed controller on the specified channel. The speed output is
551+
* limited to [-1.0,1.0] inclusive.
552+
*
553+
* @param channel the channel the controller is connected to
554+
* @return a motor on the specified channel
555+
*/
556+
public static Motor victorSP(int channel) {
557+
return victorSP(channel, SPEED_LIMITER);
558+
}
559+
560+
/**
561+
* Create a motor driven by a VEX Robotics Victor SP speed controller on the specified channel, with a custom speed
562+
* limiting function.
563+
*
564+
* @param channel the channel the controller is connected to
565+
* @param speedLimiter function that will be used to limit the speed (input voltage); may not be null
566+
* @return a motor on the specified channel
567+
*/
568+
public static Motor victorSP(int channel, DoubleToDoubleFunction speedLimiter) {
569+
return new HardwareMotor(new VictorSP(channel), SPEED_LIMITER);
570+
}
571+
548572
/**
549573
* Creates a {@link TalonSRX} motor controlled by a Talon SRX with no sensors wired as inputs.
550574
* <p>

strongback/src/org/strongback/util/Values.java

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import org.strongback.function.DoubleToDoubleFunction;
1919

20-
2120
/**
2221
* Utility class for working with values.
2322
*
@@ -75,10 +74,12 @@ private static double calcTolerance(int bits) {
7574
* @throws IllegalArgumentException if the tolerance is negative
7675
*/
7776
public static int fuzzyCompare(double a, double b, double tolerance) {
78-
if ( tolerance < 0.0 ) throw new IllegalArgumentException("The tolerance may not be negative");
77+
if (tolerance < 0.0) throw new IllegalArgumentException("The tolerance may not be negative");
7978
double difference = a - b;
80-
return (Math.abs(difference) <= tolerance ? 0 : // the two values are within the tolerance of each other
81-
(difference > 0 ? 1 : // the first is greater than the second
79+
return (Math.abs(difference) <= tolerance ? 0
80+
: // the two values are within the tolerance of each other
81+
(difference > 0 ? 1
82+
: // the first is greater than the second
8283
-1)); // the first is less than the second
8384
}
8485

@@ -92,7 +93,8 @@ public static int fuzzyCompare(double a, double b, double tolerance) {
9293
* @throws IllegalArgumentException if the minimum value is greater than the maximum value
9394
*/
9495
public static double limit(double minimum, double num, double maximum) {
95-
if ( maximum < minimum ) throw new IllegalArgumentException("The minimum value must be less than or equal to the maximum value");
96+
if (maximum < minimum) throw new IllegalArgumentException(
97+
"The minimum value must be less than or equal to the maximum value");
9698
if (num > maximum) {
9799
return maximum;
98100
}
@@ -112,7 +114,8 @@ public static double limit(double minimum, double num, double maximum) {
112114
* @throws IllegalArgumentException if the minimum value is greater than the maximum value
113115
*/
114116
public static DoubleToDoubleFunction limiter(double minimum, double maximum) {
115-
if ( maximum < minimum ) throw new IllegalArgumentException("The minimum value must be less than or equal to the maximum value");
117+
if (maximum < minimum) throw new IllegalArgumentException(
118+
"The minimum value must be less than or equal to the maximum value");
116119
return new DoubleToDoubleFunction() {
117120
@Override
118121
public double applyAsDouble(double value) {
@@ -138,9 +141,10 @@ public double applyAsDouble(double value) {
138141
* @throws IllegalArgumentException if the minimum and maximum values are invalid
139142
*/
140143
public static double symmetricLimit(double minimum, double num, double maximum) {
141-
if ( minimum < 0 ) throw new IllegalArgumentException("The minimum value may not be negative");
142-
if ( maximum < 0 ) throw new IllegalArgumentException("The maximum value may not be negative");
143-
if ( maximum < minimum ) throw new IllegalArgumentException("The minimum value must be less than or equal to the maximum value");
144+
if (minimum < 0) throw new IllegalArgumentException("The minimum value may not be negative");
145+
if (maximum < 0) throw new IllegalArgumentException("The maximum value may not be negative");
146+
if (maximum < minimum) throw new IllegalArgumentException(
147+
"The minimum value must be less than or equal to the maximum value");
144148
if (num > maximum) {
145149
return maximum;
146150
}
@@ -160,9 +164,10 @@ public static double symmetricLimit(double minimum, double num, double maximum)
160164
* @return the function that limits to the maximum and minimum values; never null
161165
*/
162166
public static DoubleToDoubleFunction symmetricLimiter(double minimum, double maximum) {
163-
if ( minimum < 0 ) throw new IllegalArgumentException("The minimum value may not be negative");
164-
if ( maximum < 0 ) throw new IllegalArgumentException("The maximum value may not be negative");
165-
if ( maximum < minimum ) throw new IllegalArgumentException("The minimum value must be less than or equal to the maximum value");
167+
if (minimum < 0) throw new IllegalArgumentException("The minimum value may not be negative");
168+
if (maximum < 0) throw new IllegalArgumentException("The maximum value may not be negative");
169+
if (maximum < minimum) throw new IllegalArgumentException(
170+
"The minimum value must be less than or equal to the maximum value");
166171
return new DoubleToDoubleFunction() {
167172
@Override
168173
public double applyAsDouble(double num) {
@@ -178,4 +183,63 @@ public double applyAsDouble(double num) {
178183
};
179184
}
180185

186+
public static interface RangeMaker {
187+
DoubleToDoubleFunction toRange(double minOutputValue, double maxOutputValue);
188+
}
189+
190+
/**
191+
* Begin to create a function that maps from the specified range of input values. To obtain a mapping function, call the
192+
* {@link RangeMaker#toRange(double, double)} method on the resulting {@link RangeMaker} instance.
193+
* <p>
194+
* This is equivalent to calling {@link #mapRange(double, double, double, double)} with the input and output range limits.
195+
* For example:
196+
*
197+
* <pre>
198+
* Values.mapRange(-1.0,1.0).toRange(0.0,1.0);
199+
* </pre>
200+
*
201+
* is equivalent to:
202+
*
203+
* <pre>
204+
* Values.mapRange(-1.0,1.0,0.0,1.0);
205+
* </pre>
206+
*
207+
* @param minInputValue the minimum value of the range of input values to the function
208+
* @param maxInputValue the maximum value of the range of input values to the function
209+
* @return the range maker that should be used to complete the function; never null
210+
* @see #mapRange(double, double, double, double)
211+
*/
212+
public static RangeMaker mapRange(double minInputValue, double maxInputValue) {
213+
return (minOutput, maxOutput) -> {
214+
return mapRange(minInputValue, maxInputValue, minOutput, maxOutput);
215+
};
216+
}
217+
218+
/**
219+
* Create a function that maps from the specified range of input values
220+
*
221+
* @param minInputValue the minimum value of the range of input values to the function
222+
* @param maxInputValue the maximum value of the range of input values to the function
223+
* @param minOutputValue the minimum value for the output of the function
224+
* @param maxOutputValue the maximum value for the output of the function
225+
* @return the mapping function; never null
226+
* @see #mapRange(double, double)
227+
*/
228+
public static DoubleToDoubleFunction mapRange(double minInputValue, double maxInputValue, double minOutputValue,
229+
double maxOutputValue) {
230+
double factor = (maxOutputValue - minOutputValue) / (maxInputValue - minInputValue);
231+
return new DoubleToDoubleFunction() {
232+
@Override
233+
public double applyAsDouble(double num) {
234+
if (num <= minInputValue) return minOutputValue;
235+
if (num >= maxInputValue) return maxOutputValue;
236+
double output = minOutputValue + ((num - minInputValue) * factor);
237+
if (output < minOutputValue)
238+
output = minOutputValue;
239+
else if (output > maxOutputValue) output = maxOutputValue;
240+
return output;
241+
}
242+
};
243+
}
244+
181245
}

0 commit comments

Comments
 (0)