2023-02-13 17:21:24 -06:00
|
|
|
package swervelib.encoders;
|
2023-02-13 14:37:05 -06:00
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
import static edu.wpi.first.units.Units.DegreesPerSecond;
|
2024-12-17 18:49:55 +00:00
|
|
|
import static edu.wpi.first.units.Units.Milliseconds;
|
2024-12-09 23:26:04 +00:00
|
|
|
import static edu.wpi.first.units.Units.Rotations;
|
2024-12-17 18:49:55 +00:00
|
|
|
import static edu.wpi.first.units.Units.Seconds;
|
2024-12-09 23:26:04 +00:00
|
|
|
|
2024-01-15 14:37:13 -06:00
|
|
|
import com.ctre.phoenix6.StatusCode;
|
|
|
|
|
import com.ctre.phoenix6.StatusSignal;
|
|
|
|
|
import com.ctre.phoenix6.configs.CANcoderConfiguration;
|
|
|
|
|
import com.ctre.phoenix6.configs.CANcoderConfigurator;
|
|
|
|
|
import com.ctre.phoenix6.hardware.CANcoder;
|
|
|
|
|
import com.ctre.phoenix6.signals.MagnetHealthValue;
|
|
|
|
|
import com.ctre.phoenix6.signals.SensorDirectionValue;
|
2024-12-09 23:26:04 +00:00
|
|
|
import edu.wpi.first.units.measure.Angle;
|
2024-12-17 18:49:55 +00:00
|
|
|
import edu.wpi.first.units.measure.AngularVelocity;
|
2024-12-09 23:26:04 +00:00
|
|
|
import edu.wpi.first.wpilibj.Alert;
|
|
|
|
|
import edu.wpi.first.wpilibj.Alert.AlertType;
|
2023-02-13 14:37:05 -06:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Swerve Absolute Encoder for CTRE CANCoders.
|
|
|
|
|
*/
|
|
|
|
|
public class CANCoderSwerve extends SwerveAbsoluteEncoder
|
|
|
|
|
{
|
|
|
|
|
|
2024-02-02 18:55:29 -06:00
|
|
|
/**
|
|
|
|
|
* Wait time for status frames to show up.
|
|
|
|
|
*/
|
2025-02-22 06:15:56 +00:00
|
|
|
public static double STATUS_TIMEOUT_SECONDS = Milliseconds.of(1).in(Seconds);
|
2024-01-17 09:17:39 -06:00
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if the CANCoder magnet field is less than ideal.
|
|
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final Alert magnetFieldLessThanIdeal;
|
2024-01-17 09:17:39 -06:00
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if the CANCoder reading is faulty.
|
|
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final Alert readingFaulty;
|
2024-01-17 09:17:39 -06:00
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if the CANCoder reading is faulty and the reading is ignored.
|
|
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final Alert readingIgnored;
|
2024-01-17 09:17:39 -06:00
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if the absolute encoder offset cannot be set.
|
|
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final Alert cannotSetOffset;
|
|
|
|
|
/**
|
|
|
|
|
* Magnet Health status signal for the CANCoder.
|
|
|
|
|
*/
|
|
|
|
|
private final StatusSignal<MagnetHealthValue> magnetHealth;
|
|
|
|
|
/**
|
|
|
|
|
* CANCoder reading cache.
|
|
|
|
|
*/
|
|
|
|
|
private final StatusSignal<Angle> angle;
|
|
|
|
|
/**
|
|
|
|
|
* Angular velocity of the {@link CANcoder}.
|
|
|
|
|
*/
|
|
|
|
|
private final StatusSignal<AngularVelocity> velocity;
|
2025-01-06 15:44:15 +00:00
|
|
|
/**
|
|
|
|
|
* CANCoder with WPILib sendable and support.
|
|
|
|
|
*/
|
|
|
|
|
public CANcoder encoder;
|
2024-12-17 18:49:55 +00:00
|
|
|
/**
|
|
|
|
|
* {@link CANcoder} Configurator objet for this class.
|
|
|
|
|
*/
|
|
|
|
|
private CANcoderConfigurator config;
|
|
|
|
|
/**
|
|
|
|
|
* {@link CANcoderConfiguration} object for the CANcoder.
|
|
|
|
|
*/
|
|
|
|
|
private CANcoderConfiguration cfg = new CANcoderConfiguration();
|
2023-02-13 14:37:05 -06:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the CANCoder on the standard CANBus.
|
|
|
|
|
*
|
|
|
|
|
* @param id CAN ID.
|
|
|
|
|
*/
|
|
|
|
|
public CANCoderSwerve(int id)
|
|
|
|
|
{
|
2024-02-02 18:55:29 -06:00
|
|
|
// Empty string uses the default canbus for the system
|
|
|
|
|
this(id, "");
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the CANCoder on the CANivore.
|
|
|
|
|
*
|
2024-12-17 18:49:55 +00:00
|
|
|
* @param id CAN ID of the {@link CANcoder}.
|
|
|
|
|
* @param canbus CAN bus to initialize it on. Should be "rio" or "" if the RIO CANbus, else is the CANivore name.
|
2023-02-13 14:37:05 -06:00
|
|
|
*/
|
|
|
|
|
public CANCoderSwerve(int id, String canbus)
|
|
|
|
|
{
|
2024-01-15 14:37:13 -06:00
|
|
|
encoder = new CANcoder(id, canbus);
|
2024-12-17 18:49:55 +00:00
|
|
|
config = encoder.getConfigurator();
|
|
|
|
|
magnetHealth = encoder.getMagnetHealth();
|
|
|
|
|
angle = encoder.getAbsolutePosition();
|
|
|
|
|
velocity = encoder.getVelocity();
|
2024-01-17 09:17:39 -06:00
|
|
|
magnetFieldLessThanIdeal = new Alert(
|
|
|
|
|
"Encoders",
|
|
|
|
|
"CANCoder " + encoder.getDeviceID() + " magnetic field is less than ideal.",
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2024-01-17 09:17:39 -06:00
|
|
|
readingFaulty = new Alert(
|
|
|
|
|
"Encoders",
|
|
|
|
|
"CANCoder " + encoder.getDeviceID() + " reading was faulty.",
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2024-01-17 09:17:39 -06:00
|
|
|
readingIgnored = new Alert(
|
|
|
|
|
"Encoders",
|
|
|
|
|
"CANCoder " + encoder.getDeviceID() + " reading was faulty, ignoring.",
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2024-01-17 09:17:39 -06:00
|
|
|
cannotSetOffset = new Alert(
|
|
|
|
|
"Encoders",
|
|
|
|
|
"Failure to set CANCoder "
|
|
|
|
|
+ encoder.getDeviceID()
|
|
|
|
|
+ " Absolute Encoder Offset",
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|
|
|
|
|
|
2025-02-22 06:15:56 +00:00
|
|
|
@Override
|
|
|
|
|
public void close()
|
|
|
|
|
{
|
|
|
|
|
encoder.close();
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
/**
|
|
|
|
|
* Reset the encoder to factory defaults.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void factoryDefault()
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
cfg = new CANcoderConfiguration();
|
|
|
|
|
config.apply(cfg);
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear sticky faults on the encoder.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void clearStickyFaults()
|
|
|
|
|
{
|
|
|
|
|
encoder.clearStickyFaults();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the absolute encoder to read from [0, 360) per second.
|
|
|
|
|
*
|
|
|
|
|
* @param inverted Whether the encoder is inverted.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void configure(boolean inverted)
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
config.refresh(cfg.MagnetSensor);
|
|
|
|
|
config.apply(cfg.MagnetSensor.withAbsoluteSensorDiscontinuityPoint(Rotations.of(1))
|
|
|
|
|
.withSensorDirection(inverted ? SensorDirectionValue.Clockwise_Positive
|
|
|
|
|
: SensorDirectionValue.CounterClockwise_Positive));
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|
|
|
|
|
|
2024-12-17 18:49:55 +00:00
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
/**
|
2023-02-20 20:59:31 -06:00
|
|
|
* Get the absolute position of the encoder. Sets {@link SwerveAbsoluteEncoder#readingError} on erroneous readings.
|
2023-02-13 14:37:05 -06:00
|
|
|
*
|
|
|
|
|
* @return Absolute position in degrees from [0, 360).
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public double getAbsolutePosition()
|
|
|
|
|
{
|
2023-02-20 20:59:31 -06:00
|
|
|
readingError = false;
|
2024-12-17 18:49:55 +00:00
|
|
|
MagnetHealthValue strength = magnetHealth.refresh().getValue();
|
2025-02-22 06:15:56 +00:00
|
|
|
angle.refresh();
|
2023-02-20 20:59:31 -06:00
|
|
|
|
2024-01-17 09:17:39 -06:00
|
|
|
magnetFieldLessThanIdeal.set(strength != MagnetHealthValue.Magnet_Green);
|
2024-01-15 14:37:13 -06:00
|
|
|
if (strength == MagnetHealthValue.Magnet_Invalid || strength == MagnetHealthValue.Magnet_Red)
|
2023-02-14 22:43:02 -06:00
|
|
|
{
|
2023-02-20 20:59:31 -06:00
|
|
|
readingError = true;
|
2024-01-17 09:17:39 -06:00
|
|
|
readingFaulty.set(true);
|
2023-02-20 20:59:31 -06:00
|
|
|
return 0;
|
2024-01-17 09:17:39 -06:00
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
readingFaulty.set(false);
|
2023-02-14 22:43:02 -06:00
|
|
|
}
|
2024-01-17 09:17:39 -06:00
|
|
|
|
2023-02-20 20:59:31 -06:00
|
|
|
// Taken from democat's library.
|
|
|
|
|
// Source: https://github.com/democat3457/swerve-lib/blob/7c03126b8c22f23a501b2c2742f9d173a5bcbc40/src/main/java/com/swervedrivespecialties/swervelib/ctre/CanCoderFactoryBuilder.java#L51-L74
|
2023-08-29 21:56:52 -05:00
|
|
|
for (int i = 0; i < maximumRetries; i++)
|
2023-02-20 20:59:31 -06:00
|
|
|
{
|
2024-01-15 14:37:13 -06:00
|
|
|
if (angle.getStatus() == StatusCode.OK)
|
2023-02-20 20:59:31 -06:00
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-12-17 18:49:55 +00:00
|
|
|
angle.waitForUpdate(STATUS_TIMEOUT_SECONDS);
|
2023-02-20 20:59:31 -06:00
|
|
|
}
|
2024-01-15 14:37:13 -06:00
|
|
|
if (angle.getStatus() != StatusCode.OK)
|
2023-02-20 20:59:31 -06:00
|
|
|
{
|
|
|
|
|
readingError = true;
|
2024-01-17 09:17:39 -06:00
|
|
|
readingIgnored.set(true);
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
readingIgnored.set(false);
|
2023-02-20 20:59:31 -06:00
|
|
|
}
|
2025-02-22 06:15:56 +00:00
|
|
|
// Convert from Rotations to Degrees.
|
|
|
|
|
return angle.getValueAsDouble() * 360;
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the instantiated absolute encoder Object.
|
|
|
|
|
*
|
|
|
|
|
* @return Absolute encoder object.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public Object getAbsoluteEncoder()
|
|
|
|
|
{
|
|
|
|
|
return encoder;
|
|
|
|
|
}
|
2023-12-05 16:25:42 -06:00
|
|
|
|
2023-12-12 10:48:54 -06:00
|
|
|
/**
|
2024-01-15 14:37:13 -06:00
|
|
|
* Sets the Absolute Encoder Offset within the CANcoder's Memory.
|
2023-12-12 10:48:54 -06:00
|
|
|
*
|
2024-01-15 14:37:13 -06:00
|
|
|
* @param offset the offset the Absolute Encoder uses as the zero point in degrees.
|
2023-12-12 10:48:54 -06:00
|
|
|
* @return if setting Absolute Encoder Offset was successful or not.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public boolean setAbsoluteEncoderOffset(double offset)
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
StatusCode error = config.refresh(cfg.MagnetSensor);
|
2024-01-15 14:37:13 -06:00
|
|
|
if (error != StatusCode.OK)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-12-17 18:49:55 +00:00
|
|
|
|
|
|
|
|
error = config.apply(cfg.MagnetSensor.withMagnetOffset(offset / 360));
|
2024-01-17 09:17:39 -06:00
|
|
|
cannotSetOffset.setText(
|
|
|
|
|
"Failure to set CANCoder "
|
|
|
|
|
+ encoder.getDeviceID()
|
|
|
|
|
+ " Absolute Encoder Offset Error: "
|
|
|
|
|
+ error);
|
2024-01-15 14:37:13 -06:00
|
|
|
if (error == StatusCode.OK)
|
2023-12-12 10:48:54 -06:00
|
|
|
{
|
2024-01-17 09:17:39 -06:00
|
|
|
cannotSetOffset.set(false);
|
2023-12-12 10:48:54 -06:00
|
|
|
return true;
|
|
|
|
|
}
|
2024-01-17 09:17:39 -06:00
|
|
|
cannotSetOffset.set(true);
|
2023-12-12 10:48:54 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-05 16:25:42 -06:00
|
|
|
/**
|
|
|
|
|
* Get the velocity in degrees/sec.
|
|
|
|
|
*
|
|
|
|
|
* @return velocity in degrees/sec.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public double getVelocity()
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
return velocity.refresh().getValue().in(DegreesPerSecond);
|
2023-12-05 16:25:42 -06:00
|
|
|
}
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|