001/** 002 * WPI Compliant motor controller class. 003 * WPILIB's object model requires many interfaces to be implemented to use 004 * the various features. 005 * This includes... 006 * - LiveWindow/Test mode features 007 * - getRotation2d/Gyro Interface 008 * - Simulation Hooks 009 */ 010 011package com.ctre.phoenix.sensors; 012 013import com.ctre.phoenix.motorcontrol.can.TalonSRX; 014import com.ctre.phoenix.platform.DeviceType; 015import com.ctre.phoenix.platform.PlatformJNI; 016import com.ctre.phoenix.sensors.Pigeon2; 017import com.ctre.phoenix.WPI_CallbackHelper; 018 019import edu.wpi.first.wpilibj.DriverStation; 020import edu.wpi.first.util.sendable.Sendable; 021import edu.wpi.first.util.sendable.SendableBuilder; 022import edu.wpi.first.util.sendable.SendableRegistry; 023import edu.wpi.first.math.geometry.Rotation2d; 024 025import edu.wpi.first.hal.SimDevice.Direction; 026import edu.wpi.first.hal.HALValue; 027import edu.wpi.first.hal.SimDevice; 028import edu.wpi.first.hal.SimDouble; 029import edu.wpi.first.wpilibj.simulation.CallbackStore; 030import edu.wpi.first.wpilibj.simulation.SimDeviceSim; 031import edu.wpi.first.hal.simulation.SimDeviceDataJNI; 032import edu.wpi.first.hal.simulation.SimValueCallback; 033import edu.wpi.first.hal.simulation.SimulatorJNI; 034import edu.wpi.first.hal.HAL; 035import edu.wpi.first.hal.HAL.SimPeriodicBeforeCallback; 036 037import java.util.ArrayList; 038 039/** 040 * Pigeon 2 Class. Class supports communicating over CANbus. 041 * 042 * @deprecated This device's Phoenix 5 API is deprecated for removal in the 043 * 2025 season. Users should update to Phoenix 6 firmware and migrate to the 044 * Phoenix 6 API. A migration guide is available at 045 * https://v6.docs.ctr-electronics.com/en/stable/docs/migration/migration-guide/index.html. 046 * <p> 047 * If the Phoenix 5 API must be used for this device, the device must have 22.X 048 * firmware. This firmware is available in Tuner X after selecting Phoenix 5 in 049 * the firmware year dropdown. 050 */ 051@Deprecated(since = "2024", forRemoval = true) 052public class WPI_Pigeon2 extends Pigeon2 implements Sendable, AutoCloseable { 053 054 private double[] _xyz_dps; //instance this so we don't alloc when getRate is called. 055 056 private SimDevice m_simPigeon; 057 private SimDouble m_simYaw; 058 private SimDouble m_simRawYaw; 059 private DeviceType m_type; 060 061 // callbacks to register 062 private SimValueCallback onValueChangedCallback = new OnValueChangedCallback(); 063 private Runnable onPeriodicCallback = new OnPeriodicCallback(); 064 065 // returned registered callbacks 066 private ArrayList<CallbackStore> simValueChangedCallbacks = new ArrayList<CallbackStore>(); 067 private SimPeriodicBeforeCallback simPeriodicCallback; 068 069 /** 070 * Constructor for Pigeon 2. 071 * @param deviceNumber device ID of Pigeon 2 072 * @param canbus Name of the CANbus; can be a CANivore device name or serial number. 073 * Pass in nothing or "rio" to use the roboRIO. 074 */ 075 public WPI_Pigeon2(int deviceNumber, String canbus) { 076 super(deviceNumber, canbus); 077 m_type = DeviceType.PigeonIMU; 078 SendableRegistry.addLW(this, "Pigeon 2 ", deviceNumber); 079 init(); 080 } 081 /** 082 * Constructor for Pigeon 2. 083 * @param deviceNumber device ID of Pigeon 2 084 */ 085 public WPI_Pigeon2(int deviceNumber) { 086 this(deviceNumber, ""); 087 } 088 089 private void init(){ 090 _xyz_dps = new double[3]; 091 092 m_simPigeon = SimDevice.create("CANGyro:Pigeon 2", getDeviceID()); 093 if(m_simPigeon != null) { 094 simPeriodicCallback = HAL.registerSimPeriodicBeforeCallback(onPeriodicCallback); 095 096 m_simYaw = m_simPigeon.createDouble("yaw", Direction.kOutput, 0); 097 098 m_simRawYaw = m_simPigeon.createDouble("rawYawInput", Direction.kInput, 0); 099 100 SimDeviceSim sim = new SimDeviceSim("CANGyro:Pigeon 2"); 101 simValueChangedCallbacks.add(sim.registerValueChangedCallback(m_simRawYaw, onValueChangedCallback, true)); 102 } 103 } 104 105 // ----- Auto-Closable, from Gyro ----- // 106 @Override 107 public void close(){ 108 SendableRegistry.remove(this); 109 if(m_simPigeon != null) { 110 m_simPigeon.close(); 111 m_simPigeon = null; 112 } 113 super.DestroyObject(); //Pigeon device, replace with super.close() once implemented 114 } 115 116 117 // ----- Callbacks for Sim ----- // 118 private class OnValueChangedCallback implements SimValueCallback { 119 @Override 120 public void callback(String name, int handle, int direction, HALValue value) { 121 String deviceName = SimDeviceDataJNI.getSimDeviceName(SimDeviceDataJNI.getSimValueDeviceHandle(handle)); 122 String physType = deviceName + ":" + name; 123 PlatformJNI.JNI_SimSetPhysicsInput(m_type.value, getDeviceID(), 124 physType, WPI_CallbackHelper.getRawValue(value)); 125 } 126 } 127 128 private class OnPeriodicCallback implements Runnable { 129 @Override 130 public void run() { 131 double value = 0; 132 int err = 0; 133 134 int deviceID = getDeviceID(); 135 136 value = PlatformJNI.JNI_SimGetPhysicsValue(m_type.value, deviceID, "FusedHeading"); 137 err = PlatformJNI.JNI_SimGetLastError(m_type.value, deviceID); 138 if (err == 0) { 139 m_simYaw.set(value); 140 } 141 value = PlatformJNI.JNI_SimGetPhysicsValue(m_type.value, deviceID, "HeadingRaw"); 142 err = PlatformJNI.JNI_SimGetLastError(m_type.value, deviceID); 143 if (err == 0) { 144 m_simRawYaw.set(value); 145 } 146 } 147 } 148 149 150 // ----- WPILib Gyro Interface ----- // 151 //WPILib no longer has a Gyro interface, but these methods are standard in FRC. 152 153 /** 154 * Resets the Pigeon 2 to a heading of zero. 155 * <p> 156 * This can be used if there is significant drift in the gyro, 157 * and it needs to be recalibrated after it has been running. 158 */ 159 public void reset(){ 160 setYaw(0); 161 } 162 163 /** 164 * Returns the heading of the robot in degrees. 165 * <p> 166 * The angle increases as the Pigeon 2 turns clockwise when looked 167 * at from the top. This follows the NED axis convention. 168 * <p> 169 * The angle is continuous; that is, it will continue from 360 to 170 * 361 degrees. This allows for algorithms that wouldn't want to 171 * see a discontinuity in the gyro output as it sweeps past from 172 * 360 to 0 on the second time around. 173 * 174 * @return The current heading of the robot in degrees 175 */ 176 public double getAngle(){ 177 //Negate since getAngle requires cw+ and Pigeon is ccw+ 178 return -getYaw(); 179 } 180 181 /** 182 * Returns the rate of rotation of the Pigeon 2. 183 * <p> 184 * The rate is positive as the Pigeon 2 turns clockwise when looked 185 * at from the top. 186 * 187 * @return The current rate in degrees per second 188 */ 189 public double getRate(){ 190 getRawGyro(_xyz_dps); 191 //Expected to return cw+ 192 return -_xyz_dps[2]; 193 } 194 195 /** 196 * Returns the heading of the robot as a {@link Rotation2d}. 197 * <p> 198 * The angle increases as the Pigeon 2 turns counterclockwise when 199 * looked at from the top. This follows the NWU axis convention. 200 * <p> 201 * The angle is continuous; that is, it will continue from 360 to 202 * 361 degrees. This allows for algorithms that wouldn't want to 203 * see a discontinuity in the gyro output as it sweeps past from 204 * 360 to 0 on the second time around. 205 * 206 * @return The current heading of the robot as a {@link Rotation2d} 207 */ 208 public Rotation2d getRotation2d() { 209 //Rotation2d and Pigeon are both ccw+ 210 return Rotation2d.fromDegrees(getYaw()); 211 } 212 213 // ----- Sendable ----- // 214 @Override 215 public void initSendable(SendableBuilder builder) { 216 builder.setSmartDashboardType("Gyro"); 217 builder.addDoubleProperty("Value", this::getYaw, null); 218 } 219}