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.sim; 008 009import static edu.wpi.first.units.Units.*; 010 011import com.ctre.phoenix6.StatusCode; 012import com.ctre.phoenix6.hardware.core.CoreTalonFXS; 013import com.ctre.phoenix6.hardware.TalonFXS; 014import com.ctre.phoenix6.jni.PlatformJNI; 015 016import edu.wpi.first.units.measure.*; 017 018/** 019 * Class to control the state of a simulated {@link TalonFXS}. 020 * <p> 021 * For simulated PID control and current limits to behave correctly, 022 * {@link #setRawRotorPosition} and {@link #setRotorVelocity} must be updated 023 * periodically. This is typically done by sending the motor output from 024 * {@link #getMotorVoltage()} or {@link #getTorqueCurrent()} to a physics 025 * simulator (such as WPILib's {@code DCMotorSim}), which then calculates the 026 * new motor position and velocity. 027 */ 028public class TalonFXSSimState { 029 private static final DeviceType kDevType = DeviceType.P6_TalonFXSType; 030 031 private final int _id; 032 033 /** 034 * The orientation of the motor attached to the TalonFXS relative 035 * to the robot chassis. This include the Commutation sensor source. 036 * <p> 037 * This value should not be changed based on the TalonFXS invert. 038 * Rather, this value should be changed when the mechanical linkage 039 * between the motor and the robot changes. 040 */ 041 public ChassisReference MotorOrientation; 042 043 /** 044 * The orientation of an external sensor attached to the TalonFXS 045 * relative to the robot chassis. This does NOT include the Commutation 046 * sensor source. 047 * <p> 048 * This value should not be changed based on the TalonFXS invert. 049 * Rather, this value should be changed when the mechanical linkage 050 * between the external sensor and the robot changes. 051 */ 052 public ChassisReference ExtSensorOrientation; 053 /** 054 * The offset of an absolute external sensor attached to the Talon FXS 055 * relative to the robot chassis, in rotations. This offset is subtracted 056 * from the Pulse Width position, allowing for a non-zero sensor offset 057 * config to behave correctly in simulation. 058 * <p> 059 * This value should not be changed after initialization unless the 060 * mechanical linkage between the external sensor and the robot changes. 061 */ 062 public double PulseWidthSensorOffset = 0.0; 063 /** 064 * The number of quadrature edges per sensor rotation for an external 065 * quadrature sensor attached to the TalonFXS. 066 */ 067 public int QuadratureEdgesPerRotation = 4096; 068 069 /** 070 * Creates an object to control the state of the given {@link TalonFXS}. 071 * <p> 072 * This constructor defaults to a counter-clockwise positive motor and 073 * external sensor orientation relative to the robot chassis. 074 * <p> 075 * Note the recommended method of accessing simulation features is to use 076 * {@link TalonFXS#getSimState}. 077 * 078 * @param device Device to which this simulation state is attached 079 */ 080 public TalonFXSSimState(CoreTalonFXS device) { 081 this( 082 device, 083 ChassisReference.CounterClockwise_Positive, 084 ChassisReference.CounterClockwise_Positive 085 ); 086 } 087 /** 088 * Creates an object to control the state of the given {@link TalonFXS}. 089 * <p> 090 * Note the recommended method of accessing simulation features is to use 091 * {@link TalonFXS#getSimState}. 092 * 093 * @param device Device to which this simulation state is attached 094 * @param motorOrientation Orientation of the motor (and commutation sensor) relative to the robot chassis 095 * @param extSensorOrientation Orientation of the external sensor relative to the robot chassis 096 */ 097 public TalonFXSSimState( 098 CoreTalonFXS device, 099 ChassisReference motorOrientation, 100 ChassisReference extSensorOrientation 101 ) { 102 _id = device.getDeviceID(); 103 MotorOrientation = motorOrientation; 104 ExtSensorOrientation = extSensorOrientation; 105 } 106 107 /** 108 * Gets the last status code generated by a simulation function. 109 * <p> 110 * Not all functions return a status code but can potentially report errors. 111 * This function can be used to retrieve those status codes. 112 * 113 * @return Last status code generated by a simulation function 114 */ 115 public final StatusCode getLastStatusCode() { 116 return StatusCode.valueOf(PlatformJNI.JNI_SimGetLastError(kDevType.value, _id)); 117 } 118 119 /** 120 * Gets the simulated output voltage of the motor. 121 * 122 * @return Voltage applied to the motor in Volts 123 */ 124 public final double getMotorVoltage() { 125 double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "MotorVoltage"); 126 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 127 value = -value; 128 } 129 return value; 130 } 131 /** 132 * Gets the simulated output voltage of the motor as a unit type. 133 * 134 * @return Voltage applied to the motor 135 */ 136 public final Voltage getMotorVoltageMeasure() { 137 return Volts.of(getMotorVoltage()); 138 } 139 140 /** 141 * Gets the simulated output torque current of the motor. 142 * <p> 143 * Phoenix 6 simulation automatically calculates current. 144 * 145 * @return Torque current applied to the motor in Amperes 146 */ 147 public final double getTorqueCurrent() { 148 double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "TorqueCurrent"); 149 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 150 value = -value; 151 } 152 return value; 153 } 154 /** 155 * Gets the simulated output torque current of the motor as a unit type. 156 * <p> 157 * Phoenix 6 simulation automatically calculates current. 158 * 159 * @return Torque current applied to the motor 160 */ 161 public final Current getTorqueCurrentMeasure() { 162 return Amps.of(getTorqueCurrent()); 163 } 164 165 /** 166 * Gets the simulated supply current of the TalonFXS. 167 * <p> 168 * Phoenix 6 simulation automatically calculates current. 169 * 170 * @return Supply current of the TalonFXS in Amperes 171 */ 172 public final double getSupplyCurrent() { 173 double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "SupplyCurrent"); 174 return value; 175 } 176 /** 177 * Gets the simulated supply current of the TalonFXS as a unit type. 178 * <p> 179 * Phoenix 6 simulation automatically calculates current. 180 * 181 * @return Supply current of the TalonFXS 182 */ 183 public final Current getSupplyCurrentMeasure() { 184 return Amps.of(getSupplyCurrent()); 185 } 186 187 /** 188 * Gets the simulated analog voltage of the TalonFXS. 189 * 190 * @return Voltage of the simulated analog input pin on the TalonFXS. 191 */ 192 public final double getAnalogVoltage() { 193 double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "AnalogVoltage"); 194 return value; 195 } 196 197 /** 198 * Gets the simulated analog voltage of the TalonFXS. 199 * 200 * @return Voltage of the simulated analog input pin on the TalonFXS. 201 */ 202 public final Voltage getAnalogVoltageMeasure() { 203 return Volts.of(getAnalogVoltage()); 204 } 205 206 /** 207 * Sets the simulated supply voltage of the TalonFXS. 208 * <p> 209 * The minimum allowed supply voltage is 4 V - values below this 210 * will be promoted to 4 V. 211 * 212 * @param volts The supply voltage in Volts 213 * @return Status code 214 */ 215 public final StatusCode setSupplyVoltage(double volts) { 216 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "SupplyVoltage", volts)); 217 } 218 /** 219 * Sets the simulated supply voltage of the TalonFXS. 220 * <p> 221 * The minimum allowed supply voltage is 4 V - values below this 222 * will be promoted to 4 V. 223 * 224 * @param voltage The supply voltage 225 * @return Status code 226 */ 227 public final StatusCode setSupplyVoltage(Voltage voltage) { 228 return setSupplyVoltage(voltage.in(Volts)); 229 } 230 231 /** 232 * Sets the simulated forward limit switch of the TalonFXS. 233 * 234 * @param closed Whether the limit switch is closed 235 * @return Status code 236 */ 237 public final StatusCode setForwardLimit(boolean closed) { 238 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "ForwardLimit", closed ? 1 : 0)); 239 } 240 /** 241 * Sets the simulated reverse limit switch of the TalonFXS. 242 * 243 * @param closed Whether the limit switch is closed 244 * @return Status code 245 */ 246 public final StatusCode setReverseLimit(boolean closed) { 247 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "ReverseLimit", closed ? 1 : 0)); 248 } 249 250 /** 251 * Sets the simulated raw rotor position of the TalonFXS. This is the position 252 * of the rotor (before gear ratio) used for the Commutation feedback source. 253 * <p> 254 * Inputs to this function over time should be continuous, as user calls of {@link TalonFXS#setPosition} will be accounted for in the callee. 255 * <p> 256 * The TalonFXS integrates this to calculate the true reported rotor position. 257 * <p> 258 * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}. 259 * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API. 260 * Changes to {@code rawPositionInput} will be integrated into the emulated position. 261 * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor. 262 * 263 * @param rotations The raw position in rotations 264 * @return Status code 265 */ 266 public final StatusCode setRawRotorPosition(double rotations) { 267 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 268 rotations = -rotations; 269 } 270 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RawRotorPosition", rotations)); 271 } 272 /** 273 * Sets the simulated raw rotor position of the TalonFXS. This is the position 274 * of the rotor (before gear ratio) used for the Commutation feedback source. 275 * <p> 276 * Inputs to this function over time should be continuous, as user calls of {@link TalonFXS#setPosition} will be accounted for in the callee. 277 * <p> 278 * The TalonFXS integrates this to calculate the true reported rotor position. 279 * <p> 280 * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}. 281 * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API. 282 * Changes to {@code rawPositionInput} will be integrated into the emulated position. 283 * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor. 284 * 285 * @param position The raw position 286 * @return Status code 287 */ 288 public final StatusCode setRawRotorPosition(Angle position) { 289 return setRawRotorPosition(position.in(Rotations)); 290 } 291 292 /** 293 * Sets the simulated voltage of the analog input pin on the TalonFXS data port. 294 * 295 * @param voltage Voltage of the pin 296 * @return Status code 297 */ 298 public final StatusCode setAnalogVoltage(double voltage) { 299 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "AnalogVoltage", voltage)); 300 } 301 302 /** 303 * Sets the simulated voltage of the analog input pin on the TalonFXS data port. 304 * 305 * @param voltage Voltage of the pin 306 * @return Status code 307 */ 308 public final StatusCode setAnalogVoltage(Voltage voltage) { 309 return setAnalogVoltage(voltage.in(Volts)); 310 } 311 312 /** 313 * Adds to the simulated rotor position of the TalonFXS. This adds to the position 314 * of the rotor (before gear ratio) used for the Commutation feedback source. 315 * 316 * @param dRotations The change in position in rotations 317 * @return Status code 318 */ 319 public final StatusCode addRotorPosition(double dRotations) { 320 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 321 dRotations = -dRotations; 322 } 323 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "AddRotorPosition", dRotations)); 324 } 325 /** 326 * Adds to the simulated rotor position of the TalonFXS. This adds to the position 327 * of the rotor (before gear ratio) used for the Commutation feedback source. 328 * 329 * @param dPosition The change in position 330 * @return Status code 331 */ 332 public final StatusCode addRotorPosition(Angle dPosition) { 333 return addRotorPosition(dPosition.in(Rotations)); 334 } 335 336 /** 337 * Sets the simulated rotor velocity of the TalonFXS. This is the velocity 338 * of the rotor (before gear ratio) used for the Commutation feedback source. 339 * 340 * @param rps The new velocity in rotations per second 341 * @return Status code 342 */ 343 public final StatusCode setRotorVelocity(double rps) { 344 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 345 rps = -rps; 346 } 347 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RotorVelocity", rps)); 348 } 349 /** 350 * Sets the simulated rotor velocity of the TalonFXS. This is the velocity 351 * of the rotor (before gear ratio) used for the Commutation feedback source. 352 * 353 * @param velocity The new velocity 354 * @return Status code 355 */ 356 public final StatusCode setRotorVelocity(AngularVelocity velocity) { 357 return setRotorVelocity(velocity.in(RotationsPerSecond)); 358 } 359 360 /** 361 * Sets the simulated rotor acceleration of the TalonFXS. This is the acceleration 362 * of the rotor (before gear ratio) used for the Commutation feedback source. 363 * 364 * @param rpss The new acceleration in rotations per second² 365 * @return Status code 366 */ 367 public final StatusCode setRotorAcceleration(double rpss) { 368 if (MotorOrientation == ChassisReference.Clockwise_Positive) { 369 rpss = -rpss; 370 } 371 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RotorAcceleration", rpss)); 372 } 373 /** 374 * Sets the simulated rotor acceleration of the TalonFXS. This is the acceleration 375 * of the rotor (before gear ratio) used for the Commutation feedback source. 376 * 377 * @param acceleration The new acceleration 378 * @return Status code 379 */ 380 public final StatusCode setRotorAcceleration(AngularAcceleration acceleration) { 381 return setRotorAcceleration(acceleration.in(RotationsPerSecondPerSecond)); 382 } 383 384 /** 385 * Sets the simulated raw quadrature position of the TalonFXS. This is the position 386 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 387 * <p> 388 * Inputs to this function over time should be continuous, as user calls of {@link TalonFXS#setPosition} will be accounted for in the callee. 389 * <p> 390 * The TalonFXS integrates this to calculate the true reported quadrature position. 391 * <p> 392 * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}. 393 * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API. 394 * Changes to {@code rawPositionInput} will be integrated into the emulated position. 395 * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor. 396 * 397 * @param rotations The raw position in rotations 398 * @return Status code 399 */ 400 public final StatusCode setRawQuadraturePosition(double rotations) { 401 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 402 rotations = -rotations; 403 } 404 return StatusCode.valueOf( 405 PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RawQuadraturePosition", rotations * QuadratureEdgesPerRotation) 406 ); 407 } 408 /** 409 * Sets the simulated raw quadrature position of the TalonFXS. This is the position 410 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 411 * <p> 412 * Inputs to this function over time should be continuous, as user calls of {@link TalonFXS#setPosition} will be accounted for in the callee. 413 * <p> 414 * The TalonFXS integrates this to calculate the true reported quadrature position. 415 * <p> 416 * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}. 417 * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API. 418 * Changes to {@code rawPositionInput} will be integrated into the emulated position. 419 * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor. 420 * 421 * @param position The raw position 422 * @return Status code 423 */ 424 public final StatusCode setRawQuadraturePosition(Angle position) { 425 return setRawQuadraturePosition(position.in(Rotations)); 426 } 427 428 /** 429 * Adds to the simulated quadrature position of the TalonFXS. This adds to the position 430 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 431 * 432 * @param dRotations The change in position in rotations 433 * @return Status code 434 */ 435 public final StatusCode addQuadraturePosition(double dRotations) { 436 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 437 dRotations = -dRotations; 438 } 439 return StatusCode.valueOf( 440 PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "AddQuadraturePosition", dRotations * QuadratureEdgesPerRotation) 441 ); 442 } 443 /** 444 * Adds to the simulated quadrature position of the TalonFXS. This adds to the position 445 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 446 * 447 * @param dPosition The change in position 448 * @return Status code 449 */ 450 public final StatusCode addQuadraturePosition(Angle dPosition) { 451 return addQuadraturePosition(dPosition.in(Rotations)); 452 } 453 454 /** 455 * Sets the simulated quadrature velocity of the TalonFXS. This is the velocity 456 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 457 * 458 * @param rps The new velocity in rotations per second 459 * @return Status code 460 */ 461 public final StatusCode setQuadratureVelocity(double rps) { 462 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 463 rps = -rps; 464 } 465 return StatusCode.valueOf( 466 PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "QuadratureVelocity", rps * QuadratureEdgesPerRotation) 467 ); 468 } 469 /** 470 * Sets the simulated quadrature velocity of the TalonFXS. This is the velocity 471 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 472 * 473 * @param velocity The new velocity 474 * @return Status code 475 */ 476 public final StatusCode setQuadratureVelocity(AngularVelocity velocity) { 477 return setQuadratureVelocity(velocity.in(RotationsPerSecond)); 478 } 479 480 /** 481 * Sets the simulated quadrature acceleration of the TalonFXS. This is the acceleration 482 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 483 * 484 * @param rpss The new acceleration in rotations per second² 485 * @return Status code 486 */ 487 public final StatusCode setQuadratureAcceleration(double rpss) { 488 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 489 rpss = -rpss; 490 } 491 return StatusCode.valueOf( 492 PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "QuadratureAcceleration", rpss * QuadratureEdgesPerRotation) 493 ); 494 } 495 /** 496 * Sets the simulated quadrature acceleration of the TalonFXS. This is the acceleration 497 * of an external quadrature encoder after any gear ratio between the rotor and the sensor. 498 * 499 * @param acceleration The new acceleration 500 * @return Status code 501 */ 502 public final StatusCode setQuadratureAcceleration(AngularAcceleration acceleration) { 503 return setQuadratureAcceleration(acceleration.in(RotationsPerSecondPerSecond)); 504 } 505 506 /** 507 * Sets the simulated pulse width position of the TalonFXS. This is the position 508 * of an external PWM encoder after any gear ratio between the rotor and the sensor. 509 * 510 * @param rotations The new position in rotations 511 * @return Status code 512 */ 513 public final StatusCode setPulseWidthPosition(double rotations) { 514 rotations -= PulseWidthSensorOffset; 515 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 516 rotations = -rotations; 517 } 518 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "PulseWidthPosition", rotations)); 519 } 520 /** 521 * Sets the simulated pulse width position of the TalonFXS. This is the position 522 * of an external PWM encoder after any gear ratio between the rotor and the sensor. 523 * 524 * @param position The new position 525 * @return Status code 526 */ 527 public final StatusCode setPulseWidthPosition(Angle position) { 528 return setPulseWidthPosition(position.in(Rotations)); 529 } 530 531 /** 532 * Sets the simulated pulse width velocity of the TalonFXS. This is the position 533 * of an external PWM encoder after any gear ratio between the rotor and the sensor. 534 * 535 * @param rps The new velocity in rotations per second 536 * @return Status code 537 */ 538 public final StatusCode setPulseWidthVelocity(double rps) { 539 if (ExtSensorOrientation == ChassisReference.Clockwise_Positive) { 540 rps = -rps; 541 } 542 return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "PulseWidthVelocity", rps)); 543 } 544 /** 545 * Sets the simulated pulse width velocity of the TalonFXS. This is the position 546 * of an external PWM encoder after any gear ratio between the rotor and the sensor. 547 * 548 * @param velocity The new velocity 549 * @return Status code 550 */ 551 public final StatusCode setPulseWidthVelocity(AngularVelocity velocity) { 552 return setPulseWidthVelocity(velocity.in(RotationsPerSecond)); 553 } 554}