001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2012. All Rights Reserved. */
003/* Open Source Software - may be modified and shared by FRC teams. The code */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project. */
006/*----------------------------------------------------------------------------*/
007package edu.wpi.first.wpilibj;
008
009import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tInstances;
010import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
011import edu.wpi.first.wpilibj.communication.UsageReporting;
012
013/**
014 * Utility class for handling Robot drive based on a definition of the motor
015 * configuration. The robot drive class handles basic driving for a robot.
016 * Currently, 2 and 4 motor tank and mecanum drive trains are supported. In the
017 * future other drive types like swerve might be implemented. Motor channel
018 * numbers are supplied on creation of the class. Those are used for either the
019 * drive function (intended for hand created drive code, such as autonomous) or
020 * with the Tank/Arcade functions intended to be used for Operator Control
021 * driving.
022 */
023public class RobotDrive implements MotorSafety {
024
025  protected MotorSafetyHelper m_safetyHelper;
026
027  /**
028   * The location of a motor on the robot for the purpose of driving
029   */
030  public static class MotorType {
031
032    /**
033     * The integer value representing this enumeration
034     */
035    public final int value;
036    static final int kFrontLeft_val = 0;
037    static final int kFrontRight_val = 1;
038    static final int kRearLeft_val = 2;
039    static final int kRearRight_val = 3;
040    /**
041     * motortype: front left
042     */
043    public static final MotorType kFrontLeft = new MotorType(kFrontLeft_val);
044    /**
045     * motortype: front right
046     */
047    public static final MotorType kFrontRight = new MotorType(kFrontRight_val);
048    /**
049     * motortype: rear left
050     */
051    public static final MotorType kRearLeft = new MotorType(kRearLeft_val);
052    /**
053     * motortype: rear right
054     */
055    public static final MotorType kRearRight = new MotorType(kRearRight_val);
056
057    private MotorType(int value) {
058      this.value = value;
059    }
060  }
061
062  public static final double kDefaultExpirationTime = 0.1;
063  public static final double kDefaultSensitivity = 0.5;
064  public static final double kDefaultMaxOutput = 1.0;
065  protected static final int kMaxNumberOfMotors = 4;
066  protected double m_sensitivity;
067  protected double m_maxOutput;
068  protected SpeedController m_frontLeftMotor;
069  protected SpeedController m_frontRightMotor;
070  protected SpeedController m_rearLeftMotor;
071  protected SpeedController m_rearRightMotor;
072  protected boolean m_allocatedSpeedControllers;
073  protected byte m_syncGroup = 0;
074  protected static boolean kArcadeRatioCurve_Reported = false;
075  protected static boolean kTank_Reported = false;
076  protected static boolean kArcadeStandard_Reported = false;
077  protected static boolean kMecanumCartesian_Reported = false;
078  protected static boolean kMecanumPolar_Reported = false;
079
080  /**
081   * Constructor for RobotDrive with 2 motors specified with channel numbers.
082   * Set up parameters for a two wheel drive system where the left and right
083   * motor pwm channels are specified in the call. This call assumes Talons for
084   * controlling the motors.
085   *$
086   * @param leftMotorChannel The PWM channel number that drives the left motor.
087   * @param rightMotorChannel The PWM channel number that drives the right
088   *        motor.
089   */
090  public RobotDrive(final int leftMotorChannel, final int rightMotorChannel) {
091    m_sensitivity = kDefaultSensitivity;
092    m_maxOutput = kDefaultMaxOutput;
093    m_frontLeftMotor = null;
094    m_rearLeftMotor = new Talon(leftMotorChannel);
095    m_frontRightMotor = null;
096    m_rearRightMotor = new Talon(rightMotorChannel);
097    m_allocatedSpeedControllers = true;
098    setupMotorSafety();
099    drive(0, 0);
100  }
101
102  /**
103   * Constructor for RobotDrive with 4 motors specified with channel numbers.
104   * Set up parameters for a four wheel drive system where all four motor pwm
105   * channels are specified in the call. This call assumes Talons for
106   * controlling the motors.
107   *$
108   * @param frontLeftMotor Front left motor channel number
109   * @param rearLeftMotor Rear Left motor channel number
110   * @param frontRightMotor Front right motor channel number
111   * @param rearRightMotor Rear Right motor channel number
112   */
113  public RobotDrive(final int frontLeftMotor, final int rearLeftMotor, final int frontRightMotor,
114      final int rearRightMotor) {
115    m_sensitivity = kDefaultSensitivity;
116    m_maxOutput = kDefaultMaxOutput;
117    m_rearLeftMotor = new Talon(rearLeftMotor);
118    m_rearRightMotor = new Talon(rearRightMotor);
119    m_frontLeftMotor = new Talon(frontLeftMotor);
120    m_frontRightMotor = new Talon(frontRightMotor);
121    m_allocatedSpeedControllers = true;
122    setupMotorSafety();
123    drive(0, 0);
124  }
125
126  /**
127   * Constructor for RobotDrive with 2 motors specified as SpeedController
128   * objects. The SpeedController version of the constructor enables programs to
129   * use the RobotDrive classes with subclasses of the SpeedController objects,
130   * for example, versions with ramping or reshaping of the curve to suit motor
131   * bias or dead-band elimination.
132   *$
133   * @param leftMotor The left SpeedController object used to drive the robot.
134   * @param rightMotor the right SpeedController object used to drive the robot.
135   */
136  public RobotDrive(SpeedController leftMotor, SpeedController rightMotor) {
137    if (leftMotor == null || rightMotor == null) {
138      m_rearLeftMotor = m_rearRightMotor = null;
139      throw new NullPointerException("Null motor provided");
140    }
141    m_frontLeftMotor = null;
142    m_rearLeftMotor = leftMotor;
143    m_frontRightMotor = null;
144    m_rearRightMotor = rightMotor;
145    m_sensitivity = kDefaultSensitivity;
146    m_maxOutput = kDefaultMaxOutput;
147    m_allocatedSpeedControllers = false;
148    setupMotorSafety();
149    drive(0, 0);
150  }
151
152  /**
153   * Constructor for RobotDrive with 4 motors specified as SpeedController
154   * objects. Speed controller input version of RobotDrive (see previous
155   * comments).
156   *$
157   * @param rearLeftMotor The back left SpeedController object used to drive the
158   *        robot.
159   * @param frontLeftMotor The front left SpeedController object used to drive
160   *        the robot
161   * @param rearRightMotor The back right SpeedController object used to drive
162   *        the robot.
163   * @param frontRightMotor The front right SpeedController object used to drive
164   *        the robot.
165   */
166  public RobotDrive(SpeedController frontLeftMotor, SpeedController rearLeftMotor,
167      SpeedController frontRightMotor, SpeedController rearRightMotor) {
168    if (frontLeftMotor == null || rearLeftMotor == null || frontRightMotor == null
169        || rearRightMotor == null) {
170      m_frontLeftMotor = m_rearLeftMotor = m_frontRightMotor = m_rearRightMotor = null;
171      throw new NullPointerException("Null motor provided");
172    }
173    m_frontLeftMotor = frontLeftMotor;
174    m_rearLeftMotor = rearLeftMotor;
175    m_frontRightMotor = frontRightMotor;
176    m_rearRightMotor = rearRightMotor;
177    m_sensitivity = kDefaultSensitivity;
178    m_maxOutput = kDefaultMaxOutput;
179    m_allocatedSpeedControllers = false;
180    setupMotorSafety();
181    drive(0, 0);
182  }
183
184  /**
185   * Drive the motors at "outputMagnitude" and "curve".
186   * Both outputMagnitude and curve are -1.0 to +1.0 values, where 0.0
187   * represents stopped and not turning. {@literal curve < 0 will turn left and curve > 0}
188   * will turn right.
189   *
190   * The algorithm for steering provides a constant turn radius for any normal
191   * speed range, both forward and backward. Increasing m_sensitivity causes
192   * sharper turns for fixed values of curve.
193   *
194   * This function will most likely be used in an autonomous routine.
195   *
196   * @param outputMagnitude The speed setting for the outside wheel in a turn,
197   *        forward or backwards, +1 to -1.
198   * @param curve The rate of turn, constant for different forward speeds. Set
199   *        {@literal curve < 0 for left turn or curve > 0 for right turn.}
200   * Set curve = e^(-r/w) to get a turn radius r for wheelbase w of your robot.
201   * Conversely, turn radius r = -ln(curve)*w for a given value of curve and
202   * wheelbase w.
203   */
204  public void drive(double outputMagnitude, double curve) {
205    double leftOutput, rightOutput;
206
207    if (!kArcadeRatioCurve_Reported) {
208      UsageReporting.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
209          tInstances.kRobotDrive_ArcadeRatioCurve);
210      kArcadeRatioCurve_Reported = true;
211    }
212    if (curve < 0) {
213      double value = Math.log(-curve);
214      double ratio = (value - m_sensitivity) / (value + m_sensitivity);
215      if (ratio == 0) {
216        ratio = .0000000001;
217      }
218      leftOutput = outputMagnitude / ratio;
219      rightOutput = outputMagnitude;
220    } else if (curve > 0) {
221      double value = Math.log(curve);
222      double ratio = (value - m_sensitivity) / (value + m_sensitivity);
223      if (ratio == 0) {
224        ratio = .0000000001;
225      }
226      leftOutput = outputMagnitude;
227      rightOutput = outputMagnitude / ratio;
228    } else {
229      leftOutput = outputMagnitude;
230      rightOutput = outputMagnitude;
231    }
232    setLeftRightMotorOutputs(leftOutput, rightOutput);
233  }
234
235  /**
236   * Provide tank steering using the stored robot configuration. drive the robot
237   * using two joystick inputs. The Y-axis will be selected from each Joystick
238   * object.
239   *$
240   * @param leftStick The joystick to control the left side of the robot.
241   * @param rightStick The joystick to control the right side of the robot.
242   */
243  public void tankDrive(GenericHID leftStick, GenericHID rightStick) {
244    if (leftStick == null || rightStick == null) {
245      throw new NullPointerException("Null HID provided");
246    }
247    tankDrive(leftStick.getY(), rightStick.getY(), true);
248  }
249
250  /**
251   * Provide tank steering using the stored robot configuration. drive the robot
252   * using two joystick inputs. The Y-axis will be selected from each Joystick
253   * object.
254   *$
255   * @param leftStick The joystick to control the left side of the robot.
256   * @param rightStick The joystick to control the right side of the robot.
257   * @param squaredInputs Setting this parameter to true decreases the
258   *        sensitivity at lower speeds
259   */
260  public void tankDrive(GenericHID leftStick, GenericHID rightStick, boolean squaredInputs) {
261    if (leftStick == null || rightStick == null) {
262      throw new NullPointerException("Null HID provided");
263    }
264    tankDrive(leftStick.getY(), rightStick.getY(), squaredInputs);
265  }
266
267  /**
268   * Provide tank steering using the stored robot configuration. This function
269   * lets you pick the axis to be used on each Joystick object for the left and
270   * right sides of the robot.
271   *$
272   * @param leftStick The Joystick object to use for the left side of the robot.
273   * @param leftAxis The axis to select on the left side Joystick object.
274   * @param rightStick The Joystick object to use for the right side of the
275   *        robot.
276   * @param rightAxis The axis to select on the right side Joystick object.
277   */
278  public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick,
279      final int rightAxis) {
280    if (leftStick == null || rightStick == null) {
281      throw new NullPointerException("Null HID provided");
282    }
283    tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), true);
284  }
285
286  /**
287   * Provide tank steering using the stored robot configuration. This function
288   * lets you pick the axis to be used on each Joystick object for the left and
289   * right sides of the robot.
290   *$
291   * @param leftStick The Joystick object to use for the left side of the robot.
292   * @param leftAxis The axis to select on the left side Joystick object.
293   * @param rightStick The Joystick object to use for the right side of the
294   *        robot.
295   * @param rightAxis The axis to select on the right side Joystick object.
296   * @param squaredInputs Setting this parameter to true decreases the
297   *        sensitivity at lower speeds
298   */
299  public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick,
300      final int rightAxis, boolean squaredInputs) {
301    if (leftStick == null || rightStick == null) {
302      throw new NullPointerException("Null HID provided");
303    }
304    tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), squaredInputs);
305  }
306
307  /**
308   * Provide tank steering using the stored robot configuration. This function
309   * lets you directly provide joystick values from any source.
310   *$
311   * @param leftValue The value of the left stick.
312   * @param rightValue The value of the right stick.
313   * @param squaredInputs Setting this parameter to true decreases the
314   *        sensitivity at lower speeds
315   */
316  public void tankDrive(double leftValue, double rightValue, boolean squaredInputs) {
317
318    if (!kTank_Reported) {
319      UsageReporting.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
320          tInstances.kRobotDrive_Tank);
321      kTank_Reported = true;
322    }
323
324    // square the inputs (while preserving the sign) to increase fine control
325    // while permitting full power
326    leftValue = limit(leftValue);
327    rightValue = limit(rightValue);
328    if (squaredInputs) {
329      if (leftValue >= 0.0) {
330        leftValue = (leftValue * leftValue);
331      } else {
332        leftValue = -(leftValue * leftValue);
333      }
334      if (rightValue >= 0.0) {
335        rightValue = (rightValue * rightValue);
336      } else {
337        rightValue = -(rightValue * rightValue);
338      }
339    }
340    setLeftRightMotorOutputs(leftValue, rightValue);
341  }
342
343  /**
344   * Provide tank steering using the stored robot configuration. This function
345   * lets you directly provide joystick values from any source.
346   *$
347   * @param leftValue The value of the left stick.
348   * @param rightValue The value of the right stick.
349   */
350  public void tankDrive(double leftValue, double rightValue) {
351    tankDrive(leftValue, rightValue, true);
352  }
353
354  /**
355   * Arcade drive implements single stick driving. Given a single Joystick, the
356   * class assumes the Y axis for the move value and the X axis for the rotate
357   * value. (Should add more information here regarding the way that arcade
358   * drive works.)
359   *$
360   * @param stick The joystick to use for Arcade single-stick driving. The
361   *        Y-axis will be selected for forwards/backwards and the X-axis will
362   *        be selected for rotation rate.
363   * @param squaredInputs If true, the sensitivity will be decreased for small
364   *        values
365   */
366  public void arcadeDrive(GenericHID stick, boolean squaredInputs) {
367    // simply call the full-featured arcadeDrive with the appropriate values
368    arcadeDrive(stick.getY(), stick.getX(), squaredInputs);
369  }
370
371  /**
372   * Arcade drive implements single stick driving. Given a single Joystick, the
373   * class assumes the Y axis for the move value and the X axis for the rotate
374   * value. (Should add more information here regarding the way that arcade
375   * drive works.)
376   *$
377   * @param stick The joystick to use for Arcade single-stick driving. The
378   *        Y-axis will be selected for forwards/backwards and the X-axis will
379   *        be selected for rotation rate.
380   */
381  public void arcadeDrive(GenericHID stick) {
382    this.arcadeDrive(stick, true);
383  }
384
385  /**
386   * Arcade drive implements single stick driving. Given two joystick instances
387   * and two axis, compute the values to send to either two or four motors.
388   *$
389   * @param moveStick The Joystick object that represents the forward/backward
390   *        direction
391   * @param moveAxis The axis on the moveStick object to use for
392   *        forwards/backwards (typically Y_AXIS)
393   * @param rotateStick The Joystick object that represents the rotation value
394   * @param rotateAxis The axis on the rotation object to use for the rotate
395   *        right/left (typically X_AXIS)
396   * @param squaredInputs Setting this parameter to true decreases the
397   *        sensitivity at lower speeds
398   */
399  public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick,
400      final int rotateAxis, boolean squaredInputs) {
401    double moveValue = moveStick.getRawAxis(moveAxis);
402    double rotateValue = rotateStick.getRawAxis(rotateAxis);
403
404    arcadeDrive(moveValue, rotateValue, squaredInputs);
405  }
406
407  /**
408   * Arcade drive implements single stick driving. Given two joystick instances
409   * and two axis, compute the values to send to either two or four motors.
410   *$
411   * @param moveStick The Joystick object that represents the forward/backward
412   *        direction
413   * @param moveAxis The axis on the moveStick object to use for
414   *        forwards/backwards (typically Y_AXIS)
415   * @param rotateStick The Joystick object that represents the rotation value
416   * @param rotateAxis The axis on the rotation object to use for the rotate
417   *        right/left (typically X_AXIS)
418   */
419  public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick,
420      final int rotateAxis) {
421    this.arcadeDrive(moveStick, moveAxis, rotateStick, rotateAxis, true);
422  }
423
424  /**
425   * Arcade drive implements single stick driving. This function lets you
426   * directly provide joystick values from any source.
427   *$
428   * @param moveValue The value to use for forwards/backwards
429   * @param rotateValue The value to use for the rotate right/left
430   * @param squaredInputs If set, decreases the sensitivity at low speeds
431   */
432  public void arcadeDrive(double moveValue, double rotateValue, boolean squaredInputs) {
433    // local variables to hold the computed PWM values for the motors
434    if (!kArcadeStandard_Reported) {
435      UsageReporting.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
436          tInstances.kRobotDrive_ArcadeStandard);
437      kArcadeStandard_Reported = true;
438    }
439
440    double leftMotorSpeed;
441    double rightMotorSpeed;
442
443    moveValue = limit(moveValue);
444    rotateValue = limit(rotateValue);
445
446    if (squaredInputs) {
447      // square the inputs (while preserving the sign) to increase fine control
448      // while permitting full power
449      if (moveValue >= 0.0) {
450        moveValue = (moveValue * moveValue);
451      } else {
452        moveValue = -(moveValue * moveValue);
453      }
454      if (rotateValue >= 0.0) {
455        rotateValue = (rotateValue * rotateValue);
456      } else {
457        rotateValue = -(rotateValue * rotateValue);
458      }
459    }
460
461    if (moveValue > 0.0) {
462      if (rotateValue > 0.0) {
463        leftMotorSpeed = moveValue - rotateValue;
464        rightMotorSpeed = Math.max(moveValue, rotateValue);
465      } else {
466        leftMotorSpeed = Math.max(moveValue, -rotateValue);
467        rightMotorSpeed = moveValue + rotateValue;
468      }
469    } else {
470      if (rotateValue > 0.0) {
471        leftMotorSpeed = -Math.max(-moveValue, rotateValue);
472        rightMotorSpeed = moveValue + rotateValue;
473      } else {
474        leftMotorSpeed = moveValue - rotateValue;
475        rightMotorSpeed = -Math.max(-moveValue, -rotateValue);
476      }
477    }
478
479    setLeftRightMotorOutputs(leftMotorSpeed, rightMotorSpeed);
480  }
481
482  /**
483   * Arcade drive implements single stick driving. This function lets you
484   * directly provide joystick values from any source.
485   *$
486   * @param moveValue The value to use for fowards/backwards
487   * @param rotateValue The value to use for the rotate right/left
488   */
489  public void arcadeDrive(double moveValue, double rotateValue) {
490    this.arcadeDrive(moveValue, rotateValue, true);
491  }
492
493  /**
494   * Drive method for Mecanum wheeled robots.
495   *
496   * A method for driving with Mecanum wheeled robots. There are 4 wheels on the
497   * robot, arranged so that the front and back wheels are toed in 45 degrees.
498   * When looking at the wheels from the top, the roller axles should form an X
499   * across the robot.
500   *
501   * This is designed to be directly driven by joystick axes.
502   *
503   * @param x The speed that the robot should drive in the X direction.
504   *        [-1.0..1.0]
505   * @param y The speed that the robot should drive in the Y direction. This
506   *        input is inverted to match the forward == -1.0 that joysticks
507   *        produce. [-1.0..1.0]
508   * @param rotation The rate of rotation for the robot that is completely
509   *        independent of the translation. [-1.0..1.0]
510   * @param gyroAngle The current angle reading from the gyro. Use this to
511   *        implement field-oriented controls.
512   */
513  public void mecanumDrive_Cartesian(double x, double y, double rotation, double gyroAngle) {
514    if (!kMecanumCartesian_Reported) {
515      UsageReporting.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
516          tInstances.kRobotDrive_MecanumCartesian);
517      kMecanumCartesian_Reported = true;
518    }
519    double xIn = x;
520    double yIn = y;
521    // Negate y for the joystick.
522    yIn = -yIn;
523    // Compenstate for gyro angle.
524    double rotated[] = rotateVector(xIn, yIn, gyroAngle);
525    xIn = rotated[0];
526    yIn = rotated[1];
527
528    double wheelSpeeds[] = new double[kMaxNumberOfMotors];
529    wheelSpeeds[MotorType.kFrontLeft_val] = xIn + yIn + rotation;
530    wheelSpeeds[MotorType.kFrontRight_val] = -xIn + yIn - rotation;
531    wheelSpeeds[MotorType.kRearLeft_val] = -xIn + yIn + rotation;
532    wheelSpeeds[MotorType.kRearRight_val] = xIn + yIn - rotation;
533
534    normalize(wheelSpeeds);
535    m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft_val] * m_maxOutput, m_syncGroup);
536    m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight_val] * m_maxOutput, m_syncGroup);
537    m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft_val] * m_maxOutput, m_syncGroup);
538    m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight_val] * m_maxOutput, m_syncGroup);
539
540    if (m_syncGroup != 0) {
541      CANJaguar.updateSyncGroup(m_syncGroup);
542    }
543
544    if (m_safetyHelper != null)
545      m_safetyHelper.feed();
546  }
547
548  /**
549   * Drive method for Mecanum wheeled robots.
550   *
551   * A method for driving with Mecanum wheeled robots. There are 4 wheels on the
552   * robot, arranged so that the front and back wheels are toed in 45 degrees.
553   * When looking at the wheels from the top, the roller axles should form an X
554   * across the robot.
555   *
556   * @param magnitude The speed that the robot should drive in a given
557   *        direction.
558   * @param direction The direction the robot should drive in degrees. The
559   *        direction and maginitute are independent of the rotation rate.
560   * @param rotation The rate of rotation for the robot that is completely
561   *        independent of the magnitute or direction. [-1.0..1.0]
562   */
563  public void mecanumDrive_Polar(double magnitude, double direction, double rotation) {
564    if (!kMecanumPolar_Reported) {
565      UsageReporting.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
566          tInstances.kRobotDrive_MecanumPolar);
567      kMecanumPolar_Reported = true;
568    }
569    double frontLeftSpeed, rearLeftSpeed, frontRightSpeed, rearRightSpeed;
570    // Normalized for full power along the Cartesian axes.
571    magnitude = limit(magnitude) * Math.sqrt(2.0);
572    // The rollers are at 45 degree angles.
573    double dirInRad = (direction + 45.0) * 3.14159 / 180.0;
574    double cosD = Math.cos(dirInRad);
575    double sinD = Math.sin(dirInRad);
576
577    double wheelSpeeds[] = new double[kMaxNumberOfMotors];
578    wheelSpeeds[MotorType.kFrontLeft_val] = (sinD * magnitude + rotation);
579    wheelSpeeds[MotorType.kFrontRight_val] = (cosD * magnitude - rotation);
580    wheelSpeeds[MotorType.kRearLeft_val] = (cosD * magnitude + rotation);
581    wheelSpeeds[MotorType.kRearRight_val] = (sinD * magnitude - rotation);
582
583    normalize(wheelSpeeds);
584
585    m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft_val] * m_maxOutput, m_syncGroup);
586    m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight_val] * m_maxOutput, m_syncGroup);
587    m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft_val] * m_maxOutput, m_syncGroup);
588    m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight_val] * m_maxOutput, m_syncGroup);
589
590    if (this.m_syncGroup != 0) {
591      CANJaguar.updateSyncGroup(m_syncGroup);
592    }
593
594    if (m_safetyHelper != null)
595      m_safetyHelper.feed();
596  }
597
598  /**
599   * Holonomic Drive method for Mecanum wheeled robots.
600   *
601   * This is an alias to mecanumDrive_Polar() for backward compatability
602   *
603   * @param magnitude The speed that the robot should drive in a given
604   *        direction. [-1.0..1.0]
605   * @param direction The direction the robot should drive. The direction and
606   *        maginitute are independent of the rotation rate.
607   * @param rotation The rate of rotation for the robot that is completely
608   *        independent of the magnitute or direction. [-1.0..1.0]
609   */
610  void holonomicDrive(float magnitude, float direction, float rotation) {
611    mecanumDrive_Polar(magnitude, direction, rotation);
612  }
613
614  /**
615   * Set the speed of the right and left motors. This is used once an
616   * appropriate drive setup function is called such as twoWheelDrive(). The
617   * motors are set to "leftSpeed" and "rightSpeed" and includes flipping the
618   * direction of one side for opposing motors.
619   *$
620   * @param leftOutput The speed to send to the left side of the robot.
621   * @param rightOutput The speed to send to the right side of the robot.
622   */
623  public void setLeftRightMotorOutputs(double leftOutput, double rightOutput) {
624    if (m_rearLeftMotor == null || m_rearRightMotor == null) {
625      throw new NullPointerException("Null motor provided");
626    }
627
628    if (m_frontLeftMotor != null) {
629      m_frontLeftMotor.set(limit(leftOutput) * m_maxOutput, m_syncGroup);
630    }
631    m_rearLeftMotor.set(limit(leftOutput) * m_maxOutput, m_syncGroup);
632
633    if (m_frontRightMotor != null) {
634      m_frontRightMotor.set(-limit(rightOutput) * m_maxOutput, m_syncGroup);
635    }
636    m_rearRightMotor.set(-limit(rightOutput) * m_maxOutput, m_syncGroup);
637
638    if (this.m_syncGroup != 0) {
639      CANJaguar.updateSyncGroup(m_syncGroup);
640    }
641
642    if (m_safetyHelper != null)
643      m_safetyHelper.feed();
644  }
645
646  /**
647   * Limit motor values to the -1.0 to +1.0 range.
648   */
649  protected static double limit(double num) {
650    if (num > 1.0) {
651      return 1.0;
652    }
653    if (num < -1.0) {
654      return -1.0;
655    }
656    return num;
657  }
658
659  /**
660   * Normalize all wheel speeds if the magnitude of any wheel is greater than
661   * 1.0.
662   */
663  protected static void normalize(double wheelSpeeds[]) {
664    double maxMagnitude = Math.abs(wheelSpeeds[0]);
665    int i;
666    for (i = 1; i < kMaxNumberOfMotors; i++) {
667      double temp = Math.abs(wheelSpeeds[i]);
668      if (maxMagnitude < temp)
669        maxMagnitude = temp;
670    }
671    if (maxMagnitude > 1.0) {
672      for (i = 0; i < kMaxNumberOfMotors; i++) {
673        wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude;
674      }
675    }
676  }
677
678  /**
679   * Rotate a vector in Cartesian space.
680   */
681  protected static double[] rotateVector(double x, double y, double angle) {
682    double cosA = Math.cos(angle * (3.14159 / 180.0));
683    double sinA = Math.sin(angle * (3.14159 / 180.0));
684    double out[] = new double[2];
685    out[0] = x * cosA - y * sinA;
686    out[1] = x * sinA + y * cosA;
687    return out;
688  }
689
690  /**
691   * Invert a motor direction. This is used when a motor should run in the
692   * opposite direction as the drive code would normally run it. Motors that are
693   * direct drive would be inverted, the drive code assumes that the motors are
694   * geared with one reversal.
695   *$
696   * @param motor The motor index to invert.
697   * @param isInverted True if the motor should be inverted when operated.
698   */
699  public void setInvertedMotor(MotorType motor, boolean isInverted) {
700    switch (motor.value) {
701      case MotorType.kFrontLeft_val:
702        m_frontLeftMotor.setInverted(isInverted);
703        break;
704      case MotorType.kFrontRight_val:
705        m_frontRightMotor.setInverted(isInverted);
706        break;
707      case MotorType.kRearLeft_val:
708        m_rearLeftMotor.setInverted(isInverted);
709        break;
710      case MotorType.kRearRight_val:
711        m_rearRightMotor.setInverted(isInverted);
712        break;
713    }
714  }
715
716  /**
717   * Set the turning sensitivity.
718   *
719   * This only impacts the drive() entry-point.
720   *$
721   * @param sensitivity Effectively sets the turning sensitivity (or turn radius
722   *        for a given value)
723   */
724  public void setSensitivity(double sensitivity) {
725    m_sensitivity = sensitivity;
726  }
727
728  /**
729   * Configure the scaling factor for using RobotDrive with motor controllers in
730   * a mode other than PercentVbus.
731   *$
732   * @param maxOutput Multiplied with the output percentage computed by the
733   *        drive functions.
734   */
735  public void setMaxOutput(double maxOutput) {
736    m_maxOutput = maxOutput;
737  }
738
739  /**
740   * Set the number of the sync group for the motor controllers. If the motor
741   * controllers are {@link CANJaguar}s, then they will all be added to this
742   * sync group, causing them to update their values at the same time.
743   *
744   * @param syncGroup the update group to add the motor controllers to
745   */
746  public void setCANJaguarSyncGroup(byte syncGroup) {
747    m_syncGroup = syncGroup;
748  }
749
750  /**
751   * Free the speed controllers if they were allocated locally
752   */
753  public void free() {
754    if (m_allocatedSpeedControllers) {
755      if (m_frontLeftMotor != null) {
756        ((PWM) m_frontLeftMotor).free();
757      }
758      if (m_frontRightMotor != null) {
759        ((PWM) m_frontRightMotor).free();
760      }
761      if (m_rearLeftMotor != null) {
762        ((PWM) m_rearLeftMotor).free();
763      }
764      if (m_rearRightMotor != null) {
765        ((PWM) m_rearRightMotor).free();
766      }
767    }
768  }
769
770  public void setExpiration(double timeout) {
771    m_safetyHelper.setExpiration(timeout);
772  }
773
774  public double getExpiration() {
775    return m_safetyHelper.getExpiration();
776  }
777
778  public boolean isAlive() {
779    return m_safetyHelper.isAlive();
780  }
781
782  public boolean isSafetyEnabled() {
783    return m_safetyHelper.isSafetyEnabled();
784  }
785
786  public void setSafetyEnabled(boolean enabled) {
787    m_safetyHelper.setSafetyEnabled(enabled);
788  }
789
790  public String getDescription() {
791    return "Robot Drive";
792  }
793
794  public void stopMotor() {
795    if (m_frontLeftMotor != null) {
796      m_frontLeftMotor.set(0.0);
797    }
798    if (m_frontRightMotor != null) {
799      m_frontRightMotor.set(0.0);
800    }
801    if (m_rearLeftMotor != null) {
802      m_rearLeftMotor.set(0.0);
803    }
804    if (m_rearRightMotor != null) {
805      m_rearRightMotor.set(0.0);
806    }
807    if (m_safetyHelper != null)
808      m_safetyHelper.feed();
809  }
810
811  private void setupMotorSafety() {
812    m_safetyHelper = new MotorSafetyHelper(this);
813    m_safetyHelper.setExpiration(kDefaultExpirationTime);
814    m_safetyHelper.setSafetyEnabled(true);
815  }
816
817  protected int getNumMotors() {
818    int motors = 0;
819    if (m_frontLeftMotor != null)
820      motors++;
821    if (m_frontRightMotor != null)
822      motors++;
823    if (m_rearLeftMotor != null)
824      motors++;
825    if (m_rearRightMotor != null)
826      motors++;
827    return motors;
828  }
829}