blob: f9372129d5b00d36b5141660c49be968a37d74e2 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#include "CANTalon.h"
#include "WPIErrors.h"
#include <unistd.h> // usleep
#include <sstream>
/**
* Number of adc engineering units per 0 to 3.3V sweep.
* This is necessary for scaling Analog Position in rotations/RPM.
*/
const double kNativeAdcUnitsPerRotation = 1024.0;
/**
* Number of pulse width engineering units per full rotation.
* This is necessary for scaling Pulse Width Decoded Position in rotations/RPM.
*/
const double kNativePwdUnitsPerRotation = 4096.0;
/**
* Number of minutes per 100ms unit. Useful for scaling velocities
* measured by Talon's 100ms timebase to rotations per minute.
*/
const double kMinutesPer100msUnit = 1.0/600.0;
/**
* Constructor for the CANTalon device.
* @param deviceNumber The CAN ID of the Talon SRX
*/
CANTalon::CANTalon(int deviceNumber)
: m_deviceNumber(deviceNumber),
m_impl(new CanTalonSRX(deviceNumber)),
m_safetyHelper(new MotorSafetyHelper(this)) {
ApplyControlMode(m_controlMode);
m_impl->SetProfileSlotSelect(m_profile);
m_isInverted = false;
}
/**
* Constructor for the CANTalon device.
* @param deviceNumber The CAN ID of the Talon SRX
* @param controlPeriodMs The period in ms to send the CAN control frame.
* Period is bounded to [1ms,95ms].
*/
CANTalon::CANTalon(int deviceNumber, int controlPeriodMs)
: m_deviceNumber(deviceNumber),
m_impl(new CanTalonSRX(deviceNumber,controlPeriodMs)),
m_safetyHelper(new MotorSafetyHelper(this)) {
ApplyControlMode(m_controlMode);
m_impl->SetProfileSlotSelect(m_profile);
}
CANTalon::~CANTalon() {
if (m_table != nullptr) m_table->RemoveTableListener(this);
if (m_hasBeenMoved) return;
Disable();
}
/**
* Write out the PID value as seen in the PIDOutput base object.
*
* @deprecated Call Set instead.
*
* @param output Write out the PercentVbus value as was computed by the
* PIDController
*/
void CANTalon::PIDWrite(float output) {
if (GetControlMode() == kPercentVbus) {
Set(output);
} else {
wpi_setWPIErrorWithContext(IncompatibleMode,
"PID only supported in PercentVbus mode");
}
}
/**
* Retrieve the current sensor value. Equivalent to Get().
*
* @return The current sensor value of the Talon.
*/
double CANTalon::PIDGet() { return Get(); }
/**
* Gets the current status of the Talon (usually a sensor value).
*
* In Current mode: returns output current.
* In Speed mode: returns current speed.
* In Position mode: returns current sensor position.
* In PercentVbus and Follower modes: returns current applied throttle.
*
* @return The current sensor value of the Talon.
*/
float CANTalon::Get() const {
int value;
switch (m_controlMode) {
case kVoltage:
return GetOutputVoltage();
case kCurrent:
return GetOutputCurrent();
case kSpeed:
m_impl->GetSensorVelocity(value);
return ScaleNativeUnitsToRpm(m_feedbackDevice, value);
case kPosition:
m_impl->GetSensorPosition(value);
return ScaleNativeUnitsToRotations(m_feedbackDevice, value);
case kPercentVbus:
case kFollower:
default:
m_impl->GetAppliedThrottle(value);
return (float)value / 1023.0;
}
}
/**
* Sets the appropriate output on the talon, depending on the mode.
*
* In PercentVbus, the output is between -1.0 and 1.0, with 0.0 as stopped.
* In Voltage mode, output value is in volts.
* In Current mode, output value is in amperes.
* In Speed mode, output value is in position change / 10ms.
* In Position mode, output value is in encoder ticks or an analog value,
* depending on the sensor.
* In Follower mode, the output value is the integer device ID of the talon to
* duplicate.
*
* @param outputValue The setpoint value, as described above.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::Set(float value, uint8_t syncGroup) {
/* feed safety helper since caller just updated our output */
m_safetyHelper->Feed();
if (m_controlEnabled) {
m_setPoint = value; /* cache set point for GetSetpoint() */
CTR_Code status = CTR_OKAY;
switch (m_controlMode) {
case CANSpeedController::kPercentVbus: {
m_impl->Set(m_isInverted ? -value : value);
status = CTR_OKAY;
} break;
case CANSpeedController::kFollower: {
status = m_impl->SetDemand((int)value);
} break;
case CANSpeedController::kVoltage: {
// Voltage is an 8.8 fixed point number.
int volts = int((m_isInverted ? -value : value) * 256);
status = m_impl->SetDemand(volts);
} break;
case CANSpeedController::kSpeed:
/* if the caller has provided scaling info, apply it */
status = m_impl->SetDemand(ScaleVelocityToNativeUnits(m_feedbackDevice, m_isInverted ? -value : value));
break;
case CANSpeedController::kPosition:
status = m_impl->SetDemand(ScaleRotationsToNativeUnits(m_feedbackDevice, value));
break;
case CANSpeedController::kCurrent: {
double milliamperes = (m_isInverted ? -value : value) * 1000.0; /* mA*/
status = m_impl->SetDemand(milliamperes);
} break;
default:
wpi_setWPIErrorWithContext(
IncompatibleMode,
"The CAN Talon does not support this control mode.");
break;
}
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->SetModeSelect(m_sendMode);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
}
/**
* Sets the setpoint to value. Equivalent to Set().
*/
void CANTalon::SetSetpoint(float value) { Set(value); }
/**
* Resets the integral term and disables the controller.
*/
void CANTalon::Reset() {
ClearIaccum();
Disable();
}
/**
* Disables control of the talon, causing the motor to brake or coast
* depending on its mode (see the Talon SRX Software Reference manual
* for more information).
*/
void CANTalon::Disable() {
m_impl->SetModeSelect((int)CANTalon::kDisabled);
m_controlEnabled = false;
}
/**
* Enables control of the Talon, allowing the motor to move.
*/
void CANTalon::EnableControl() {
SetControlMode(m_controlMode);
m_controlEnabled = true;
}
/**
* Enables control of the Talon, allowing the motor to move.
*/
void CANTalon::Enable() { EnableControl(); }
/**
* @return Whether the Talon is currently enabled.
*/
bool CANTalon::IsControlEnabled() const { return m_controlEnabled; }
/**
* @return Whether the Talon is currently enabled.
*/
bool CANTalon::IsEnabled() const { return IsControlEnabled(); }
/**
* @param p Proportional constant to use in PID loop.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetP(double p) {
CTR_Code status = m_impl->SetPgain(m_profile, p);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Set the integration constant of the currently selected profile.
*
* @param i Integration constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetI(double i) {
CTR_Code status = m_impl->SetIgain(m_profile, i);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Set the derivative constant of the currently selected profile.
*
* @param d Derivative constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetD(double d) {
CTR_Code status = m_impl->SetDgain(m_profile, d);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Set the feedforward value of the currently selected profile.
*
* @param f Feedforward constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetF(double f) {
CTR_Code status = m_impl->SetFgain(m_profile, f);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Set the Izone to a nonzero value to auto clear the integral accumulator
* when the absolute value of CloseLoopError exceeds Izone.
*
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetIzone(unsigned iz) {
CTR_Code status = m_impl->SetIzone(m_profile, iz);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* SRX has two available slots for PID.
* @param slotIdx one or zero depending on which slot caller wants.
*/
void CANTalon::SelectProfileSlot(int slotIdx) {
m_profile = (slotIdx == 0) ? 0 : 1; /* only get two slots for now */
CTR_Code status = m_impl->SetProfileSlotSelect(m_profile);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Sets control values for closed loop control.
*
* @param p Proportional constant.
* @param i Integration constant.
* @param d Differential constant.
* This function does not modify F-gain. Considerable passing a zero for f
* using
* the four-parameter function.
*/
void CANTalon::SetPID(double p, double i, double d) {
SetP(p);
SetI(i);
SetD(d);
}
/**
* Sets control values for closed loop control.
*
* @param p Proportional constant.
* @param i Integration constant.
* @param d Differential constant.
* @param f Feedforward constant.
*/
void CANTalon::SetPID(double p, double i, double d, double f) {
SetP(p);
SetI(i);
SetD(d);
SetF(f);
}
/**
* Select the feedback device to use in closed-loop
*/
void CANTalon::SetFeedbackDevice(FeedbackDevice feedbackDevice) {
/* save the selection so that future setters/getters know which scalars to apply */
m_feedbackDevice = feedbackDevice;
/* pass feedback to actual CAN frame */
CTR_Code status = m_impl->SetFeedbackDeviceSelect((int)feedbackDevice);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Select the feedback device to use in closed-loop
*/
void CANTalon::SetStatusFrameRateMs(StatusFrameRate stateFrame, int periodMs) {
CTR_Code status = m_impl->SetStatusFrameRate((int)stateFrame, periodMs);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Get the current proportional constant.
*
* @return double proportional constant for current profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
double CANTalon::GetP() const {
CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_P
: CanTalonSRX::eProfileParamSlot0_P;
// Update the info in m_impl.
CTR_Code status = m_impl->RequestParam(param);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
double p;
status = m_impl->GetPgain(m_profile, p);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return p;
}
/**
* TODO documentation (see CANJaguar.cpp)
* @see SelectProfileSlot to choose between the two sets of gains.
*/
double CANTalon::GetI() const {
CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_I
: CanTalonSRX::eProfileParamSlot0_I;
// Update the info in m_impl.
CTR_Code status = m_impl->RequestParam(param);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
double i;
status = m_impl->GetIgain(m_profile, i);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return i;
}
/**
* TODO documentation (see CANJaguar.cpp)
* @see SelectProfileSlot to choose between the two sets of gains.
*/
double CANTalon::GetD() const {
CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_D
: CanTalonSRX::eProfileParamSlot0_D;
// Update the info in m_impl.
CTR_Code status = m_impl->RequestParam(param);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
double d;
status = m_impl->GetDgain(m_profile, d);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return d;
}
/**
*
* @see SelectProfileSlot to choose between the two sets of gains.
*/
double CANTalon::GetF() const {
CanTalonSRX::param_t param = m_profile ? CanTalonSRX::eProfileParamSlot1_F
: CanTalonSRX::eProfileParamSlot0_F;
// Update the info in m_impl.
CTR_Code status = m_impl->RequestParam(param);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
double f;
status = m_impl->GetFgain(m_profile, f);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return f;
}
/**
* @see SelectProfileSlot to choose between the two sets of gains.
*/
int CANTalon::GetIzone() const {
CanTalonSRX::param_t param = m_profile
? CanTalonSRX::eProfileParamSlot1_IZone
: CanTalonSRX::eProfileParamSlot0_IZone;
// Update the info in m_impl.
CTR_Code status = m_impl->RequestParam(param);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs);
int iz;
status = m_impl->GetIzone(m_profile, iz);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return iz;
}
/**
* @return the current setpoint; ie, whatever was last passed to Set().
*/
double CANTalon::GetSetpoint() const { return m_setPoint; }
/**
* Returns the voltage coming in from the battery.
*
* @return The input voltage in volts.
*/
float CANTalon::GetBusVoltage() const {
double voltage;
CTR_Code status = m_impl->GetBatteryV(voltage);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return voltage;
}
/**
* @return The voltage being output by the Talon, in Volts.
*/
float CANTalon::GetOutputVoltage() const {
int throttle11;
CTR_Code status = m_impl->GetAppliedThrottle(throttle11);
float voltage = GetBusVoltage() * (float(throttle11) / 1023.0);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return voltage;
}
/**
* Returns the current going through the Talon, in Amperes.
*/
float CANTalon::GetOutputCurrent() const {
double current;
CTR_Code status = m_impl->GetCurrent(current);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return current;
}
/**
* Returns temperature of Talon, in degrees Celsius.
*/
float CANTalon::GetTemperature() const {
double temp;
CTR_Code status = m_impl->GetTemp(temp);
if (temp != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return temp;
}
/**
* Set the position value of the selected sensor. This is useful for zero-ing
* quadrature encoders.
* Continuous sensors (like analog encoderes) can also partially be set (the
* portion of the postion based on overflows).
*/
void CANTalon::SetPosition(double pos) {
int32_t nativePos = ScaleRotationsToNativeUnits(m_feedbackDevice, pos);
CTR_Code status = m_impl->SetSensorPosition(nativePos);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*
* @return The position of the sensor currently providing feedback.
* When using analog sensors, 0 units corresponds to 0V, 1023
* units corresponds to 3.3V
* When using an analog encoder (wrapping around 1023 => 0 is
* possible) the units are still 3.3V per 1023 units.
* When using quadrature, each unit is a quadrature edge (4X)
* mode.
*/
double CANTalon::GetPosition() const {
int32_t position;
CTR_Code status = m_impl->GetSensorPosition(position);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return ScaleNativeUnitsToRotations(m_feedbackDevice, position);
}
/**
* If sensor and motor are out of phase, sensor can be inverted
* (position and velocity multiplied by -1).
* @see GetPosition and @see GetSpeed.
*/
void CANTalon::SetSensorDirection(bool reverseSensor) {
CTR_Code status = m_impl->SetRevFeedbackSensor(reverseSensor ? 1 : 0);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Flips the sign (multiplies by negative one) the throttle values going into
* the motor on the talon in closed loop modes. Typically the application
* should use SetSensorDirection to keep sensor and motor in phase.
* @see SetSensorDirection
* However this routine is helpful for reversing the motor direction
* when Talon is in slave mode, or when using a single-direction position
* sensor in a closed-loop mode.
*
* @param reverseOutput True if motor output should be flipped; False if not.
*/
void CANTalon::SetClosedLoopOutputDirection(bool reverseOutput) {
CTR_Code status = m_impl->SetRevMotDuringCloseLoopEn(reverseOutput ? 1 : 0);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Returns the current error in the controller.
*
* @return the difference between the setpoint and the sensor value.
*/
int CANTalon::GetClosedLoopError() const {
int error;
/* retrieve the closed loop error in native units */
CTR_Code status = m_impl->GetCloseLoopErr(error);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return error;
}
/**
* Set the allowable closed loop error.
* @param allowableCloseLoopError allowable closed loop error for selected profile.
* mA for Curent closed loop.
* Talon Native Units for position and velocity.
*/
void CANTalon::SetAllowableClosedLoopErr(uint32_t allowableCloseLoopError)
{
/* grab param enum */
CanTalonSRX::param_t param;
if (m_profile == 1) {
param = CanTalonSRX::eProfileParamSlot1_AllowableClosedLoopErr;
} else {
param = CanTalonSRX::eProfileParamSlot0_AllowableClosedLoopErr;
}
/* send allowable close loop er in native units */
ConfigSetParameter(param, allowableCloseLoopError);
}
/**
* TODO documentation (see CANJaguar.cpp)
*
* @returns The speed of the sensor currently providing feedback.
*
* The speed units will be in the sensor's native ticks per 100ms.
*
* For analog sensors, 3.3V corresponds to 1023 units.
* So a speed of 200 equates to ~0.645 dV per 100ms or 6.451 dV per
* second.
* If this is an analog encoder, that likely means 1.9548 rotations
* per sec.
* For quadrature encoders, each unit corresponds a quadrature edge (4X).
* So a 250 count encoder will produce 1000 edge events per
* rotation.
* An example speed of 200 would then equate to 20% of a rotation
* per 100ms,
* or 10 rotations per second.
*/
double CANTalon::GetSpeed() const {
int32_t speed;
CTR_Code status = m_impl->GetSensorVelocity(speed);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return ScaleNativeUnitsToRpm(m_feedbackDevice, speed);
}
/**
* Get the position of whatever is in the analog pin of the Talon, regardless of
* whether it is actually being used for feedback.
*
* @returns The 24bit analog value. The bottom ten bits is the ADC (0 - 1023)
* on the analog pin of the Talon.
* The upper 14 bits tracks the overflows and
* underflows (continuous sensor).
*/
int CANTalon::GetAnalogIn() const {
int position;
CTR_Code status = m_impl->GetAnalogInWithOv(position);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return position;
}
void CANTalon::SetAnalogPosition(int newPosition) {
CTR_Code status = m_impl->SetParam(CanTalonSRX::eAinPosition, newPosition);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Get the position of whatever is in the analog pin of the Talon, regardless of
* whether it is actually being used for feedback.
*
* @returns The ADC (0 - 1023) on analog pin of the Talon.
*/
int CANTalon::GetAnalogInRaw() const { return GetAnalogIn() & 0x3FF; }
/**
* Get the position of whatever is in the analog pin of the Talon, regardless of
* whether it is actually being used for feedback.
*
* @returns The value (0 - 1023) on the analog pin of the Talon.
*/
int CANTalon::GetAnalogInVel() const {
int vel;
CTR_Code status = m_impl->GetAnalogInVel(vel);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return vel;
}
/**
* Get the position of whatever is in the analog pin of the Talon, regardless of
* whether it is actually being used for feedback.
*
* @returns The value (0 - 1023) on the analog pin of the Talon.
*/
int CANTalon::GetEncPosition() const {
int position;
CTR_Code status = m_impl->GetEncPosition(position);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return position;
}
void CANTalon::SetEncPosition(int newPosition) {
CTR_Code status = m_impl->SetParam(CanTalonSRX::eEncPosition, newPosition);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Get the position of whatever is in the analog pin of the Talon, regardless of
* whether it is actually being used for feedback.
*
* @returns The value (0 - 1023) on the analog pin of the Talon.
*/
int CANTalon::GetEncVel() const {
int vel;
CTR_Code status = m_impl->GetEncVel(vel);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return vel;
}
int CANTalon::GetPulseWidthPosition() const {
int param;
CTR_Code status = m_impl->GetPulseWidthPosition(param);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
return param;
}
void CANTalon::SetPulseWidthPosition(int newPosition)
{
CTR_Code status = m_impl->SetParam(CanTalonSRX::ePwdPosition, newPosition);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
int CANTalon::GetPulseWidthVelocity()const
{
int param;
CTR_Code status = m_impl->GetPulseWidthVelocity(param);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
return param;
}
int CANTalon::GetPulseWidthRiseToFallUs()const
{
int param;
CTR_Code status = m_impl->GetPulseWidthRiseToFallUs(param);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
return param;
}
int CANTalon::GetPulseWidthRiseToRiseUs()const
{
int param;
CTR_Code status = m_impl->GetPulseWidthRiseToRiseUs(param);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
return param;
}
/**
* @param which feedback sensor to check it if is connected.
* @return status of caller's specified sensor type.
*/
CANTalon::FeedbackDeviceStatus CANTalon::IsSensorPresent(FeedbackDevice feedbackDevice)const
{
FeedbackDeviceStatus retval = FeedbackStatusUnknown;
int param;
/* detecting sensor health depends on which sensor caller cares about */
switch (feedbackDevice) {
case QuadEncoder:
case AnalogPot:
case AnalogEncoder:
case EncRising:
case EncFalling:
/* no real good way to tell if these sensor
are actually present so return status unknown. */
break;
case PulseWidth:
case CtreMagEncoder_Relative:
case CtreMagEncoder_Absolute:
/* all of these require pulse width signal to be present. */
CTR_Code status = m_impl->IsPulseWidthSensorPresent(param);
if (status != CTR_OKAY) {
/* we're not getting status info, signal unknown status */
} else {
/* param is updated */
if (param) {
/* pulse signal is present, sensor must be working since it always
generates a pulse waveform.*/
retval = FeedbackStatusPresent;
} else {
/* no pulse present, sensor disconnected */
retval = FeedbackStatusNotPresent;
}
}
break;
}
return retval;
}
/**
* @return IO level of QUADA pin.
*/
int CANTalon::GetPinStateQuadA() const {
int retval;
CTR_Code status = m_impl->GetQuadApin(retval);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return retval;
}
/**
* @return IO level of QUADB pin.
*/
int CANTalon::GetPinStateQuadB() const {
int retval;
CTR_Code status = m_impl->GetQuadBpin(retval);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return retval;
}
/**
* @return IO level of QUAD Index pin.
*/
int CANTalon::GetPinStateQuadIdx() const {
int retval;
CTR_Code status = m_impl->GetQuadIdxpin(retval);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return retval;
}
/**
* @return '1' iff forward limit switch is closed, 0 iff switch is open.
* This function works regardless if limit switch feature is enabled.
*/
int CANTalon::IsFwdLimitSwitchClosed() const {
int retval;
CTR_Code status = m_impl->GetLimitSwitchClosedFor(
retval); /* rename this func, '1' => open, '0' => closed */
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return retval ? 0 : 1;
}
/**
* @return '1' iff reverse limit switch is closed, 0 iff switch is open.
* This function works regardless if limit switch feature is enabled.
*/
int CANTalon::IsRevLimitSwitchClosed() const {
int retval;
CTR_Code status = m_impl->GetLimitSwitchClosedRev(
retval); /* rename this func, '1' => open, '0' => closed */
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return retval ? 0 : 1;
}
/*
* Simple accessor for tracked rise eventso index pin.
* @return number of rising edges on idx pin.
*/
int CANTalon::GetNumberOfQuadIdxRises() const {
int rises;
CTR_Code status = m_impl->GetEncIndexRiseEvents(
rises); /* rename this func, '1' => open, '0' => closed */
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return rises;
}
/*
* @param rises integral value to set into index-rises register. Great way to
* zero the index count.
*/
void CANTalon::SetNumberOfQuadIdxRises(int rises) {
CTR_Code status = m_impl->SetParam(
CanTalonSRX::eEncIndexRiseEvents,
rises); /* rename this func, '1' => open, '0' => closed */
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
bool CANTalon::GetForwardLimitOK() const {
int limSwit = 0;
int softLim = 0;
CTR_Code status = CTR_OKAY;
status = m_impl->GetFault_ForSoftLim(softLim);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->GetFault_ForLim(limSwit);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* If either fault is asserted, signal caller we are disabled (with false?) */
return (softLim | limSwit) ? false : true;
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
bool CANTalon::GetReverseLimitOK() const {
int limSwit = 0;
int softLim = 0;
CTR_Code status = CTR_OKAY;
status = m_impl->GetFault_RevSoftLim(softLim);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->GetFault_RevLim(limSwit);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* If either fault is asserted, signal caller we are disabled (with false?) */
return (softLim | limSwit) ? false : true;
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
uint16_t CANTalon::GetFaults() const {
uint16_t retval = 0;
int val;
CTR_Code status = CTR_OKAY;
/* temperature */
val = 0;
status = m_impl->GetFault_OverTemp(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kTemperatureFault : 0;
/* voltage */
val = 0;
status = m_impl->GetFault_UnderVoltage(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kBusVoltageFault : 0;
/* fwd-limit-switch */
val = 0;
status = m_impl->GetFault_ForLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kFwdLimitSwitch : 0;
/* rev-limit-switch */
val = 0;
status = m_impl->GetFault_RevLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kRevLimitSwitch : 0;
/* fwd-soft-limit */
val = 0;
status = m_impl->GetFault_ForSoftLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kFwdSoftLimit : 0;
/* rev-soft-limit */
val = 0;
status = m_impl->GetFault_RevSoftLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kRevSoftLimit : 0;
return retval;
}
uint16_t CANTalon::GetStickyFaults() const {
uint16_t retval = 0;
int val;
CTR_Code status = CTR_OKAY;
/* temperature */
val = 0;
status = m_impl->GetStckyFault_OverTemp(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kTemperatureFault : 0;
/* voltage */
val = 0;
status = m_impl->GetStckyFault_UnderVoltage(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kBusVoltageFault : 0;
/* fwd-limit-switch */
val = 0;
status = m_impl->GetStckyFault_ForLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kFwdLimitSwitch : 0;
/* rev-limit-switch */
val = 0;
status = m_impl->GetStckyFault_RevLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kRevLimitSwitch : 0;
/* fwd-soft-limit */
val = 0;
status = m_impl->GetStckyFault_ForSoftLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kFwdSoftLimit : 0;
/* rev-soft-limit */
val = 0;
status = m_impl->GetStckyFault_RevSoftLim(val);
if (status != CTR_OKAY)
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval |= (val) ? CANSpeedController::kRevSoftLimit : 0;
return retval;
}
void CANTalon::ClearStickyFaults() {
CTR_Code status = m_impl->ClearStickyFaults();
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/**
* Set the maximum voltage change rate. This ramp rate is in affect regardless
* of which control mode
* the TALON is in.
*
* When in PercentVbus or Voltage output mode, the rate at which the voltage
* changes can
* be limited to reduce current spikes. Set this to 0.0 to disable rate
* limiting.
*
* @param rampRate The maximum rate of voltage change in Percent Voltage mode in
* V/s.
*/
void CANTalon::SetVoltageRampRate(double rampRate) {
/* Caller is expressing ramp as Voltage per sec, assuming 12V is full.
Talon's throttle ramp is in dThrot/d10ms. 1023 is full fwd, -1023 is
full rev. */
double rampRatedThrotPer10ms = (rampRate * 1023.0 / 12.0) / 100;
CTR_Code status = m_impl->SetRampThrottle((int)rampRatedThrotPer10ms);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
void CANTalon::SetVoltageCompensationRampRate(double rampRate) {
/* when in voltage compensation mode, the voltage compensation rate
directly caps the change in target voltage */
CTR_Code status = CTR_OKAY;
status = m_impl->SetVoltageCompensationRate(rampRate / 1000);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Sets a voltage change rate that applies only when a close loop contorl mode
* is enabled.
* This allows close loop specific ramp behavior.
*
* @param rampRate The maximum rate of voltage change in Percent Voltage mode in
* V/s.
*/
void CANTalon::SetCloseLoopRampRate(double rampRate) {
CTR_Code status = m_impl->SetCloseLoopRampRate(
m_profile, rampRate * 1023.0 / 12.0 / 1000.0);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* @return The version of the firmware running on the Talon
*/
uint32_t CANTalon::GetFirmwareVersion() const {
int firmwareVersion;
CTR_Code status = m_impl->RequestParam(CanTalonSRX::eFirmVers);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs);
status =
m_impl->GetParamResponseInt32(CanTalonSRX::eFirmVers, firmwareVersion);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* only sent once on boot */
// CTR_Code status = m_impl->GetFirmVers(firmwareVersion);
// if (status != CTR_OKAY) {
// wpi_setErrorWithContext(status, getHALErrorMessage(status));
//}
return firmwareVersion;
}
/**
* @return The accumulator for I gain.
*/
int CANTalon::GetIaccum() const {
CTR_Code status = m_impl->RequestParam(CanTalonSRX::ePidIaccum);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
usleep(kDelayForSolicitedSignalsUs); /* small yield for getting response */
int iaccum;
status = m_impl->GetParamResponseInt32(CanTalonSRX::ePidIaccum, iaccum);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return iaccum;
}
/**
* Clear the accumulator for I gain.
*/
void CANTalon::ClearIaccum() {
CTR_Code status = m_impl->SetParam(CanTalonSRX::ePidIaccum, 0);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::ConfigNeutralMode(NeutralMode mode) {
CTR_Code status = CTR_OKAY;
switch (mode) {
default:
case kNeutralMode_Jumper: /* use default setting in flash based on
webdash/BrakeCal button selection */
status = m_impl->SetOverrideBrakeType(
CanTalonSRX::kBrakeOverride_UseDefaultsFromFlash);
break;
case kNeutralMode_Brake:
status = m_impl->SetOverrideBrakeType(
CanTalonSRX::kBrakeOverride_OverrideBrake);
break;
case kNeutralMode_Coast:
status = m_impl->SetOverrideBrakeType(
CanTalonSRX::kBrakeOverride_OverrideCoast);
break;
}
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* @return nonzero if brake is enabled during neutral. Zero if coast is enabled
* during neutral.
*/
int CANTalon::GetBrakeEnableDuringNeutral() const {
int brakeEn = 0;
CTR_Code status = m_impl->GetBrakeIsEnabled(brakeEn);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
return brakeEn;
}
/**
* Configure how many codes per revolution are generated by your encoder.
*
* @param codesPerRev The number of counts per revolution.
*/
void CANTalon::ConfigEncoderCodesPerRev(uint16_t codesPerRev) {
/* first save the scalar so that all getters/setter work as the user expects */
m_codesPerRev = codesPerRev;
/* next send the scalar to the Talon over CAN. This is so that the Talon can report
it to whoever needs it, like the webdash. Don't bother checking the return,
this is only for instrumentation and is not necessary for Talon functionality. */
(void)m_impl->SetParam(CanTalonSRX::eNumberEncoderCPR, m_codesPerRev);
}
/**
* Configure the number of turns on the potentiometer.
*
* @param turns The number of turns of the potentiometer.
*/
void CANTalon::ConfigPotentiometerTurns(uint16_t turns) {
/* first save the scalar so that all getters/setter work as the user expects */
m_numPotTurns = turns;
/* next send the scalar to the Talon over CAN. This is so that the Talon can report
it to whoever needs it, like the webdash. Don't bother checking the return,
this is only for instrumentation and is not necessary for Talon functionality. */
(void)m_impl->SetParam(CanTalonSRX::eNumberPotTurns, m_numPotTurns);
}
/**
* @deprecated not implemented
*/
void CANTalon::ConfigSoftPositionLimits(double forwardLimitPosition,
double reverseLimitPosition) {
ConfigLimitMode(kLimitMode_SoftPositionLimits);
ConfigForwardLimit(forwardLimitPosition);
ConfigReverseLimit(reverseLimitPosition);
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::DisableSoftPositionLimits() {
ConfigLimitMode(kLimitMode_SwitchInputsOnly);
}
/**
* TODO documentation (see CANJaguar.cpp)
* Configures the soft limit enable (wear leveled persistent memory).
* Also sets the limit switch overrides.
*/
void CANTalon::ConfigLimitMode(LimitMode mode) {
CTR_Code status = CTR_OKAY;
switch (mode) {
case kLimitMode_SwitchInputsOnly: /** Only use switches for limits */
/* turn OFF both limits. SRX has individual enables and polarity for each
* limit switch.*/
status = m_impl->SetForwardSoftEnable(false);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->SetReverseSoftEnable(false);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* override enable the limit switches, this circumvents the webdash */
status = m_impl->SetOverrideLimitSwitchEn(
CanTalonSRX::kLimitSwitchOverride_EnableFwd_EnableRev);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
break;
case kLimitMode_SoftPositionLimits: /** Use both switches and soft limits */
/* turn on both limits. SRX has individual enables and polarity for each
* limit switch.*/
status = m_impl->SetForwardSoftEnable(true);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->SetReverseSoftEnable(true);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* override enable the limit switches, this circumvents the webdash */
status = m_impl->SetOverrideLimitSwitchEn(
CanTalonSRX::kLimitSwitchOverride_EnableFwd_EnableRev);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
break;
case kLimitMode_SrxDisableSwitchInputs: /** disable both limit switches and
soft limits */
/* turn on both limits. SRX has individual enables and polarity for each
* limit switch.*/
status = m_impl->SetForwardSoftEnable(false);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
status = m_impl->SetReverseSoftEnable(false);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* override enable the limit switches, this circumvents the webdash */
status = m_impl->SetOverrideLimitSwitchEn(
CanTalonSRX::kLimitSwitchOverride_DisableFwd_DisableRev);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
break;
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::ConfigForwardLimit(double forwardLimitPosition) {
CTR_Code status = CTR_OKAY;
int32_t nativeLimitPos = ScaleRotationsToNativeUnits(m_feedbackDevice, forwardLimitPosition);
status = m_impl->SetForwardSoftLimit(nativeLimitPos);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Change the fwd limit switch setting to normally open or closed.
* Talon will disable momentarilly if the Talon's current setting
* is dissimilar to the caller's requested setting.
*
* Since Talon saves setting to flash this should only affect
* a given Talon initially during robot install.
*
* @param normallyOpen true for normally open. false for normally closed.
*/
void CANTalon::ConfigFwdLimitSwitchNormallyOpen(bool normallyOpen) {
CTR_Code status =
m_impl->SetParam(CanTalonSRX::eOnBoot_LimitSwitch_Forward_NormallyClosed,
normallyOpen ? 0 : 1);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* Change the rev limit switch setting to normally open or closed.
* Talon will disable momentarilly if the Talon's current setting
* is dissimilar to the caller's requested setting.
*
* Since Talon saves setting to flash this should only affect
* a given Talon initially during robot install.
*
* @param normallyOpen true for normally open. false for normally closed.
*/
void CANTalon::ConfigRevLimitSwitchNormallyOpen(bool normallyOpen) {
CTR_Code status =
m_impl->SetParam(CanTalonSRX::eOnBoot_LimitSwitch_Reverse_NormallyClosed,
normallyOpen ? 0 : 1);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::ConfigReverseLimit(double reverseLimitPosition) {
CTR_Code status = CTR_OKAY;
int32_t nativeLimitPos = ScaleRotationsToNativeUnits(m_feedbackDevice, reverseLimitPosition);
status = m_impl->SetReverseSoftLimit(nativeLimitPos);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::ConfigMaxOutputVoltage(double voltage) {
/* config peak throttle when in closed-loop mode in the fwd and rev direction. */
ConfigPeakOutputVoltage(voltage, -voltage);
}
void CANTalon::ConfigPeakOutputVoltage(double forwardVoltage,double reverseVoltage) {
/* bounds checking */
if (forwardVoltage > 12)
forwardVoltage = 12;
else if (forwardVoltage < 0)
forwardVoltage = 0;
if (reverseVoltage > 0)
reverseVoltage = 0;
else if (reverseVoltage < -12)
reverseVoltage = -12;
/* config calls */
ConfigSetParameter(CanTalonSRX::ePeakPosOutput, 1023 * forwardVoltage / 12.0);
ConfigSetParameter(CanTalonSRX::ePeakNegOutput, 1023 * reverseVoltage / 12.0);
}
void CANTalon::ConfigNominalOutputVoltage(double forwardVoltage,double reverseVoltage) {
/* bounds checking */
if (forwardVoltage > 12)
forwardVoltage = 12;
else if (forwardVoltage < 0)
forwardVoltage = 0;
if (reverseVoltage > 0)
reverseVoltage = 0;
else if (reverseVoltage < -12)
reverseVoltage = -12;
/* config calls */
ConfigSetParameter(CanTalonSRX::eNominalPosOutput,1023*forwardVoltage/12.0);
ConfigSetParameter(CanTalonSRX::eNominalNegOutput,1023*reverseVoltage/12.0);
}
/**
* General set frame. Since the parameter is a general integral type, this can
* be used for testing future features.
*/
void CANTalon::ConfigSetParameter(uint32_t paramEnum, double value) {
CTR_Code status;
/* config peak throttle when in closed-loop mode in the positive direction. */
status = m_impl->SetParam((CanTalonSRX::param_t)paramEnum,value);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* General get frame. Since the parameter is a general integral type, this can
* be used for testing future features.
*/
bool CANTalon::GetParameter(uint32_t paramEnum, double & dvalue) const {
bool retval = true;
/* send the request frame */
CTR_Code status = m_impl->RequestParam((CanTalonSRX::param_t)paramEnum);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval = false;
}
/* small yield for getting response */
usleep(kDelayForSolicitedSignalsUs);
/* get the last received update */
status = m_impl->GetParamResponse((CanTalonSRX::param_t)paramEnum, dvalue);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
retval = false;
}
return retval;
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::ConfigFaultTime(float faultTime) {
/* SRX does not have fault time. SRX motor drive is only disabled for soft
* limits and limit-switch faults. */
wpi_setWPIErrorWithContext(IncompatibleMode, "Fault Time not supported.");
}
/**
* Fixup the sendMode so Set() serializes the correct demand value.
* Also fills the modeSelecet in the control frame to disabled.
* @param mode Control mode to ultimately enter once user calls Set().
* @see Set()
*/
void CANTalon::ApplyControlMode(CANSpeedController::ControlMode mode) {
m_controlMode = mode;
HALReport(HALUsageReporting::kResourceType_CANTalonSRX, m_deviceNumber + 1,
mode);
switch (mode) {
case kPercentVbus:
m_sendMode = kThrottle;
break;
case kCurrent:
m_sendMode = kCurrentMode;
break;
case kSpeed:
m_sendMode = kSpeedMode;
break;
case kPosition:
m_sendMode = kPositionMode;
break;
case kVoltage:
m_sendMode = kVoltageMode;
break;
case kFollower:
m_sendMode = kFollowerMode;
break;
}
// Keep the talon disabled until Set() is called.
CTR_Code status = m_impl->SetModeSelect((int)kDisabled);
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
void CANTalon::SetControlMode(CANSpeedController::ControlMode mode) {
if (m_controlMode == mode) {
/* we already are in this mode, don't perform disable workaround */
} else {
ApplyControlMode(mode);
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
CANSpeedController::ControlMode CANTalon::GetControlMode() const {
return m_controlMode;
}
void CANTalon::SetExpiration(float timeout) {
m_safetyHelper->SetExpiration(timeout);
}
float CANTalon::GetExpiration() const {
return m_safetyHelper->GetExpiration();
}
bool CANTalon::IsAlive() const { return m_safetyHelper->IsAlive(); }
bool CANTalon::IsSafetyEnabled() const {
return m_safetyHelper->IsSafetyEnabled();
}
void CANTalon::SetSafetyEnabled(bool enabled) {
m_safetyHelper->SetSafetyEnabled(enabled);
}
void CANTalon::GetDescription(std::ostringstream& desc) const {
desc << "CANTalon ID " << m_deviceNumber;
}
/**
* @param devToLookup FeedbackDevice to lookup the scalar for. Because Talon
* allows multiple sensors to be attached simultaneously, caller must
* specify which sensor to lookup.
* @return The number of native Talon units per rotation of the selected sensor.
* Zero if the necessary sensor information is not available.
* @see ConfigEncoderCodesPerRev
* @see ConfigPotentiometerTurns
*/
double CANTalon::GetNativeUnitsPerRotationScalar(FeedbackDevice devToLookup)const
{
bool scalingAvail = false;
CTR_Code status = CTR_OKAY;
double retval = 0;
switch (devToLookup) {
case QuadEncoder:
{ /* When caller wants to lookup Quadrature, the QEI may be in 1x if the selected feedback is edge counter.
* Additionally if the quadrature source is the CTRE Mag encoder, then the CPR is known.
* This is nice in that the calling app does not require knowing the CPR at all.
* So do both checks here.
*/
int32_t qeiPulsePerCount = 4; /* default to 4x */
switch (m_feedbackDevice) {
case CtreMagEncoder_Relative:
case CtreMagEncoder_Absolute:
/* we assume the quadrature signal comes from the MagEnc,
of which we know the CPR already */
retval = kNativePwdUnitsPerRotation;
scalingAvail = true;
break;
case EncRising: /* Talon's QEI is setup for 1x, so perform 1x math */
case EncFalling:
qeiPulsePerCount = 1;
break;
case QuadEncoder: /* Talon's QEI is 4x */
default: /* pulse width and everything else, assume its regular quad use. */
break;
}
if (scalingAvail) {
/* already deduced the scalar above, we're done. */
} else {
/* we couldn't deduce the scalar just based on the selection */
if (0 == m_codesPerRev) {
/* caller has never set the CPR. Most likely caller
is just using engineering units so fall to the
bottom of this func.*/
} else {
/* Talon expects PPR units */
retval = qeiPulsePerCount * m_codesPerRev;
scalingAvail = true;
}
}
} break;
case EncRising:
case EncFalling:
if (0 == m_codesPerRev) {
/* caller has never set the CPR. Most likely caller
is just using engineering units so fall to the
bottom of this func.*/
} else {
/* Talon expects PPR units */
retval = 1 * m_codesPerRev;
scalingAvail = true;
}
break;
case AnalogPot:
case AnalogEncoder:
if (0 == m_numPotTurns) {
/* caller has never set the CPR. Most likely caller
is just using engineering units so fall to the
bottom of this func.*/
} else {
retval = (double)kNativeAdcUnitsPerRotation / m_numPotTurns;
scalingAvail = true;
}
break;
case CtreMagEncoder_Relative:
case CtreMagEncoder_Absolute:
case PulseWidth:
retval = kNativePwdUnitsPerRotation;
scalingAvail = true;
break;
}
/* handle any detected errors */
if (status != CTR_OKAY) {
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/* if scaling information is not possible, signal caller
by returning zero */
if (false == scalingAvail)
retval = 0;
return retval;
}
/**
* @param fullRotations double precision value representing number of rotations of selected feedback sensor.
* If user has never called the config routine for the selected sensor, then the caller
* is likely passing rotations in engineering units already, in which case it is returned
* as is.
* @see ConfigPotentiometerTurns
* @see ConfigEncoderCodesPerRev
* @return fullRotations in native engineering units of the Talon SRX firmware.
*/
int32_t CANTalon::ScaleRotationsToNativeUnits(FeedbackDevice devToLookup,double fullRotations)const
{
/* first assume we don't have config info, prep the default return */
int32_t retval = (int32_t)fullRotations;
/* retrieve scaling info */
double scalar = GetNativeUnitsPerRotationScalar(devToLookup);
/* apply scalar if its available */
if (scalar > 0)
retval = (int32_t)(fullRotations*scalar);
return retval;
}
/**
* @param rpm double precision value representing number of rotations per minute of selected feedback sensor.
* If user has never called the config routine for the selected sensor, then the caller
* is likely passing rotations in engineering units already, in which case it is returned
* as is.
* @see ConfigPotentiometerTurns
* @see ConfigEncoderCodesPerRev
* @return sensor velocity in native engineering units of the Talon SRX firmware.
*/
int32_t CANTalon::ScaleVelocityToNativeUnits(FeedbackDevice devToLookup,double rpm)const
{
/* first assume we don't have config info, prep the default return */
int32_t retval = (int32_t)rpm;
/* retrieve scaling info */
double scalar = GetNativeUnitsPerRotationScalar(devToLookup);
/* apply scalar if its available */
if (scalar > 0)
retval = (int32_t)(rpm * kMinutesPer100msUnit * scalar);
return retval;
}
/**
* @param nativePos integral position of the feedback sensor in native Talon SRX units.
* If user has never called the config routine for the selected sensor, then the return
* will be in TALON SRX units as well to match the behavior in the 2015 season.
* @see ConfigPotentiometerTurns
* @see ConfigEncoderCodesPerRev
* @return double precision number of rotations, unless config was never performed.
*/
double CANTalon::ScaleNativeUnitsToRotations(FeedbackDevice devToLookup,int32_t nativePos)const
{
/* first assume we don't have config info, prep the default return */
double retval = (double)nativePos;
/* retrieve scaling info */
double scalar = GetNativeUnitsPerRotationScalar(devToLookup);
/* apply scalar if its available */
if (scalar > 0)
retval = ((double)nativePos) / scalar;
return retval;
}
/**
* @param nativeVel integral velocity of the feedback sensor in native Talon SRX units.
* If user has never called the config routine for the selected sensor, then the return
* will be in TALON SRX units as well to match the behavior in the 2015 season.
* @see ConfigPotentiometerTurns
* @see ConfigEncoderCodesPerRev
* @return double precision of sensor velocity in RPM, unless config was never performed.
*/
double CANTalon::ScaleNativeUnitsToRpm(FeedbackDevice devToLookup, int32_t nativeVel)const
{
/* first assume we don't have config info, prep the default return */
double retval = (double)nativeVel;
/* retrieve scaling info */
double scalar = GetNativeUnitsPerRotationScalar(devToLookup);
/* apply scalar if its available */
if (scalar > 0)
retval = (double)(nativeVel) / (scalar*kMinutesPer100msUnit);
return retval;
}
/**
* Enables Talon SRX to automatically zero the Sensor Position whenever an
* edge is detected on the index signal.
* @param enable boolean input, pass true to enable feature or false to disable.
* @param risingEdge boolean input, pass true to clear the position on rising edge,
* pass false to clear the position on falling edge.
*/
void CANTalon::EnableZeroSensorPositionOnIndex(bool enable, bool risingEdge)
{
if (enable) {
/* enable the feature, update the edge polarity first to ensure
it is correct before the feature is enabled. */
ConfigSetParameter(CanTalonSRX::eQuadIdxPolarity,risingEdge ? 1 : 0);
ConfigSetParameter(CanTalonSRX::eClearPositionOnIdx,1);
} else {
/* disable the feature first, then update the edge polarity. */
ConfigSetParameter(CanTalonSRX::eClearPositionOnIdx,0);
ConfigSetParameter(CanTalonSRX::eQuadIdxPolarity,risingEdge ? 1 : 0);
}
}
/**
* Common interface for inverting direction of a speed controller.
* Only works in PercentVbus, speed, and Voltage modes.
* @param isInverted The state of inversion, true is inverted.
*/
void CANTalon::SetInverted(bool isInverted) { m_isInverted = isInverted; }
/**
* Common interface for the inverting direction of a speed controller.
*
* @return isInverted The state of inversion, true is inverted.
*
*/
bool CANTalon::GetInverted() const { return m_isInverted; }
/**
* Common interface for stopping the motor
* Part of the MotorSafety interface
*
* @deprecated Call Disable instead.
*/
void CANTalon::StopMotor() { Disable(); }
void CANTalon::ValueChanged(ITable* source, llvm::StringRef key,
std::shared_ptr<nt::Value> value, bool isNew) {
if(key == "Mode" && value->IsDouble()) SetControlMode(static_cast<CANSpeedController::ControlMode>(value->GetDouble()));
if(key == "p" && value->IsDouble()) SetP(value->GetDouble());
if(key == "i" && value->IsDouble()) SetI(value->GetDouble());
if(key == "d" && value->IsDouble()) SetD(value->GetDouble());
if(key == "f" && value->IsDouble()) SetF(value->GetDouble());
if(key == "Enabled" && value->IsBoolean()) {
if (value->GetBoolean()) {
Enable();
} else {
Disable();
}
}
if(key == "Value" && value->IsDouble()) Set(value->GetDouble());
}
bool CANTalon::IsModePID(CANSpeedController::ControlMode mode) const {
return mode == kCurrent || mode == kSpeed || mode == kPosition;
}
void CANTalon::UpdateTable() {
if (m_table != nullptr) {
m_table->PutString("~TYPE~", "CANSpeedController");
m_table->PutString("Type", "CANTalon");
m_table->PutString("Mode", GetModeName(m_controlMode));
m_table->PutNumber("p", GetP());
m_table->PutNumber("i", GetI());
m_table->PutNumber("d", GetD());
m_table->PutNumber("f", GetF());
m_table->PutBoolean("Enabled", IsControlEnabled());
m_table->PutNumber("Value", Get());
}
}
void CANTalon::StartLiveWindowMode() {
if (m_table != nullptr) {
m_table->AddTableListener(this, true);
}
}
void CANTalon::StopLiveWindowMode() {
if (m_table != nullptr) {
m_table->RemoveTableListener(this);
}
}
std::string CANTalon::GetSmartDashboardType() const {
return "CANSpeedController";
}
void CANTalon::InitTable(std::shared_ptr<ITable> subTable) {
m_table = subTable;
UpdateTable();
}
std::shared_ptr<ITable> CANTalon::GetTable() const { return m_table; }