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.swerve;
008
009import static org.wpilib.units.Units.*;
010
011import java.util.Optional;
012import java.util.concurrent.locks.Lock;
013import java.util.concurrent.locks.ReentrantLock;
014import java.util.function.Consumer;
015import java.util.function.IntSupplier;
016
017import com.ctre.phoenix6.CANBus;
018import com.ctre.phoenix6.StatusCode;
019import com.ctre.phoenix6.Utils;
020import com.ctre.phoenix6.hardware.ParentDevice;
021import com.ctre.phoenix6.hardware.Pigeon2;
022import com.ctre.phoenix6.hardware.traits.CommonDevice;
023import com.ctre.phoenix6.hardware.traits.CommonTalon;
024import com.ctre.phoenix6.signals.NeutralModeValue;
025import com.ctre.phoenix6.swerve.SwerveRequest.NativeSwerveRequest;
026import com.ctre.phoenix6.swerve.jni.SwerveJNI;
027
028import org.wpilib.driverstation.DriverStationErrors;
029import org.wpilib.math.geometry.Pose2d;
030import org.wpilib.math.geometry.Rotation2d;
031import org.wpilib.math.geometry.Rotation3d;
032import org.wpilib.math.geometry.Translation2d;
033import org.wpilib.math.kinematics.ChassisVelocities;
034import org.wpilib.math.kinematics.SwerveDriveKinematics;
035import org.wpilib.math.kinematics.SwerveModulePosition;
036import org.wpilib.math.kinematics.SwerveModuleVelocity;
037import org.wpilib.math.linalg.Matrix;
038import org.wpilib.math.numbers.N1;
039import org.wpilib.math.numbers.N3;
040import org.wpilib.units.measure.*;
041
042/**
043 * Swerve Drive class utilizing CTR Electronics' Phoenix 6 API.
044 * <p>
045 * This class handles the kinematics, configuration, and odometry of a
046 * swerve drive utilizing CTR Electronics devices. We recommend using
047 * the Swerve Project Generator in Tuner X to create a template project
048 * that demonstrates how to use this class.
049 * <p>
050 * This class will construct the hardware devices internally, so the user
051 * only specifies the constants (IDs, PID gains, gear ratios, etc).
052 * Getters for these hardware devices are available.
053 * <p>
054 * This class performs pose estimation internally using a separate odometry
055 * thread. Vision measurements can be added using {@link #addVisionMeasurement}.
056 * Other odometry APIs such as {@link #resetPose} are also available. The
057 * resulting pose estimate can be retrieved along with module states and
058 * other information using {@link #getState}. Additionally, the odometry
059 * thread synchronously provides all new state updates to a telemetry function
060 * registered with {@link #registerTelemetry}.
061 * <p>
062 * If using the generator, the order in which modules are constructed is
063 * Front Left, Front Right, Back Left, Back Right. This means if you need
064 * the Back Left module, call {@code getModule(2);} to get the third
065 * (0-indexed) module.
066 */
067public class SwerveDrivetrain<
068    DriveMotorT extends CommonTalon,
069    SteerMotorT extends CommonTalon,
070    EncoderT extends ParentDevice
071> implements AutoCloseable {
072    /**
073     * Functional interface for device constructors.
074     */
075    @FunctionalInterface
076    public static interface DeviceConstructor<DeviceT> {
077        DeviceT create(int id, CANBus canbus);
078    }
079
080    /**
081     * Plain-Old-Data class holding the state of the swerve drivetrain.
082     * This encapsulates most data that is relevant for telemetry or
083     * decision-making from the Swerve Drive.
084     */
085    public static class SwerveDriveState implements Cloneable {
086        /** The current pose of the robot */
087        public Pose2d Pose = new Pose2d();
088        /** The current robot-centric velocity */
089        public ChassisVelocities Velocity = new ChassisVelocities();
090        /** The current module positions */
091        public SwerveModulePosition[] ModulePositions;
092        /** The current module velocities */
093        public SwerveModuleVelocity[] ModuleVelocities;
094        /** The target module velocities */
095        public SwerveModuleVelocity[] ModuleTargets;
096        /** The raw heading of the robot, unaffected by vision updates and odometry resets */
097        public Rotation2d RawHeading = new Rotation2d();
098        /** The timestamp of the state capture, in the timebase of {@link Utils#getCurrentTimeSeconds()} */
099        public double Timestamp;
100        /** The measured odometry update period, in seconds */
101        public double OdometryPeriod;
102        /** Number of successful data acquisitions */
103        public int SuccessfulDaqs;
104        /** Number of failed data acquisitions */
105        public int FailedDaqs;
106
107        /**
108         * Creates a deep copy of this state object.
109         * This API is not thread-safe.
110         */
111        @Override
112        public SwerveDriveState clone() {
113            final var toReturn = new SwerveDriveState();
114            toReturn.Pose = Pose;
115            toReturn.Velocity = Velocity;
116            toReturn.ModulePositions = ModulePositions.clone();
117            toReturn.ModuleVelocities = ModuleVelocities.clone();
118            toReturn.ModuleTargets = ModuleTargets.clone();
119            toReturn.RawHeading = RawHeading;
120            toReturn.Timestamp = Timestamp;
121            toReturn.OdometryPeriod = OdometryPeriod;
122            toReturn.SuccessfulDaqs = SuccessfulDaqs;
123            toReturn.FailedDaqs = FailedDaqs;
124            return toReturn;
125        }
126
127        protected void updateFromJni(SwerveJNI.DriveState driveState) {
128            if (Pose.getX() != driveState.PoseX ||
129                Pose.getY() != driveState.PoseY ||
130                Pose.getRotation().getRadians() != driveState.PoseTheta)
131            {
132                Pose = new Pose2d(driveState.PoseX, driveState.PoseY, Rotation2d.fromRadians(driveState.PoseTheta));
133            }
134
135            if (Velocity.vx != driveState.VelocityVx ||
136                Velocity.vy != driveState.VelocityVy ||
137                Velocity.omega != driveState.VelocityOmega)
138            {
139                Velocity = new ChassisVelocities(driveState.VelocityVx, driveState.VelocityVy, driveState.VelocityOmega);
140            }
141
142            for (int i = 0; i < ModulePositions.length; ++i) {
143                if (ModulePositions[i].distance != driveState.ModulePositions[i].distance ||
144                    ModulePositions[i].angle.getRadians() != driveState.ModulePositions[i].angle)
145                {
146                    ModulePositions[i] = new SwerveModulePosition(driveState.ModulePositions[i].distance, Rotation2d.fromRadians(driveState.ModulePositions[i].angle));
147                }
148            }
149            for (int i = 0; i < ModuleVelocities.length; ++i) {
150                if (ModuleVelocities[i].velocity != driveState.ModuleVelocities[i].velocity ||
151                    ModuleVelocities[i].angle.getRadians() != driveState.ModuleVelocities[i].angle)
152                {
153                    ModuleVelocities[i] = new SwerveModuleVelocity(driveState.ModuleVelocities[i].velocity, Rotation2d.fromRadians(driveState.ModuleVelocities[i].angle));
154                }
155            }
156            for (int i = 0; i < ModuleTargets.length; ++i) {
157                if (ModuleTargets[i].velocity != driveState.ModuleTargets[i].velocity ||
158                    ModuleTargets[i].angle.getRadians() != driveState.ModuleTargets[i].angle)
159                {
160                    ModuleTargets[i] = new SwerveModuleVelocity(driveState.ModuleTargets[i].velocity, Rotation2d.fromRadians(driveState.ModuleTargets[i].angle));
161                }
162            }
163
164            if (RawHeading.getRadians() != driveState.RawHeading) {
165                RawHeading = Rotation2d.fromRadians(driveState.RawHeading);
166            }
167
168            Timestamp = driveState.Timestamp;
169            OdometryPeriod = driveState.OdometryPeriod;
170            SuccessfulDaqs = driveState.SuccessfulDaqs;
171            FailedDaqs = driveState.FailedDaqs;
172        }
173    }
174
175    /**
176     * Contains everything the control requests need to calculate the module state.
177     */
178    public static class SwerveControlParameters {
179        /** ID of the native drivetrain instance, used for JNI calls */
180        public int drivetrainId;
181
182        /** The kinematics object used for control */
183        public SwerveDriveKinematics kinematics;
184        /** The locations of the swerve modules */
185        public Translation2d[] moduleLocations;
186        /** The max speed of the robot at 12 V output, in m/s */
187        public double kMaxSpeedMps;
188
189        /** The forward direction from the operator perspective */
190        public Rotation2d operatorForwardDirection = new Rotation2d();
191        /** The current robot-centric chassis velocity */
192        public ChassisVelocities currentChassisVelocity = new ChassisVelocities();
193        /** The current pose of the robot */
194        public Pose2d currentPose = new Pose2d();
195        /** The timestamp of the current control apply, in the timebase of {@link Utils#getCurrentTimeSeconds()} */
196        public double timestamp;
197        /** The update period of control apply */
198        public double updatePeriod;
199
200        protected void updateFromJni(SwerveJNI.ControlParams controlParams) {
201            kMaxSpeedMps = controlParams.kMaxSpeedMps;
202            if (operatorForwardDirection.getRadians() != controlParams.operatorForwardDirection) {
203                operatorForwardDirection = Rotation2d.fromRadians(controlParams.operatorForwardDirection);
204            }
205            currentChassisVelocity.vx = controlParams.currentChassisVelocityVx;
206            currentChassisVelocity.vy = controlParams.currentChassisVelocityVy;
207            currentChassisVelocity.omega = controlParams.currentChassisVelocityOmega;
208            if (currentPose.getX() != controlParams.currentPoseX ||
209                currentPose.getY() != controlParams.currentPoseY ||
210                currentPose.getRotation().getRadians() != controlParams.currentPoseTheta)
211            {
212                currentPose = new Pose2d(
213                    controlParams.currentPoseX,
214                    controlParams.currentPoseY,
215                    Rotation2d.fromRadians(controlParams.currentPoseTheta)
216                );
217            }
218            timestamp = controlParams.timestamp;
219            updatePeriod = controlParams.updatePeriod;
220        }
221    }
222
223    /** Number of times to attempt config applies. */
224    protected static final int kNumConfigAttempts = 2;
225
226    /** ID of the native drivetrain instance, used for JNI calls. */
227    protected final int m_drivetrainId;
228    /** JNI instance to use for non-static JNI calls. This object is not thread-safe. */
229    protected final SwerveJNI m_jni = new SwerveJNI();
230
231    private final SwerveModule<DriveMotorT, SteerMotorT, EncoderT>[] m_modules;
232    private final Translation2d[] m_moduleLocations;
233
234    private final SwerveDriveKinematics m_kinematics;
235
236    private final Pigeon2 m_pigeon2;
237    private final SimSwerveDrivetrain m_simDrive;
238
239    /** The control parameters supplied to a non-native SwerveRequest. */
240    private final SwerveControlParameters m_controlParams = new SwerveControlParameters();
241    /** The swerve request currently applied. */
242    private SwerveRequest m_swerveRequest = new SwerveRequest.Idle();
243    /** Handle to the native object used to apply a non-native SwerveRequest. */
244    private long m_controlHandle = 0;
245
246    /** JNI instance to use for telemetry JNI calls. This object is not thread-safe. */
247    protected final SwerveJNI m_telemetryJNI;
248    /** The telemetry function currently applied. */
249    private Consumer<SwerveDriveState> m_telemetryFunction = null;
250    /** Handle to the native object used to call the telemetry function. */
251    private long m_telemetryHandle = 0;
252
253    /** Lock protecting the swerve drive state. */
254    private final Lock m_stateLock = new ReentrantLock();
255    /** The last received swerve drive state. */
256    private final SwerveDriveState m_cachedState = new SwerveDriveState();
257    /** The current operator forward direction. */
258    private Rotation2d m_operatorForwardDirection = new Rotation2d();
259
260    /** Performs swerve module updates in a separate thread to minimize latency. */
261    public class OdometryThread {
262        /**
263         * Starts the odometry thread.
264         */
265        public final void start() {
266            SwerveJNI.JNI_Odom_Start(m_drivetrainId);
267        }
268
269        /**
270         * Stops the odometry thread.
271         */
272        public final void stop() {
273            SwerveJNI.JNI_Odom_Stop(m_drivetrainId);
274        }
275
276        /**
277         * Check if the odometry is currently valid.
278         *
279         * @return True if odometry is valid
280         */
281        public boolean isOdometryValid() {
282            return SwerveJNI.JNI_IsOdometryValid(m_drivetrainId);
283        }
284
285        /**
286         * Sets the odometry thread priority to a real time priority under the specified priority level
287         *
288         * @param priority Priority level to set the odometry thread to.
289         *                 This is a value between 0 and 99, with 99 indicating higher priority and 0 indicating lower priority.
290         */
291        public final void setThreadPriority(int priority) {
292            SwerveJNI.JNI_Odom_SetThreadPriority(m_drivetrainId, priority);
293        }
294    }
295
296    private final OdometryThread m_odometryThread = new OdometryThread();
297
298    /** Thread responsible for disposing the native drivetrain on shutdown. */
299    private Thread m_shutdownHook;
300
301    private double m_lastErrorSeconds = 0.0;
302
303    /**
304     * Constructs a CTRE SwerveDrivetrain using the specified constants.
305     * <p>
306     * This constructs the underlying hardware devices, so users should not construct
307     * the devices themselves. If they need the devices, they can access them through
308     * getters in the classes.
309     *
310     * @param driveMotorConstructor Constructor for the drive motor, such as {@code TalonFX::new}
311     * @param steerMotorConstructor Constructor for the steer motor, such as {@code TalonFX::new}
312     * @param encoderConstructor    Constructor for the azimuth encoder, such as {@code CANcoder::new}
313     * @param drivetrainConstants   Drivetrain-wide constants for the swerve drive
314     * @param modules               Constants for each specific module
315     */
316    public SwerveDrivetrain(
317        DeviceConstructor<DriveMotorT> driveMotorConstructor,
318        DeviceConstructor<SteerMotorT> steerMotorConstructor,
319        DeviceConstructor<EncoderT> encoderConstructor,
320        SwerveDrivetrainConstants drivetrainConstants,
321        SwerveModuleConstants<?, ?, ?>... modules
322    ) {
323        this(() -> {
324                long nativeDriveConstants = drivetrainConstants.createNativeInstance();
325                long nativeModuleConstants = SwerveModuleConstants.createNativeInstance(modules);
326
327                var drivetrain = SwerveJNI.JNI_CreateDrivetrain(nativeDriveConstants, nativeModuleConstants, modules.length);
328
329                SwerveJNI.JNI_DestroyConstants(nativeDriveConstants);
330                SwerveJNI.JNI_DestroyConstants(nativeModuleConstants);
331                return drivetrain;
332            },
333            driveMotorConstructor, steerMotorConstructor, encoderConstructor,
334            drivetrainConstants, modules
335        );
336    }
337
338    /**
339     * Constructs a CTRE SwerveDrivetrain using the specified constants.
340     * <p>
341     * This constructs the underlying hardware devices, so users should not construct
342     * the devices themselves. If they need the devices, they can access them through
343     * getters in the classes.
344     *
345     * @param driveMotorConstructor   Constructor for the drive motor, such as {@code TalonFX::new}
346     * @param steerMotorConstructor   Constructor for the steer motor, such as {@code TalonFX::new}
347     * @param encoderConstructor      Constructor for the azimuth encoder, such as {@code CANcoder::new}
348     * @param drivetrainConstants     Drivetrain-wide constants for the swerve drive
349     * @param odometryUpdateFrequency The frequency to run the odometry loop. If
350     *                                unspecified or set to 0 Hz, this is 250 Hz on
351     *                                CAN FD, and 100 Hz on CAN 2.0.
352     * @param modules                 Constants for each specific module
353     */
354    public SwerveDrivetrain(
355        DeviceConstructor<DriveMotorT> driveMotorConstructor,
356        DeviceConstructor<SteerMotorT> steerMotorConstructor,
357        DeviceConstructor<EncoderT> encoderConstructor,
358        SwerveDrivetrainConstants drivetrainConstants,
359        double odometryUpdateFrequency,
360        SwerveModuleConstants<?, ?, ?>... modules
361    ) {
362        this(() -> {
363                long nativeDriveConstants = drivetrainConstants.createNativeInstance();
364                long nativeModuleConstants = SwerveModuleConstants.createNativeInstance(modules);
365
366                var drivetrain = SwerveJNI.JNI_CreateDrivetrainWithFreq(nativeDriveConstants, odometryUpdateFrequency,
367                        nativeModuleConstants, modules.length);
368
369                SwerveJNI.JNI_DestroyConstants(nativeDriveConstants);
370                SwerveJNI.JNI_DestroyConstants(nativeModuleConstants);
371                return drivetrain;
372            },
373            driveMotorConstructor, steerMotorConstructor, encoderConstructor,
374            drivetrainConstants, modules
375        );
376    }
377
378    /**
379     * Constructs a CTRE SwerveDrivetrain using the specified constants.
380     * <p>
381     * This constructs the underlying hardware devices, so users should not construct
382     * the devices themselves. If they need the devices, they can access them through
383     * getters in the classes.
384     *
385     * @param driveMotorConstructor     Constructor for the drive motor, such as {@code TalonFX::new}
386     * @param steerMotorConstructor     Constructor for the steer motor, such as {@code TalonFX::new}
387     * @param encoderConstructor        Constructor for the azimuth encoder, such as {@code CANcoder::new}
388     * @param drivetrainConstants       Drivetrain-wide constants for the swerve drive
389     * @param odometryUpdateFrequency   The frequency to run the odometry loop. If
390     *                                  unspecified or set to 0 Hz, this is 250 Hz on
391     *                                  CAN FD, and 100 Hz on CAN 2.0.
392     * @param odometryStandardDeviation The standard deviation for odometry calculation
393     *                                  in the form [x, y, theta]ᵀ, with units in meters
394     *                                  and radians
395     * @param visionStandardDeviation   The standard deviation for vision calculation
396     *                                  in the form [x, y, theta]ᵀ, with units in meters
397     *                                  and radians
398     * @param modules                   Constants for each specific module
399     */
400    public SwerveDrivetrain(
401        DeviceConstructor<DriveMotorT> driveMotorConstructor,
402        DeviceConstructor<SteerMotorT> steerMotorConstructor,
403        DeviceConstructor<EncoderT> encoderConstructor,
404        SwerveDrivetrainConstants drivetrainConstants,
405        double odometryUpdateFrequency,
406        Matrix<N3, N1> odometryStandardDeviation,
407        Matrix<N3, N1> visionStandardDeviation,
408        SwerveModuleConstants<?, ?, ?>... modules
409    ) {
410        this(() -> {
411                long nativeDriveConstants = drivetrainConstants.createNativeInstance();
412                long nativeModuleConstants = SwerveModuleConstants.createNativeInstance(modules);
413
414                var drivetrain = SwerveJNI.JNI_CreateDrivetrainWithStddev(nativeDriveConstants, odometryUpdateFrequency,
415                        odometryStandardDeviation.getData(), visionStandardDeviation.getData(),
416                        nativeModuleConstants, modules.length);
417
418                SwerveJNI.JNI_DestroyConstants(nativeDriveConstants);
419                SwerveJNI.JNI_DestroyConstants(nativeModuleConstants);
420                return drivetrain;
421            },
422            driveMotorConstructor, steerMotorConstructor, encoderConstructor,
423            drivetrainConstants, modules
424        );
425    }
426
427    @SuppressWarnings("this-escape")
428    private SwerveDrivetrain(
429        IntSupplier createNativeInst,
430        DeviceConstructor<DriveMotorT> driveMotorConstructor,
431        DeviceConstructor<SteerMotorT> steerMotorConstructor,
432        DeviceConstructor<EncoderT> encoderConstructor,
433        SwerveDrivetrainConstants drivetrainConstants,
434        SwerveModuleConstants<?, ?, ?>... modules
435    ) {
436        m_drivetrainId = createNativeInst.getAsInt();
437
438        m_cachedState.ModulePositions = new SwerveModulePosition[modules.length];
439        m_cachedState.ModuleVelocities = new SwerveModuleVelocity[modules.length];
440        m_cachedState.ModuleTargets = new SwerveModuleVelocity[modules.length];
441        m_jni.driveState.ModulePositions = new SwerveJNI.ModulePosition[modules.length];
442        m_jni.driveState.ModuleVelocities = new SwerveJNI.ModuleVelocity[modules.length];
443        m_jni.driveState.ModuleTargets = new SwerveJNI.ModuleVelocity[modules.length];
444        for (int i = 0; i < modules.length; ++i) {
445            m_cachedState.ModulePositions[i] = new SwerveModulePosition();
446            m_cachedState.ModuleVelocities[i] = new SwerveModuleVelocity();
447            m_cachedState.ModuleTargets[i] = new SwerveModuleVelocity();
448            m_jni.driveState.ModulePositions[i] = new SwerveJNI.ModulePosition();
449            m_jni.driveState.ModuleVelocities[i] = new SwerveJNI.ModuleVelocity();
450            m_jni.driveState.ModuleTargets[i] = new SwerveJNI.ModuleVelocity();
451        }
452
453        m_telemetryJNI = m_jni.clone();
454
455        final var canbus = new CANBus(drivetrainConstants.CANBusName);
456        @SuppressWarnings("unchecked")
457        final var tmpModules = (SwerveModule<DriveMotorT, SteerMotorT, EncoderT>[])new SwerveModule<?, ?, ?>[modules.length];
458
459        m_modules = tmpModules;
460        m_moduleLocations = new Translation2d[modules.length];
461        for (int i = 0; i < modules.length; ++i) {
462            m_modules[i] = new SwerveModule<DriveMotorT, SteerMotorT, EncoderT>(
463                driveMotorConstructor,
464                steerMotorConstructor,
465                encoderConstructor,
466                modules[i],
467                canbus,
468                m_drivetrainId, i
469            );
470            m_moduleLocations[i] = new Translation2d(modules[i].LocationX, modules[i].LocationY);
471        }
472
473        m_kinematics = new SwerveDriveKinematics(m_moduleLocations);
474
475        m_controlParams.drivetrainId = m_drivetrainId;
476        m_controlParams.kinematics = m_kinematics;
477        m_controlParams.moduleLocations = m_moduleLocations;
478
479        m_pigeon2 = new Pigeon2(drivetrainConstants.Pigeon2Id, canbus);
480        m_simDrive = new SimSwerveDrivetrain(m_moduleLocations, m_pigeon2.getSimState(), modules);
481
482        if (drivetrainConstants.Pigeon2Configs != null) {
483            StatusCode retval = StatusCode.OK;
484            for (int i = 0; i < kNumConfigAttempts; ++i) {
485                retval = getPigeon2().getConfigurator().apply(drivetrainConstants.Pigeon2Configs);
486                if (retval.isOK()) break;
487            }
488            if (!retval.isOK()) {
489                System.out.println("Pigeon2 ID " + getPigeon2().getDeviceID() + " failed config with error: " + retval);
490            }
491        }
492        /* do not start thread until after applying Pigeon 2 configs */
493        m_odometryThread.start();
494
495        m_shutdownHook = new Thread(() -> {
496            m_shutdownHook = null;
497            close();
498        });
499        Runtime.getRuntime().addShutdownHook(m_shutdownHook);
500    }
501
502    @Override
503    public void close() {
504        SwerveJNI.JNI_DestroyDrivetrain(m_drivetrainId);
505        if (m_controlHandle != 0) {
506            SwerveJNI.JNI_DestroyControl(m_controlHandle);
507            m_controlHandle = 0;
508        }
509        if (m_telemetryHandle != 0) {
510            SwerveJNI.JNI_DestroyTelemetry(m_telemetryHandle);
511            m_telemetryHandle = 0;
512        }
513
514        if (m_shutdownHook != null) {
515            Runtime.getRuntime().removeShutdownHook(m_shutdownHook);
516            m_shutdownHook = null;
517        }
518    }
519
520    /**
521     * Updates all the simulation state variables for this
522     * drivetrain class. User provides the update variables for the simulation.
523     *
524     * @param dtSeconds time since last update call
525     * @param supplyVoltage voltage as seen at the motor controllers
526     */
527    public void updateSimState(double dtSeconds, double supplyVoltage) {
528        m_simDrive.update(dtSeconds, supplyVoltage, m_modules);
529    }
530
531    /**
532     * Optimizes the bus utilization of all devices in the swerve drivetrain by reducing
533     * the update frequencies of their status signals. All signals necessary for drivetrain
534     * functionality will remain enabled.
535     * <p>
536     * This is equivalent to calling {@link ParentDevice#optimizeBusUtilizationForAll}
537     * with all devices in the drivetrain.
538     * <p>
539     * This will wait up to 0.100 seconds (100ms) for each status frame.
540     *
541     * @return Status code of the first failed optimize call, or OK if all succeeded
542     */
543    public StatusCode optimizeBusUtilization() {
544        final var devices = new CommonDevice[m_modules.length * 3 + 1];
545        devices[0] = getPigeon2();
546        for (int i = 0; i < m_modules.length; ++i) {
547            devices[i * 3 + 1] = m_modules[i].getDriveMotor();
548            devices[i * 3 + 2] = m_modules[i].getSteerMotor();
549            devices[i * 3 + 3] = m_modules[i].getEncoder();
550        }
551        return ParentDevice.optimizeBusUtilizationForAll(devices);
552    }
553
554    /**
555     * Optimizes the bus utilization of all devices in the swerve drivetrain by reducing
556     * the update frequencies of their status signals. All signals necessary for drivetrain
557     * functionality will remain enabled.
558     * <p>
559     * This is equivalent to calling {@link ParentDevice#optimizeBusUtilizationForAll}
560     * with all devices in the drivetrain.
561     * <p>
562     * This will wait up to 0.100 seconds (100ms) for each status frame.
563     *
564     * @param optimizedFreqHz The update frequency to apply to the optimized status signals. A frequency
565     *                        of 0 Hz will turn off the signals. Otherwise, the minimum supported signal
566     *                        frequency is 4 Hz.
567     * @return Status code of the first failed optimize call, or OK if all succeeded
568     */
569    public StatusCode optimizeBusUtilization(double optimizedFreqHz) {
570        final var devices = new CommonDevice[m_modules.length * 3 + 1];
571        devices[0] = getPigeon2();
572        for (int i = 0; i < m_modules.length; ++i) {
573            devices[i * 3 + 1] = m_modules[i].getDriveMotor();
574            devices[i * 3 + 2] = m_modules[i].getSteerMotor();
575            devices[i * 3 + 3] = m_modules[i].getEncoder();
576        }
577        return ParentDevice.optimizeBusUtilizationForAll(optimizedFreqHz, devices);
578    }
579
580    /**
581     * Optimizes the bus utilization of all devices in the swerve drivetrain by reducing
582     * the update frequencies of their status signals. All signals necessary for drivetrain
583     * functionality will remain enabled.
584     * <p>
585     * This is equivalent to calling {@link ParentDevice#optimizeBusUtilizationForAll}
586     * with all devices in the drivetrain.
587     * <p>
588     * This will wait up to 0.100 seconds (100ms) for each status frame.
589     *
590     * @param optimizedFreq The update frequency to apply to the optimized status signals. A frequency
591     *                      of 0 Hz will turn off the signals. Otherwise, the minimum supported signal
592     *                      frequency is 4 Hz.
593     * @return Status code of the first failed optimize call, or OK if all succeeded
594     */
595    public StatusCode optimizeBusUtilization(Frequency optimizedFreq) {
596        return optimizeBusUtilization(optimizedFreq.in(Hertz));
597    }
598
599    /**
600     * Gets whether the drivetrain is on a CAN FD bus.
601     * 
602     * @return true if on CAN FD
603     */
604    public final boolean isOnCANFD() {
605        return SwerveJNI.JNI_IsOnCANFD(m_drivetrainId);
606    }
607
608    /**
609     * Gets the target odometry update frequency in Hz.
610     * 
611     * @return Target odometry update frequency
612     */
613    public final double getOdometryFrequency() {
614        return SwerveJNI.JNI_GetOdometryFrequency(m_drivetrainId);
615    }
616
617    /**
618     * Gets the target odometry update frequency.
619     * 
620     * @return Target odometry update frequency
621     */
622    public final Frequency getOdometryFrequencyMeasure() {
623        return Hertz.of(getOdometryFrequency());
624    }
625
626    /**
627     * Gets a reference to the odometry thread.
628     *
629     * @return Odometry thread
630     */
631    public final OdometryThread getOdometryThread() {
632        return m_odometryThread;
633    }
634
635    /**
636     * Check if the odometry is currently valid.
637     *
638     * @return True if odometry is valid
639     */
640    public boolean isOdometryValid() {
641        return SwerveJNI.JNI_IsOdometryValid(m_drivetrainId);
642    }
643
644    /**
645     * Gets a reference to the kinematics used for the drivetrain.
646     * 
647     * @return Swerve kinematics
648     */
649    public final SwerveDriveKinematics getKinematics() {
650        return m_kinematics;
651    }
652
653    /**
654     * Applies the specified control request to this swerve drivetrain.
655     *
656     * @param request Request to apply
657     */
658    public void setControl(SwerveRequest request) {
659        if (!m_modules[0].m_isValid) {
660            double now = Utils.getCurrentTimeSeconds();
661            if (now - m_lastErrorSeconds > 3.0) {
662                m_lastErrorSeconds = now;
663                DriverStationErrors.reportError(
664                    "[phoenix] SwerveDrivetrain configuration invalid, drive is disabled. Ensure that the SwerveDrivetrain and SwerveModuleConstants instances are constructed with matching motor types (TalonFX/TalonFXConfiguration, TalonFXS/TalonFXSConfiguration, etc.).",
665                    true
666                );
667            }
668            return;
669        }
670
671        if (m_swerveRequest != request) {
672            m_swerveRequest = request;
673
674            var prevControlHandle = m_controlHandle;
675
676            if (request == null) {
677                m_controlHandle = m_jni.JNI_SetControl(m_drivetrainId, null);
678            } else if (request instanceof NativeSwerveRequest req) {
679                req.applyNative(m_drivetrainId);
680                m_controlHandle = 0;
681            } else {
682                m_controlHandle = m_jni.JNI_SetControl(m_drivetrainId, () -> {
683                    m_controlParams.updateFromJni(m_jni.controlParams);
684                    return request.apply(m_controlParams, m_modules).value;
685                });
686            }
687
688            if (prevControlHandle != 0) {
689                SwerveJNI.JNI_DestroyControl(prevControlHandle);
690            }
691        } else if (request instanceof NativeSwerveRequest req) {
692            /* update the native object */
693            req.applyNative(m_drivetrainId);
694        }
695    }
696
697    /**
698     * Gets the current state of the swerve drivetrain.
699     * This includes information such as the pose estimate,
700     * module states, and chassis velocity.
701     *
702     * @return Current state of the drivetrain
703     */
704    public final SwerveDriveState getState() {
705        m_jni.JNI_GetState(m_drivetrainId);
706        try {
707            m_stateLock.lock();
708            m_cachedState.updateFromJni(m_jni.driveState);
709            return m_cachedState;
710        } finally {
711            m_stateLock.unlock();
712        }
713    }
714
715    /**
716     * Gets a copy of the current state of the swerve drivetrain.
717     * This includes information such as the pose estimate,
718     * module states, and chassis velocity.
719     * <p>
720     * This can be used to get a thread-safe copy of the state object.
721     *
722     * @return Copy of the current state of the drivetrain
723     */
724    public final SwerveDriveState getStateCopy() {
725        m_jni.JNI_GetState(m_drivetrainId);
726        try {
727            m_stateLock.lock();
728            m_cachedState.updateFromJni(m_jni.driveState);
729            return m_cachedState.clone();
730        } finally {
731            m_stateLock.unlock();
732        }
733    }
734
735    /**
736     * Register the specified lambda to be executed whenever our SwerveDriveState function
737     * is updated in our odometry thread.
738     * <p>
739     * It is imperative that this function is cheap, as it will be executed along with
740     * the odometry call, and if this takes a long time, it may negatively impact the
741     * odometry of this stack.
742     * <p>
743     * This can also be used for logging data if the function performs logging instead of telemetry.
744     * Additionally, the SwerveDriveState object can be cloned and stored for later processing.
745     *
746     * @param telemetryFunction Function to call for telemetry or logging
747     */
748    public void registerTelemetry(Consumer<SwerveDriveState> telemetryFunction) {
749        if (m_telemetryFunction != telemetryFunction) {
750            m_telemetryFunction = telemetryFunction;
751
752            var prevTelemetryHandle = m_telemetryHandle;
753
754            if (telemetryFunction == null) {
755                m_telemetryHandle = m_telemetryJNI.JNI_RegisterTelemetry(m_drivetrainId, null);
756            } else {
757                m_telemetryHandle = m_telemetryJNI.JNI_RegisterTelemetry(m_drivetrainId, () -> {
758                    try {
759                        m_stateLock.lock();
760                        m_cachedState.updateFromJni(m_telemetryJNI.driveState);
761                        telemetryFunction.accept(m_cachedState);
762                    } finally {
763                        m_stateLock.unlock();
764                    }
765                });
766            }
767
768            if (prevTelemetryHandle != 0) {
769                SwerveJNI.JNI_DestroyTelemetry(prevTelemetryHandle);
770            }
771        }
772    }
773
774    /**
775     * Configures the neutral mode to use for all modules' drive motors.
776     * <p>
777     * This will wait up to 0.100 seconds (100ms) by default.
778     *
779     * @param neutralMode The drive motor neutral mode
780     * @return Status code of the first failed config call, or OK if all succeeded
781     */
782    public StatusCode configNeutralMode(NeutralModeValue neutralMode) {
783        return StatusCode.valueOf(SwerveJNI.JNI_ConfigNeutralMode(m_drivetrainId, neutralMode.value));
784    }
785
786    /**
787     * Configures the neutral mode to use for all modules' drive motors.
788     *
789     * @param neutralMode The drive motor neutral mode
790     * @param timeoutSeconds Maximum amount of time to wait when performing each configuration
791     * @return Status code of the first failed config call, or OK if all succeeded
792     */
793    public StatusCode configNeutralMode(NeutralModeValue neutralMode, double timeoutSeconds) {
794        return StatusCode.valueOf(SwerveJNI.JNI_ConfigNeutralModeWithTimeout(m_drivetrainId, neutralMode.value, timeoutSeconds));
795    }
796
797    /**
798     * Zero's this swerve drive's odometry entirely.
799     * <p>
800     * This will zero the entire odometry, and place the robot at 0,0
801     */
802    public void tareEverything() {
803        SwerveJNI.JNI_TareEverything(m_drivetrainId);
804    }
805
806    /**
807     * Resets the rotation of the robot pose to 0 from the
808     * {@link SwerveRequest.ForwardPerspectiveValue#OperatorPerspective}
809     * perspective. This makes the current orientation of the robot the
810     * X forward for field-centric maneuvers.
811     * <p>
812     * This is equivalent to calling {@link #resetRotation} with the operator
813     * perspective rotation.
814     */
815    public void seedFieldCentric() {
816        seedFieldCentric(Rotation2d.kZero);
817    }
818
819    /**
820     * Resets the rotation of the robot pose to the given value from the
821     * {@link SwerveRequest.ForwardPerspectiveValue#OperatorPerspective}
822     * perspective. This makes the current orientation of the robot minus
823     * {@code rotation} the X forward for field-centric maneuvers.
824     * <p>
825     * This is equivalent to calling {@link #resetRotation} with
826     * {@code rotation.plus(getOperatorForwardDirection())}.
827     *
828     * @param rotation Rotation to make the current rotation
829     */
830    public void seedFieldCentric(Rotation2d rotation) {
831        SwerveJNI.JNI_SeedFieldCentric(m_drivetrainId, rotation.getRadians());
832    }
833
834    /**
835     * Resets the pose of the robot. The pose should be from the
836     * {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perspective.
837     *
838     * @param pose Pose to make the current pose
839     */
840    public void resetPose(Pose2d pose) {
841        SwerveJNI.JNI_ResetPose(m_drivetrainId, pose.getX(), pose.getY(), pose.getRotation().getRadians());
842    }
843
844    /**
845     * Resets the translation of the robot pose without affecting rotation.
846     * The translation should be from the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance}
847     * perspective.
848     *
849     * @param translation Translation to make the current translation
850     */
851    public void resetTranslation(Translation2d translation) {
852        SwerveJNI.JNI_ResetTranslation(m_drivetrainId, translation.getX(), translation.getY());
853    }
854
855    /**
856     * Resets the rotation of the robot pose without affecting translation.
857     * The rotation should be from the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance}
858     * perspective.
859     *
860     * @param rotation Rotation to make the current rotation
861     */
862    public void resetRotation(Rotation2d rotation) {
863        SwerveJNI.JNI_ResetRotation(m_drivetrainId, rotation.getRadians());
864    }
865
866    /**
867     * Takes the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perpective direction
868     * and treats it as the forward direction for
869     * {@link SwerveRequest.ForwardPerspectiveValue#OperatorPerspective}.
870     * <p>
871     * If the operator is in the Blue Alliance Station, this should be 0 degrees.
872     * If the operator is in the Red Alliance Station, this should be 180 degrees.
873     * <p>
874     * This does not change the robot pose, which is in the
875     * {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perspective.
876     * As a result, the robot pose may need to be reset using {@link #resetPose}.
877     *
878     * @param fieldDirection Heading indicating which direction is forward from
879     *                       the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perspective
880     */
881    public void setOperatorPerspectiveForward(Rotation2d fieldDirection) {
882        SwerveJNI.JNI_SetOperatorPerspectiveForward(m_drivetrainId, fieldDirection.getRadians());
883    }
884
885    /**
886     * Returns the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perpective
887     * direction that is treated as the forward direction for
888     * {@link SwerveRequest.ForwardPerspectiveValue#OperatorPerspective}.
889     * <p>
890     * If the operator is in the Blue Alliance Station, this should be 0 degrees.
891     * If the operator is in the Red Alliance Station, this should be 180 degrees.
892     *
893     * @return Heading indicating which direction is forward from
894     *         the {@link SwerveRequest.ForwardPerspectiveValue#BlueAlliance} perspective
895     */
896    public final Rotation2d getOperatorForwardDirection() {
897        double operatorForwardDirectionRad = SwerveJNI.JNI_GetOperatorForwardDirection(m_drivetrainId);
898        try {
899            m_stateLock.lock();
900            if (m_operatorForwardDirection.getRadians() != operatorForwardDirectionRad) {
901                m_operatorForwardDirection = Rotation2d.fromRadians(operatorForwardDirectionRad);
902            }
903            return m_operatorForwardDirection;
904        } finally {
905            m_stateLock.unlock();
906        }
907    }
908
909    /**
910     * Adds a vision measurement to the Kalman Filter. This will correct the
911     * odometry pose estimate while still accounting for measurement noise.
912     * <p>
913     * This method can be called as infrequently as you want.
914     * <p>
915     * To promote stability of the pose estimate and make it robust to bad vision
916     * data, we recommend only adding vision measurements that are already within
917     * one meter or so of the current pose estimate.
918     *
919     * @param visionRobotPoseMeters The pose of the robot as measured by the vision
920     *                              camera.
921     * @param timestampSeconds      The timestamp of the vision measurement in
922     *                              seconds. Note that you must use a timestamp with an
923     *                              epoch since system startup (i.e., the epoch of this
924     *                              timestamp is the same epoch as
925     *                              {@link Utils#getCurrentTimeSeconds}).
926     *                              This means that you should use
927     *                              {@link Utils#getCurrentTimeSeconds}
928     *                              as your time source or sync the epochs.
929     */
930    public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) {
931        SwerveJNI.JNI_AddVisionMeasurement(m_drivetrainId, visionRobotPoseMeters.getX(), visionRobotPoseMeters.getY(),
932            visionRobotPoseMeters.getRotation().getRadians(), timestampSeconds);
933    }
934
935    /**
936     * Adds a vision measurement to the Kalman Filter. This will correct the
937     * odometry pose estimate while still accounting for measurement noise.
938     * <p>
939     * This method can be called as infrequently as you want.
940     * <p>
941     * To promote stability of the pose estimate and make it robust to bad vision
942     * data, we recommend only adding vision measurements that are already within
943     * one meter or so of the current pose estimate.
944     * <p>
945     * Note that the vision measurement standard deviations passed into this method
946     * will continue to apply to future measurements until a subsequent call to
947     * {@link #setVisionMeasurementStdDevs(Matrix)} or this method.
948     *
949     * @param visionRobotPoseMeters    The pose of the robot as measured by the
950     *                                 vision camera.
951     * @param timestampSeconds         The timestamp of the vision measurement in
952     *                                 seconds. Note that you must use a timestamp with an
953     *                                 epoch since system startup (i.e., the epoch of this
954     *                                 timestamp is the same epoch as
955     *                                 {@link Utils#getCurrentTimeSeconds}).
956     *                                 This means that you should use
957     *                                 {@link Utils#getCurrentTimeSeconds}
958     *                                 as your time source or sync the epochs.
959     * @param visionMeasurementStdDevs Standard deviations of the vision pose
960     *                                 measurement (x position
961     *                                 in meters, y position in meters, and heading
962     *                                 in radians). Increase these numbers to trust
963     *                                 the vision pose measurement less.
964     */
965    public void addVisionMeasurement(
966            Pose2d visionRobotPoseMeters,
967            double timestampSeconds,
968            Matrix<N3, N1> visionMeasurementStdDevs)
969    {
970        SwerveJNI.JNI_AddVisionMeasurementWithStdDev(m_drivetrainId, visionRobotPoseMeters.getX(), visionRobotPoseMeters.getY(),
971            visionRobotPoseMeters.getRotation().getRadians(), timestampSeconds, visionMeasurementStdDevs.getData());
972    }
973
974    /**
975     * Sets the pose estimator's trust of global measurements. This might be used to
976     * change trust in vision measurements after the autonomous period, or to change
977     * trust as distance to a vision target increases.
978     *
979     * @param visionMeasurementStdDevs Standard deviations of the vision
980     *                                 measurements. Increase these numbers to
981     *                                 trust global measurements from vision less.
982     *                                 This matrix is in the form [x, y, theta]ᵀ,
983     *                                 with units in meters and radians.
984     */
985    public void setVisionMeasurementStdDevs(Matrix<N3, N1> visionMeasurementStdDevs) {
986        SwerveJNI.JNI_SetVisionMeasurementStdDevs(m_drivetrainId, visionMeasurementStdDevs.getData());
987    }
988
989    /**
990     * Sets the pose estimator's trust in robot odometry. This might be used to change
991     * trust in odometry after an impact with the wall or traversing a bump.
992     *
993     * @param stateStdDevs Standard deviations of the pose estimate. Increase these
994     *                     numbers to trust your state estimate less. This matrix is
995     *                     in the form [x, y, theta]ᵀ, with units in meters and radians.
996     */
997    public void setStateStdDevs(Matrix<N3, N1> stateStdDevs) {
998        SwerveJNI.JNI_SetStateStdDevs(m_drivetrainId, stateStdDevs.getData());
999    }
1000
1001    /**
1002     * Return the pose at a given timestamp, if the buffer is not empty.
1003     *
1004     * @param timestampSeconds The pose's timestamp. Note that you must use a timestamp
1005     *                         with an epoch since system startup (i.e., the epoch of
1006     *                         this timestamp is the same epoch as
1007     *                         {@link Utils#getCurrentTimeSeconds}).
1008     *                         This means that you should use
1009     *                         {@link Utils#getCurrentTimeSeconds}
1010     *                         as your time source in this case.
1011     * @return The pose at the given timestamp (or Optional.empty() if the buffer is
1012     *         empty).
1013     */
1014    public Optional<Pose2d> samplePoseAt(double timestampSeconds) {
1015        double[] retval = SwerveJNI.JNI_SamplePoseAt(m_drivetrainId, timestampSeconds);
1016        if (retval == null) {
1017            return Optional.empty();
1018        }
1019        return Optional.of(new Pose2d(retval[0], retval[1], Rotation2d.fromRadians(retval[2])));
1020    }
1021
1022    /**
1023     * Get a reference to the module at the specified index.
1024     * The index corresponds to the module described in the constructor.
1025     *
1026     * @param index Which module to get
1027     * @return Reference to SwerveModule
1028     */
1029    public final SwerveModule<DriveMotorT, SteerMotorT, EncoderT> getModule(int index) {
1030        if (index >= m_modules.length) {
1031            return null;
1032        }
1033        return m_modules[index];
1034    }
1035
1036    /**
1037     * Get a reference to the full array of modules.
1038     * The indexes correspond to the module described in the constructor.
1039     *
1040     * @return Reference to the SwerveModule array
1041     */
1042    public final SwerveModule<DriveMotorT, SteerMotorT, EncoderT>[] getModules() {
1043        return m_modules;
1044    }
1045
1046    /**
1047     * Gets the locations of the swerve modules.
1048     * 
1049     * @return Reference to the array of swerve module locations
1050     */
1051    public final Translation2d[] getModuleLocations() {
1052        return m_moduleLocations;
1053    }
1054
1055    /**
1056     * Gets the current orientation of the robot as a {@link Rotation3d} from
1057     * the Pigeon 2 quaternion values.
1058     *
1059     * @return The robot orientation as a {@link Rotation3d}
1060     */
1061    public Rotation3d getRotation3d() {
1062        return m_pigeon2.getRotation3d();
1063    }
1064
1065    /**
1066     * Gets this drivetrain's Pigeon 2 reference.
1067     * <p>
1068     * This should be used only to access signals and change configurations that the
1069     * swerve drivetrain does not configure itself.
1070     *
1071     * @return This drivetrain's Pigeon 2 reference
1072     */
1073    public final Pigeon2 getPigeon2() {
1074        return m_pigeon2;
1075    }
1076}