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}