001/*
002 * Copyright (C) Cross The Road Electronics.  All rights reserved.
003 * License information can be found in CTRE_LICENSE.txt
004 * For support and suggestions contact support@ctr-electronics.com or file
005 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
006 */
007package com.ctre.phoenix6.mechanisms.swerve;
008
009import static edu.wpi.first.units.Units.Volts;
010
011import com.ctre.phoenix6.StatusCode;
012import com.ctre.phoenix6.controls.VoltageOut;
013import com.ctre.phoenix6.mechanisms.swerve.utility.LegacyPhoenixPIDController;
014
015import edu.wpi.first.math.geometry.Pose2d;
016import edu.wpi.first.math.geometry.Rotation2d;
017import edu.wpi.first.math.geometry.Translation2d;
018import edu.wpi.first.math.kinematics.ChassisSpeeds;
019import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
020import edu.wpi.first.math.kinematics.SwerveModuleState;
021import edu.wpi.first.units.measure.MutVoltage;
022import edu.wpi.first.units.measure.Voltage;
023
024/**
025 * Container for all the Swerve Requests. Use this to find all applicable swerve
026 * drive requests.
027 * <p>
028 * This is also an interface common to all swerve drive control requests that
029 * allow the request to calculate the state to apply to the modules.
030 */
031public interface LegacySwerveRequest {
032
033    /**
034     * The reference for "forward" is sometimes different if you're talking
035     * about field relative. This addresses which forward to use.
036     */
037    public enum ForwardReference {
038        /**
039         * This forward reference makes it so "forward" (positive X) is always towards the red alliance.
040         * This is important in situations such as path following where positive X is always towards the
041         * red alliance wall, regardless of where the operator physically are located.
042         */
043        RedAlliance,
044        /**
045         * This forward references makes it so "forward" (positive X) is determined from the operator's
046         * perspective. This is important for most teleop driven field-centric requests, where positive
047         * X really means to drive away from the operator.
048         * <p>
049         * <b>Important</b>: Users must specify the OperatorPerspective with {@link LegacySwerveDrivetrain} object
050         */
051        OperatorPerspective
052    }
053
054    /*
055     * Contains everything the control requests need to calculate the module state.
056     */
057    public class LegacySwerveControlRequestParameters {
058        public double maxSpeedMps;
059        public SwerveDriveKinematics kinematics;
060        public ChassisSpeeds currentChassisSpeed;
061        public Pose2d currentPose;
062        public double timestamp;
063        public Translation2d[] swervePositions;
064        public Rotation2d operatorForwardDirection;
065        public double updatePeriod;
066    }
067
068    /**
069     * Applies this swerve request to the given modules.
070     * This is typically called by the SwerveDrivetrain.
071     *
072     * @param parameters Parameters the control request needs to calculate the module state
073     * @param modulesToApply Modules to which the control request is applied
074     * @return Status code of sending the request
075     */
076    public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply);
077
078    /**
079     * Sets the swerve drive module states to point inward on the
080     * robot in an "X" fashion, creating a natural brake which will
081     * oppose any motion.
082     */
083    public class SwerveDriveBrake implements LegacySwerveRequest {
084
085        /**
086         * The type of control request to use for the drive motor.
087         */
088        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
089        /**
090         * The type of control request to use for the steer motor.
091         */
092        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
093
094        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
095
096            for (int i = 0; i < modulesToApply.length; ++i) {
097                SwerveModuleState state = new SwerveModuleState(0, parameters.swervePositions[i].getAngle());
098                modulesToApply[i].apply(state, DriveRequestType, SteerRequestType);
099            }
100
101            return StatusCode.OK;
102        }
103
104        /**
105         * Sets the type of control request to use for the drive motor.
106         *
107         * @param driveRequestType The type of control request to use for the drive motor
108         * @return this request
109         */
110        public SwerveDriveBrake withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
111            this.DriveRequestType = driveRequestType;
112            return this;
113        }
114        /**
115         * Sets the type of control request to use for the steer motor.
116         *
117         * @param steerRequestType The type of control request to use for the steer motor
118         * @return this request
119         */
120        public SwerveDriveBrake withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
121            this.SteerRequestType = steerRequestType;
122            return this;
123        }
124    }
125
126    /**
127     * Drives the swerve drivetrain in a field-centric manner.
128     * <p>
129     * When users use this request, they specify the direction the robot should
130     * travel oriented against the field, and the rate at which their robot should
131     * rotate about the center of the robot.
132     * <p>
133     * An example scenario is that the robot is oriented to the east, the
134     * VelocityX is +5 m/s, VelocityY is 0 m/s, and RotationRate is 0.5 rad/s.
135     * In this scenario, the robot would drive northward at 5 m/s and turn
136     * counterclockwise at 0.5 rad/s.
137     */
138    public class FieldCentric implements LegacySwerveRequest {
139        /**
140         * The velocity in the X direction, in m/s.
141         * X is defined as forward according to WPILib convention,
142         * so this determines how fast to travel forward.
143         */
144        public double VelocityX = 0;
145        /**
146         * The velocity in the Y direction, in m/s.
147         * Y is defined as to the left according to WPILib convention,
148         * so this determines how fast to travel to the left.
149         */
150        public double VelocityY = 0;
151        /**
152         * The angular rate to rotate at, in radians per second.
153         * Angular rate is defined as counterclockwise positive,
154         * so this determines how fast to turn counterclockwise.
155         */
156        public double RotationalRate = 0;
157        /**
158         * The allowable deadband of the request.
159         */
160        public double Deadband = 0;
161        /**
162         * The rotational deadband of the request.
163         */
164        public double RotationalDeadband = 0;
165        /**
166         * The center of rotation the robot should rotate around.
167         * This is (0,0) by default, which will rotate around the center of the robot.
168         */
169        public Translation2d CenterOfRotation = new Translation2d();
170
171        /**
172         * The type of control request to use for the drive motor.
173         */
174        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
175        /**
176         * The type of control request to use for the steer motor.
177         */
178        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
179        /**
180         * Whether to desaturate wheel speeds before applying.
181         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
182         */
183        public boolean DesaturateWheelSpeeds = true;
184
185        /**
186         * The perspective to use when determining which direction is forward.
187         */
188        public ForwardReference ForwardReference = LegacySwerveRequest.ForwardReference.OperatorPerspective;
189
190        /**
191         * The last applied state in case we don't have anything to drive.
192         */
193        protected SwerveModuleState[] m_lastAppliedState = null;
194
195        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
196            double toApplyX = VelocityX;
197            double toApplyY = VelocityY;
198            if (ForwardReference == LegacySwerveRequest.ForwardReference.OperatorPerspective) {
199                /* If we're operator perspective, modify the X/Y translation by the angle */
200                Translation2d tmp = new Translation2d(toApplyX, toApplyY);
201                tmp = tmp.rotateBy(parameters.operatorForwardDirection);
202                toApplyX = tmp.getX();
203                toApplyY = tmp.getY();
204            }
205            double toApplyOmega = RotationalRate;
206            if (Math.sqrt(toApplyX * toApplyX + toApplyY * toApplyY) < Deadband) {
207                toApplyX = 0;
208                toApplyY = 0;
209            }
210            if (Math.abs(toApplyOmega) < RotationalDeadband) {
211                toApplyOmega = 0;
212            }
213
214            ChassisSpeeds speeds = ChassisSpeeds.discretize(
215                ChassisSpeeds.fromFieldRelativeSpeeds(
216                    toApplyX, toApplyY, toApplyOmega,
217                    parameters.currentPose.getRotation()
218                ),
219                parameters.updatePeriod
220            );
221
222            var states = parameters.kinematics.toSwerveModuleStates(speeds, CenterOfRotation);
223            if (DesaturateWheelSpeeds && parameters.maxSpeedMps > 0.0) {
224                SwerveDriveKinematics.desaturateWheelSpeeds(states, parameters.maxSpeedMps);
225            }
226
227            for (int i = 0; i < modulesToApply.length; ++i) {
228                modulesToApply[i].apply(states[i], DriveRequestType, SteerRequestType);
229            }
230
231            return StatusCode.OK;
232        }
233
234        /**
235         * Sets the velocity in the X direction, in m/s.
236         * X is defined as forward according to WPILib convention,
237         * so this determines how fast to travel forward.
238         *
239         * @param velocityX Velocity in the X direction, in m/s
240         * @return this request
241         */
242        public FieldCentric withVelocityX(double velocityX) {
243            this.VelocityX = velocityX;
244            return this;
245        }
246
247        /**
248         * Sets the velocity in the Y direction, in m/s.
249         * Y is defined as to the left according to WPILib convention,
250         * so this determines how fast to travel to the left.
251         *
252         * @param velocityY Velocity in the Y direction, in m/s
253         * @return this request
254         */
255        public FieldCentric withVelocityY(double velocityY) {
256            this.VelocityY = velocityY;
257            return this;
258        }
259
260        /**
261         * The angular rate to rotate at, in radians per second.
262         * Angular rate is defined as counterclockwise positive,
263         * so this determines how fast to turn counterclockwise.
264         *
265         * @param rotationalRate Angular rate to rotate at, in radians per second
266         * @return this request
267         */
268        public FieldCentric withRotationalRate(double rotationalRate) {
269            this.RotationalRate = rotationalRate;
270            return this;
271        }
272
273        /**
274         * Sets the allowable deadband of the request.
275         *
276         * @param deadband Allowable deadband of the request
277         * @return this request
278         */
279        public FieldCentric withDeadband(double deadband) {
280            this.Deadband = deadband;
281            return this;
282        }
283        /**
284         * Sets the rotational deadband of the request.
285         *
286         * @param rotationalDeadband Rotational deadband of the request
287         * @return this request
288         */
289        public FieldCentric withRotationalDeadband(double rotationalDeadband) {
290            this.RotationalDeadband = rotationalDeadband;
291            return this;
292        }
293        /**
294         * Sets the center of rotation of the request
295         *
296         * @param centerOfRotation The center of rotation the robot should rotate around.
297         * @return this request
298         */
299        public FieldCentric withCenterOfRotation(Translation2d centerOfRotation) {
300            this.CenterOfRotation = centerOfRotation;
301            return this;
302        }
303
304        /**
305         * Sets the type of control request to use for the drive motor.
306         *
307         * @param driveRequestType The type of control request to use for the drive motor
308         * @return this request
309         */
310        public FieldCentric withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
311            this.DriveRequestType = driveRequestType;
312            return this;
313        }
314        /**
315         * Sets the type of control request to use for the steer motor.
316         *
317         * @param steerRequestType The type of control request to use for the steer motor
318         * @return this request
319         */
320        public FieldCentric withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
321            this.SteerRequestType = steerRequestType;
322            return this;
323        }
324
325        /**
326         * Sets whether to desaturate wheel speeds before applying.
327         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
328         *
329         * @param desaturateWheelSpeeds Whether to desaturate wheel speeds
330         * @return this request
331         */
332        public FieldCentric withDesaturateWheelSpeeds(boolean desaturateWheelSpeeds) {
333            this.DesaturateWheelSpeeds = desaturateWheelSpeeds;
334            return this;
335        }
336    }
337
338    /**
339     * Drives the swerve drivetrain in a field-centric manner, maintaining a
340     * specified heading angle to ensure the robot is facing the desired direction
341     * <p>
342     * When users use this request, they specify the direction the robot should
343     * travel oriented against the field, and the direction the robot should be facing.
344     * <p>
345     * An example scenario is that the robot is oriented to the east, the VelocityX
346     * is +5 m/s, VelocityY is 0 m/s, and TargetDirection is 180 degrees.
347     * In this scenario, the robot would drive northward at 5 m/s and turn clockwise
348     * to a target of 180 degrees.
349     * <p>
350     * This control request is especially useful for autonomous control, where the
351     * robot should be facing a changing direction throughout the motion.
352     */
353    public class FieldCentricFacingAngle implements LegacySwerveRequest {
354        /**
355         * The velocity in the X direction, in m/s.
356         * X is defined as forward according to WPILib convention,
357         * so this determines how fast to travel forward.
358         */
359        public double VelocityX = 0;
360        /**
361         * The velocity in the Y direction, in m/s.
362         * Y is defined as to the left according to WPILib convention,
363         * so this determines how fast to travel to the left.
364         */
365        public double VelocityY = 0;
366        /**
367         * The desired direction to face.
368         * 0 Degrees is defined as in the direction of the X axis.
369         * As a result, a TargetDirection of 90 degrees will point along
370         * the Y axis, or to the left.
371         */
372        public Rotation2d TargetDirection = new Rotation2d();
373
374        /**
375         * The allowable deadband of the request.
376         */
377        public double Deadband = 0;
378        /**
379         * The rotational deadband of the request.
380         */
381        public double RotationalDeadband = 0;
382        /**
383         * The center of rotation the robot should rotate around.
384         * This is (0,0) by default, which will rotate around the center of the robot.
385         */
386        public Translation2d CenterOfRotation = new Translation2d();
387
388        /**
389         * The type of control request to use for the drive motor.
390         */
391        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
392        /**
393         * The type of control request to use for the steer motor.
394         */
395        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
396        /**
397         * Whether to desaturate wheel speeds before applying.
398         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
399         */
400        public boolean DesaturateWheelSpeeds = true;
401
402        /**
403         * The PID controller used to maintain the desired heading.
404         * Users can specify the PID gains to change how aggressively to maintain
405         * heading.
406         * <p>
407         * This PID controller operates on heading radians and outputs a target
408         * rotational rate in radians per second.
409         */
410        public LegacyPhoenixPIDController HeadingController = new LegacyPhoenixPIDController(0, 0, 0);
411
412        /**
413         * The perspective to use when determining which direction is forward.
414         */
415        public ForwardReference ForwardReference = LegacySwerveRequest.ForwardReference.OperatorPerspective;
416
417        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
418            double toApplyX = VelocityX;
419            double toApplyY = VelocityY;
420            Rotation2d angleToFace = TargetDirection;
421            if (ForwardReference == LegacySwerveRequest.ForwardReference.OperatorPerspective) {
422                /* If we're operator perspective, modify the X/Y translation by the angle */
423                Translation2d tmp = new Translation2d(toApplyX, toApplyY);
424                tmp = tmp.rotateBy(parameters.operatorForwardDirection);
425                toApplyX = tmp.getX();
426                toApplyY = tmp.getY();
427                /* And rotate the direction we want to face by the angle */
428                angleToFace = angleToFace.rotateBy(parameters.operatorForwardDirection);
429            }
430
431            double rotationRate = HeadingController.calculate(parameters.currentPose.getRotation().getRadians(),
432                    angleToFace.getRadians(), parameters.timestamp);
433
434            double toApplyOmega = rotationRate;
435            if (Math.sqrt(toApplyX * toApplyX + toApplyY * toApplyY) < Deadband) {
436                toApplyX = 0;
437                toApplyY = 0;
438            }
439            if (Math.abs(toApplyOmega) < RotationalDeadband) {
440                toApplyOmega = 0;
441            }
442
443            ChassisSpeeds speeds = ChassisSpeeds.discretize(
444                ChassisSpeeds.fromFieldRelativeSpeeds(
445                    toApplyX, toApplyY, toApplyOmega,
446                    parameters.currentPose.getRotation()
447                ),
448                parameters.updatePeriod
449            );
450
451            var states = parameters.kinematics.toSwerveModuleStates(speeds, CenterOfRotation);
452            if (DesaturateWheelSpeeds && parameters.maxSpeedMps > 0.0) {
453                SwerveDriveKinematics.desaturateWheelSpeeds(states, parameters.maxSpeedMps);
454            }
455
456            for (int i = 0; i < modulesToApply.length; ++i) {
457                modulesToApply[i].apply(states[i], DriveRequestType, SteerRequestType);
458            }
459
460            return StatusCode.OK;
461        }
462
463        /**
464         * Sets the velocity in the X direction, in m/s.
465         * X is defined as forward according to WPILib convention,
466         * so this determines how fast to travel forward.
467         *
468         * @param velocityX Velocity in the X direction, in m/s
469         * @return this request
470         */
471        public FieldCentricFacingAngle withVelocityX(double velocityX) {
472            this.VelocityX = velocityX;
473            return this;
474        }
475
476        /**
477         * Sets the velocity in the Y direction, in m/s.
478         * Y is defined as to the left according to WPILib convention,
479         * so this determines how fast to travel to the left.
480         *
481         * @param velocityY Velocity in the Y direction, in m/s
482         * @return this request
483         */
484        public FieldCentricFacingAngle withVelocityY(double velocityY) {
485            this.VelocityY = velocityY;
486            return this;
487        }
488
489        /**
490         * Sets the desired direction to face.
491         * 0 Degrees is defined as in the direction of the X axis.
492         * As a result, a TargetDirection of 90 degrees will point along
493         * the Y axis, or to the left.
494         *
495         * @param targetDirection Desired direction to face
496         * @return this request
497         */
498        public FieldCentricFacingAngle withTargetDirection(Rotation2d targetDirection) {
499            this.TargetDirection = targetDirection;
500            return this;
501        }
502
503        /**
504         * Sets the allowable deadband of the request.
505         *
506         * @param deadband Allowable deadband of the request
507         * @return this request
508         */
509        public FieldCentricFacingAngle withDeadband(double deadband) {
510            this.Deadband = deadband;
511            return this;
512        }
513        /**
514         * Sets the rotational deadband of the request.
515         *
516         * @param rotationalDeadband Rotational deadband of the request
517         * @return this request
518         */
519        public FieldCentricFacingAngle withRotationalDeadband(double rotationalDeadband) {
520            this.RotationalDeadband = rotationalDeadband;
521            return this;
522        }
523        /**
524         * Sets the center of rotation of the request
525         *
526         * @param centerOfRotation The center of rotation the robot should rotate around.
527         * @return this request
528         */
529        public FieldCentricFacingAngle withCenterOfRotation(Translation2d centerOfRotation) {
530            this.CenterOfRotation = centerOfRotation;
531            return this;
532        }
533
534        /**
535         * Sets the type of control request to use for the drive motor.
536         *
537         * @param driveRequestType The type of control request to use for the drive motor
538         * @return this request
539         */
540        public FieldCentricFacingAngle withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
541            this.DriveRequestType = driveRequestType;
542            return this;
543        }
544        /**
545         * Sets the type of control request to use for the steer motor.
546         *
547         * @param steerRequestType The type of control request to use for the steer motor
548         * @return this request
549         */
550        public FieldCentricFacingAngle withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
551            this.SteerRequestType = steerRequestType;
552            return this;
553        }
554
555        /**
556         * Sets whether to desaturate wheel speeds before applying.
557         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
558         *
559         * @param desaturateWheelSpeeds Whether to desaturate wheel speeds
560         * @return this request
561         */
562        public FieldCentricFacingAngle withDesaturateWheelSpeeds(boolean desaturateWheelSpeeds) {
563            this.DesaturateWheelSpeeds = desaturateWheelSpeeds;
564            return this;
565        }
566    }
567
568    /**
569     * Does nothing to the swerve module state. This is the default state of a newly
570     * created swerve drive mechanism.
571     */
572    public class Idle implements LegacySwerveRequest {
573        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
574            return StatusCode.OK;
575        }
576    }
577
578    /**
579     * Sets the swerve drive modules to point to a specified direction.
580     */
581    public class PointWheelsAt implements LegacySwerveRequest {
582
583        /**
584         * The direction to point the modules toward.
585         * This direction is still optimized to what the module was previously at.
586         */
587        public Rotation2d ModuleDirection = new Rotation2d();
588        /**
589         * The type of control request to use for the drive motor.
590         */
591        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
592        /**
593         * The type of control request to use for the steer motor.
594         */
595        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
596
597        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
598
599            for (int i = 0; i < modulesToApply.length; ++i) {
600                SwerveModuleState state = new SwerveModuleState(0, ModuleDirection);
601                modulesToApply[i].apply(state, DriveRequestType, SteerRequestType);
602            }
603
604            return StatusCode.OK;
605        }
606
607        /**
608         * Sets the direction to point the modules toward.
609         * This direction is still optimized to what the module was previously at.
610         *
611         * @param moduleDirection Direction to point the modules toward
612         * @return this request
613         */
614        public PointWheelsAt withModuleDirection(Rotation2d moduleDirection) {
615            this.ModuleDirection = moduleDirection;
616            return this;
617        }
618
619        /**
620         * Sets the type of control request to use for the drive motor.
621         *
622         * @param driveRequestType The type of control request to use for the drive motor
623         * @return this request
624         */
625        public PointWheelsAt withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
626            this.DriveRequestType = driveRequestType;
627            return this;
628        }
629        /**
630         * Sets the type of control request to use for the steer motor.
631         *
632         * @param steerRequestType The type of control request to use for the steer motor
633         * @return this request
634         */
635        public PointWheelsAt withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
636            this.SteerRequestType = steerRequestType;
637            return this;
638        }
639    }
640
641    /**
642     * Drives the swerve drivetrain in a robot-centric manner.
643     * <p>
644     * When users use this request, they specify the direction the robot should
645     * travel oriented against the robot itself, and the rate at which their
646     * robot should rotate about the center of the robot.
647     * <p>
648     * An example scenario is that the robot is oriented to the east, the
649     * VelocityX is +5 m/s, VelocityY is 0 m/s, and RotationRate is 0.5 rad/s.
650     * In this scenario, the robot would drive eastward at 5 m/s and turn
651     * counterclockwise at 0.5 rad/s.
652     */
653    public class RobotCentric implements LegacySwerveRequest {
654        /**
655         * The velocity in the X direction, in m/s.
656         * X is defined as forward according to WPILib convention,
657         * so this determines how fast to travel forward.
658         */
659        public double VelocityX = 0;
660        /**
661         * The velocity in the Y direction, in m/s.
662         * Y is defined as to the left according to WPILib convention,
663         * so this determines how fast to travel to the left.
664         */
665        public double VelocityY = 0;
666        /**
667         * The angular rate to rotate at, in radians per second.
668         * Angular rate is defined as counterclockwise positive,
669         * so this determines how fast to turn counterclockwise.
670         */
671        public double RotationalRate = 0;
672
673        /**
674         * The allowable deadband of the request.
675         */
676        public double Deadband = 0;
677        /**
678         * The rotational deadband of the request.
679         */
680        public double RotationalDeadband = 0;
681        /**
682         * The center of rotation the robot should rotate around.
683         * This is (0,0) by default, which will rotate around the center of the robot.
684         */
685        public Translation2d CenterOfRotation = new Translation2d();
686
687        /**
688         * The type of control request to use for the drive motor.
689         */
690        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
691        /**
692         * The type of control request to use for the steer motor.
693         */
694        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
695        /**
696         * Whether to desaturate wheel speeds before applying.
697         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
698         */
699        public boolean DesaturateWheelSpeeds = true;
700
701        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
702            double toApplyX = VelocityX;
703            double toApplyY = VelocityY;
704            double toApplyOmega = RotationalRate;
705            if (Math.sqrt(toApplyX * toApplyX + toApplyY * toApplyY) < Deadband) {
706                toApplyX = 0;
707                toApplyY = 0;
708            }
709            if (Math.abs(toApplyOmega) < RotationalDeadband) {
710                toApplyOmega = 0;
711            }
712            ChassisSpeeds speeds = new ChassisSpeeds(toApplyX, toApplyY, toApplyOmega);
713
714            var states = parameters.kinematics.toSwerveModuleStates(speeds, CenterOfRotation);
715            if (DesaturateWheelSpeeds && parameters.maxSpeedMps > 0.0) {
716                SwerveDriveKinematics.desaturateWheelSpeeds(states, parameters.maxSpeedMps);
717            }
718
719            for (int i = 0; i < modulesToApply.length; ++i) {
720                modulesToApply[i].apply(states[i], DriveRequestType, SteerRequestType);
721            }
722
723            return StatusCode.OK;
724        }
725
726        /**
727         * Sets the velocity in the X direction, in m/s.
728         * X is defined as forward according to WPILib convention,
729         * so this determines how fast to travel forward.
730         *
731         * @param velocityX Velocity in the X direction, in m/s
732         * @return this request
733         */
734        public RobotCentric withVelocityX(double velocityX) {
735            this.VelocityX = velocityX;
736            return this;
737        }
738
739        /**
740         * Sets the velocity in the Y direction, in m/s.
741         * Y is defined as to the left according to WPILib convention,
742         * so this determines how fast to travel to the left.
743         *
744         * @param velocityY Velocity in the Y direction, in m/s
745         * @return this request
746         */
747        public RobotCentric withVelocityY(double velocityY) {
748            this.VelocityY = velocityY;
749            return this;
750        }
751
752        /**
753         * The angular rate to rotate at, in radians per second.
754         * Angular rate is defined as counterclockwise positive,
755         * so this determines how fast to turn counterclockwise.
756         *
757         * @param rotationalRate Angular rate to rotate at, in radians per second
758         * @return this request
759         */
760        public RobotCentric withRotationalRate(double rotationalRate) {
761            this.RotationalRate = rotationalRate;
762            return this;
763        }
764
765        /**
766         * Sets the allowable deadband of the request.
767         *
768         * @param deadband Allowable deadband of the request
769         * @return this request
770         */
771        public RobotCentric withDeadband(double deadband) {
772            this.Deadband = deadband;
773            return this;
774        }
775        /**
776         * Sets the rotational deadband of the request.
777         *
778         * @param rotationalDeadband Rotational deadband of the request
779         * @return this request
780         */
781        public RobotCentric withRotationalDeadband(double rotationalDeadband) {
782            this.RotationalDeadband = rotationalDeadband;
783            return this;
784        }
785        /**
786         * Sets the center of rotation of the request
787         *
788         * @param centerOfRotation The center of rotation the robot should rotate around.
789         * @return this request
790         */
791        public RobotCentric withCenterOfRotation(Translation2d centerOfRotation) {
792            this.CenterOfRotation = centerOfRotation;
793            return this;
794        }
795
796        /**
797         * Sets the type of control request to use for the drive motor.
798         *
799         * @param driveRequestType The type of control request to use for the drive motor
800         * @return this request
801         */
802        public RobotCentric withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
803            this.DriveRequestType = driveRequestType;
804            return this;
805        }
806        /**
807         * Sets the type of control request to use for the steer motor.
808         *
809         * @param steerRequestType The type of control request to use for the steer motor
810         * @return this request
811         */
812        public RobotCentric withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
813            this.SteerRequestType = steerRequestType;
814            return this;
815        }
816
817        /**
818         * Sets whether to desaturate wheel speeds before applying.
819         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
820         *
821         * @param desaturateWheelSpeeds Whether to desaturate wheel speeds
822         * @return this request
823         */
824        public RobotCentric withDesaturateWheelSpeeds(boolean desaturateWheelSpeeds) {
825            this.DesaturateWheelSpeeds = desaturateWheelSpeeds;
826            return this;
827        }
828    }
829
830    /**
831     * Accepts a generic ChassisSpeeds to apply to the drivetrain.
832     */
833    public class ApplyChassisSpeeds implements LegacySwerveRequest {
834
835        /**
836         * The chassis speeds to apply to the drivetrain.
837         */
838        public ChassisSpeeds Speeds = new ChassisSpeeds();
839        /**
840         * The center of rotation to rotate around.
841         */
842        public Translation2d CenterOfRotation = new Translation2d(0, 0);
843        /**
844         * The type of control request to use for the drive motor.
845         */
846        public LegacySwerveModule.DriveRequestType DriveRequestType = LegacySwerveModule.DriveRequestType.OpenLoopVoltage;
847        /**
848         * The type of control request to use for the steer motor.
849         */
850        public LegacySwerveModule.SteerRequestType SteerRequestType = LegacySwerveModule.SteerRequestType.MotionMagic;
851        /**
852         * Whether to desaturate wheel speeds before applying.
853         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
854         */
855        public boolean DesaturateWheelSpeeds = true;
856
857        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
858            var states = parameters.kinematics.toSwerveModuleStates(Speeds, CenterOfRotation);
859            if (DesaturateWheelSpeeds && parameters.maxSpeedMps > 0.0) {
860                SwerveDriveKinematics.desaturateWheelSpeeds(states, parameters.maxSpeedMps);
861            }
862
863            for (int i = 0; i < modulesToApply.length; ++i) {
864                modulesToApply[i].apply(states[i], DriveRequestType, SteerRequestType);
865            }
866
867            return StatusCode.OK;
868        }
869
870        /**
871         * Sets the chassis speeds to apply to the drivetrain.
872         *
873         * @param speeds Chassis speeds to apply to the drivetrain
874         * @return this request
875         */
876        public ApplyChassisSpeeds withSpeeds(ChassisSpeeds speeds) {
877            this.Speeds = speeds;
878            return this;
879        }
880        /**
881         * Sets the center of rotation to rotate around.
882         *
883         * @param centerOfRotation Center of rotation to rotate around
884         * @return this request
885         */
886        public ApplyChassisSpeeds withCenterOfRotation(Translation2d centerOfRotation) {
887            this.CenterOfRotation = centerOfRotation;
888            return this;
889        }
890
891        /**
892         * Sets the type of control request to use for the drive motor.
893         *
894         * @param driveRequestType The type of control request to use for the drive motor
895         * @return this request
896         */
897        public ApplyChassisSpeeds withDriveRequestType(LegacySwerveModule.DriveRequestType driveRequestType) {
898            this.DriveRequestType = driveRequestType;
899            return this;
900        }
901        /**
902         * Sets the type of control request to use for the steer motor.
903         *
904         * @param steerRequestType The type of control request to use for the steer motor
905         * @return this request
906         */
907        public ApplyChassisSpeeds withSteerRequestType(LegacySwerveModule.SteerRequestType steerRequestType) {
908            this.SteerRequestType = steerRequestType;
909            return this;
910        }
911
912        /**
913         * Sets whether to desaturate wheel speeds before applying.
914         * For more information, see the documentation of {@link SwerveDriveKinematics#desaturateWheelSpeeds}.
915         *
916         * @param desaturateWheelSpeeds Whether to desaturate wheel speeds
917         * @return this request
918         */
919        public ApplyChassisSpeeds withDesaturateWheelSpeeds(boolean desaturateWheelSpeeds) {
920            this.DesaturateWheelSpeeds = desaturateWheelSpeeds;
921            return this;
922        }
923    }
924
925    /**
926     * SysId-specific SwerveRequest to characterize the translational
927     * characteristics of a swerve drivetrain.
928     */
929    public class SysIdSwerveTranslation implements LegacySwerveRequest {
930        /**
931         * Voltage to apply to drive wheels. This is final to enforce mutating the value.
932         */
933        public final MutVoltage VoltsToApply = Volts.mutable(0);
934
935        /* Local reference to a voltage request to drive the motors with */
936        private VoltageOut m_voltRequest = new VoltageOut(0);
937
938        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
939            for (int i = 0; i < modulesToApply.length; ++i) {
940                modulesToApply[i].applyCharacterization(Rotation2d.fromDegrees(0), m_voltRequest.withOutput(VoltsToApply.in(Volts)));
941            }
942            return StatusCode.OK;
943        }
944
945        /**
946         * Sets the voltage to apply to the drive wheels.
947         *
948         * @param volts Voltage to apply
949         * @return this request
950         */
951        public SysIdSwerveTranslation withVolts(Voltage volts) {
952            VoltsToApply.mut_replace(volts);
953            return this;
954        }
955    }
956
957    /**
958     * SysId-specific SwerveRequest to characterize the rotational
959     * characteristics of a swerve drivetrain.
960     */
961    public class SysIdSwerveRotation implements LegacySwerveRequest {
962        /**
963         * Voltage to apply to drive wheels. This is final to enforce mutating the value.
964         */
965        public final MutVoltage VoltsToApply = Volts.mutable(0);
966
967        /* Local reference to a voltage request to drive the motors with */
968        private VoltageOut m_voltRequest = new VoltageOut(0);
969
970        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
971            for (int i = 0; i < modulesToApply.length; ++i) {
972                modulesToApply[i].applyCharacterization(parameters.swervePositions[i].getAngle().plus(Rotation2d.fromDegrees(90)),
973                                                        m_voltRequest.withOutput(VoltsToApply.in(Volts)));
974            }
975            return StatusCode.OK;
976        }
977
978        /**
979         * Update the voltage to apply to the drive wheels.
980         *
981         * @param volts Voltage to apply
982         * @return this request
983         */
984        public SysIdSwerveRotation withVolts(Voltage volts) {
985            VoltsToApply.mut_replace(volts);
986            return this;
987        }
988    }
989
990    /**
991     * SysId-specific SwerveRequest to characterize the steer module
992     * characteristics of a swerve drivetrain.
993     */
994    public class SysIdSwerveSteerGains implements LegacySwerveRequest {
995        /**
996         * Voltage to apply to steer motors. This is final to enforce mutating the value.
997         */
998        public final MutVoltage VoltsToApply = Volts.mutable(0);
999
1000        /* Local reference to a voltage request to drive the motors with */
1001        private VoltageOut m_voltRequest = new VoltageOut(0);
1002
1003        public StatusCode apply(LegacySwerveControlRequestParameters parameters, LegacySwerveModule... modulesToApply) {
1004            for (int i = 0; i < modulesToApply.length; ++i) {
1005                modulesToApply[i].getSteerMotor().setControl(m_voltRequest.withOutput(VoltsToApply.in(Volts)));
1006                modulesToApply[i].getDriveMotor().setControl(m_voltRequest.withOutput(0));
1007            }
1008            return StatusCode.OK;
1009        }
1010
1011        /**
1012         * Sets the voltage to apply to the steer motors.
1013         *
1014         * @param volts Voltage to apply
1015         * @return this request
1016         */
1017        public SysIdSwerveSteerGains withVolts(Voltage volts) {
1018            VoltsToApply.mut_replace(volts);
1019            return this;
1020        }
1021    }
1022}