SCRIPT Move java files

This commit is contained in:
PJ Reiniger
2025-11-07 19:55:40 -05:00
committed by Peter Johnson
parent 7ca1be9bae
commit c350c5f112
1486 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.wpilibj.ADXL345_I2C;
import java.util.Objects;
/** Class to control a simulated ADXL345. */
public class ADXL345Sim {
private SimDouble m_simX;
private SimDouble m_simY;
private SimDouble m_simZ;
/**
* Constructor.
*
* @param device The device to simulate
*/
public ADXL345Sim(ADXL345_I2C device) {
SimDeviceSim simDevice =
new SimDeviceSim(
"Accel:ADXL345_I2C" + "[" + device.getPort() + "," + device.getDeviceAddress() + "]");
initSim(simDevice);
}
private void initSim(SimDeviceSim simDevice) {
Objects.requireNonNull(simDevice);
m_simX = simDevice.getDouble("x");
m_simY = simDevice.getDouble("y");
m_simZ = simDevice.getDouble("z");
Objects.requireNonNull(m_simX);
Objects.requireNonNull(m_simY);
Objects.requireNonNull(m_simZ);
}
/**
* Sets the X acceleration.
*
* @param accel The X acceleration.
*/
public void setX(double accel) {
m_simX.set(accel);
}
/**
* Sets the Y acceleration.
*
* @param accel The Y acceleration.
*/
public void setY(double accel) {
m_simY.set(accel);
}
/**
* Sets the Z acceleration.
*
* @param accel The Z acceleration.
*/
public void setZ(double accel) {
m_simZ.set(accel);
}
}

View File

@@ -0,0 +1,178 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.AddressableLEDDataJNI;
import edu.wpi.first.hal.simulation.ConstBufferCallback;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.AddressableLED;
/** Class to control a simulated addressable LED. */
public class AddressableLEDSim {
private final int m_channel;
/**
* Constructs an addressable LED for a specific channel.
*
* @param channel output channel
*/
public AddressableLEDSim(int channel) {
m_channel = channel;
}
/**
* Constructs from an AddressableLED object.
*
* @param addressableLED AddressableLED to simulate
*/
public AddressableLEDSim(AddressableLED addressableLED) {
m_channel = addressableLED.getChannel();
}
/**
* Register a callback on the Initialized property.
*
* @param callback the callback that will be called whenever the Initialized property is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AddressableLEDDataJNI.registerInitializedCallback(m_channel, callback, initialNotify);
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelInitializedCallback);
}
/**
* Check if initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return AddressableLEDDataJNI.getInitialized(m_channel);
}
/**
* Change the Initialized value of the LED strip.
*
* @param initialized the new value
*/
public void setInitialized(boolean initialized) {
AddressableLEDDataJNI.setInitialized(m_channel, initialized);
}
/**
* Register a callback on the start.
*
* @param callback the callback that will be called whenever the start is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerStartCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AddressableLEDDataJNI.registerStartCallback(m_channel, callback, initialNotify);
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelStartCallback);
}
/**
* Get the start.
*
* @return the start
*/
public int getStart() {
return AddressableLEDDataJNI.getStart(m_channel);
}
/**
* Change the start.
*
* @param start the new start
*/
public void setStart(int start) {
AddressableLEDDataJNI.setStart(m_channel, start);
}
/**
* Register a callback on the length.
*
* @param callback the callback that will be called whenever the length is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerLengthCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AddressableLEDDataJNI.registerLengthCallback(m_channel, callback, initialNotify);
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelLengthCallback);
}
/**
* Get the length of the LED strip.
*
* @return the length
*/
public int getLength() {
return AddressableLEDDataJNI.getLength(m_channel);
}
/**
* Change the length of the LED strip.
*
* @param length the new value
*/
public void setLength(int length) {
AddressableLEDDataJNI.setLength(m_channel, length);
}
/**
* Register a callback on the LED data.
*
* @param callback the callback that will be called whenever the LED data is changed
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerDataCallback(ConstBufferCallback callback) {
int uid = AddressableLEDDataJNI.registerDataCallback(callback);
return new CallbackStore(uid, AddressableLEDDataJNI::cancelDataCallback);
}
/**
* Get the LED data.
*
* @return the LED data
*/
public byte[] getData() {
return getGlobalData(getStart(), getLength());
}
/**
* Change the LED data.
*
* @param data the new data
*/
public void setData(byte[] data) {
setGlobalData(getStart(), data);
}
/**
* Get the global LED data.
*
* @param start start, in LEDs
* @param length length, in LEDs
* @return the LED data
*/
public static byte[] getGlobalData(int start, int length) {
return AddressableLEDDataJNI.getData(start, length);
}
/**
* Change the global LED data.
*
* @param start start, in LEDs
* @param data the new data
*/
public static void setGlobalData(int start, byte[] data) {
AddressableLEDDataJNI.setData(start, data);
}
/** Reset all simulation data for this LED object. */
public void resetData() {
AddressableLEDDataJNI.resetData(m_channel);
}
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.wpilibj.AnalogEncoder;
/** Class to control a simulated analog encoder. */
public class AnalogEncoderSim {
private final SimDouble m_simPosition;
/**
* Constructs from an AnalogEncoder object.
*
* @param encoder AnalogEncoder to simulate
*/
public AnalogEncoderSim(AnalogEncoder encoder) {
SimDeviceSim wrappedSimDevice =
new SimDeviceSim("AnalogEncoder" + "[" + encoder.getChannel() + "]");
m_simPosition = wrappedSimDevice.getDouble("Position");
}
/**
* Set the position.
*
* @param value The position.
*/
public void set(double value) {
m_simPosition.set(value);
}
/**
* Get the simulated position.
*
* @return The simulated position.
*/
public double get() {
return m_simPosition.get();
}
}

View File

@@ -0,0 +1,158 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.AnalogInDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.AnalogInput;
/** Class to control a simulated analog input. */
public class AnalogInputSim {
private final int m_index;
/**
* Constructs from an AnalogInput object.
*
* @param analogInput AnalogInput to simulate
*/
public AnalogInputSim(AnalogInput analogInput) {
m_index = analogInput.getChannel();
}
/**
* Constructs from an analog input channel number.
*
* @param channel Channel number
*/
public AnalogInputSim(int channel) {
m_index = channel;
}
/**
* Register a callback on whether the analog input is initialized.
*
* @param callback the callback that will be called whenever the analog input is initialized
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AnalogInDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, AnalogInDataJNI::cancelInitializedCallback);
}
/**
* Check if this analog input has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return AnalogInDataJNI.getInitialized(m_index);
}
/**
* Change whether this analog input has been initialized.
*
* @param initialized the new value
*/
public void setInitialized(boolean initialized) {
AnalogInDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback on the number of average bits.
*
* @param callback the callback that will be called whenever the number of average bits is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerAverageBitsCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AnalogInDataJNI.registerAverageBitsCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, AnalogInDataJNI::cancelAverageBitsCallback);
}
/**
* Get the number of average bits.
*
* @return the number of average bits
*/
public int getAverageBits() {
return AnalogInDataJNI.getAverageBits(m_index);
}
/**
* Change the number of average bits.
*
* @param averageBits the new value
*/
public void setAverageBits(int averageBits) {
AnalogInDataJNI.setAverageBits(m_index, averageBits);
}
/**
* Register a callback on the amount of oversampling bits.
*
* @param callback the callback that will be called whenever the oversampling bits are changed.
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerOversampleBitsCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = AnalogInDataJNI.registerOversampleBitsCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, AnalogInDataJNI::cancelOversampleBitsCallback);
}
/**
* Get the amount of oversampling bits.
*
* @return the amount of oversampling bits
*/
public int getOversampleBits() {
return AnalogInDataJNI.getOversampleBits(m_index);
}
/**
* Change the amount of oversampling bits.
*
* @param oversampleBits the new value
*/
public void setOversampleBits(int oversampleBits) {
AnalogInDataJNI.setOversampleBits(m_index, oversampleBits);
}
/**
* Register a callback on the voltage.
*
* @param callback the callback that will be called whenever the voltage is changed.
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerVoltageCallback(NotifyCallback callback, boolean initialNotify) {
int uid = AnalogInDataJNI.registerVoltageCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, AnalogInDataJNI::cancelVoltageCallback);
}
/**
* Get the voltage.
*
* @return the voltage
*/
public double getVoltage() {
return AnalogInDataJNI.getVoltage(m_index);
}
/**
* Change the voltage.
*
* @param voltage the new value
*/
public void setVoltage(double voltage) {
AnalogInDataJNI.setVoltage(m_index, voltage);
}
/** Reset all simulation data for this object. */
public void resetData() {
AnalogInDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
/** A utility class to simulate the robot battery. */
public final class BatterySim {
private BatterySim() {
// Utility class
}
/**
* Calculate the loaded battery voltage. Use this with {@link RoboRioSim#setVInVoltage(double)} to
* set the simulated battery voltage, which can then be retrieved with the {@link
* edu.wpi.first.wpilibj.RobotController#getBatteryVoltage()} method.
*
* @param nominalVoltage The nominal battery voltage. Usually 12v.
* @param resistanceOhms The forward resistance of the battery. Most batteries are at or below 20
* milliohms.
* @param currents The currents drawn from the battery.
* @return The battery's voltage under load.
*/
public static double calculateLoadedBatteryVoltage(
double nominalVoltage, double resistanceOhms, double... currents) {
double retval = nominalVoltage;
for (var current : currents) {
retval -= current * resistanceOhms;
}
return Math.max(0.0, retval);
}
/**
* Calculate the loaded battery voltage. Use this with {@link RoboRioSim#setVInVoltage(double)} to
* set the simulated battery voltage, which can then be retrieved with the {@link
* edu.wpi.first.wpilibj.RobotController#getBatteryVoltage()} method. This function assumes a
* nominal voltage of 12v and a resistance of 20 milliohms (0.020 ohms)
*
* @param currents The currents drawn from the battery.
* @return The battery's voltage under load.
*/
public static double calculateDefaultBatteryLoadedVoltage(double... currents) {
return calculateLoadedBatteryVoltage(12, 0.02, currents);
}
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.CTREPCMDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.PneumaticsControlModule;
import edu.wpi.first.wpilibj.SensorUtil;
/** Class to control a simulated Pneumatic Control Module (PCM). */
public class CTREPCMSim extends PneumaticsBaseSim {
/** Constructs for the default PCM. */
public CTREPCMSim() {
super(SensorUtil.getDefaultCTREPCMModule());
}
/**
* Constructs from a PCM module number (CAN ID).
*
* @param module module number
*/
public CTREPCMSim(int module) {
super(module);
}
/**
* Constructs from a PneumaticsControlModule object.
*
* @param module PCM module to simulate
*/
public CTREPCMSim(PneumaticsControlModule module) {
super(module);
}
/**
* Check whether the closed loop compressor control is active.
*
* @return true if active
*/
public boolean getClosedLoopEnabled() {
return CTREPCMDataJNI.getClosedLoopEnabled(m_index);
}
/**
* Turn on/off the closed loop control of the compressor.
*
* @param closedLoopEnabled whether the control loop is active
*/
public void setClosedLoopEnabled(boolean closedLoopEnabled) {
CTREPCMDataJNI.setClosedLoopEnabled(m_index, closedLoopEnabled);
}
/**
* Register a callback to be run whenever the closed loop state changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerClosedLoopEnabledCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = CTREPCMDataJNI.registerClosedLoopEnabledCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, CTREPCMDataJNI::cancelClosedLoopEnabledCallback);
}
@Override
public boolean getInitialized() {
return CTREPCMDataJNI.getInitialized(m_index);
}
@Override
public void setInitialized(boolean initialized) {
CTREPCMDataJNI.setInitialized(m_index, initialized);
}
@Override
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = CTREPCMDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, CTREPCMDataJNI::cancelInitializedCallback);
}
@Override
public boolean getCompressorOn() {
return CTREPCMDataJNI.getCompressorOn(m_index);
}
@Override
public void setCompressorOn(boolean compressorOn) {
CTREPCMDataJNI.setCompressorOn(m_index, compressorOn);
}
@Override
public CallbackStore registerCompressorOnCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = CTREPCMDataJNI.registerCompressorOnCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, CTREPCMDataJNI::cancelCompressorOnCallback);
}
@Override
public boolean getSolenoidOutput(int channel) {
return CTREPCMDataJNI.getSolenoidOutput(m_index, channel);
}
@Override
public void setSolenoidOutput(int channel, boolean solenoidOutput) {
CTREPCMDataJNI.setSolenoidOutput(m_index, channel, solenoidOutput);
}
@Override
public CallbackStore registerSolenoidOutputCallback(
int channel, NotifyCallback callback, boolean initialNotify) {
int uid =
CTREPCMDataJNI.registerSolenoidOutputCallback(m_index, channel, callback, initialNotify);
return new CallbackStore(m_index, channel, uid, CTREPCMDataJNI::cancelSolenoidOutputCallback);
}
@Override
public boolean getPressureSwitch() {
return CTREPCMDataJNI.getPressureSwitch(m_index);
}
@Override
public void setPressureSwitch(boolean pressureSwitch) {
CTREPCMDataJNI.setPressureSwitch(m_index, pressureSwitch);
}
@Override
public CallbackStore registerPressureSwitchCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = CTREPCMDataJNI.registerPressureSwitchCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, CTREPCMDataJNI::cancelPressureSwitchCallback);
}
@Override
public double getCompressorCurrent() {
return CTREPCMDataJNI.getCompressorCurrent(m_index);
}
@Override
public void setCompressorCurrent(double compressorCurrent) {
CTREPCMDataJNI.setCompressorCurrent(m_index, compressorCurrent);
}
@Override
public CallbackStore registerCompressorCurrentCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = CTREPCMDataJNI.registerCompressorCurrentCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, CTREPCMDataJNI::cancelCompressorCurrentCallback);
}
@Override
public void resetData() {
CTREPCMDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,112 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
/** Manages simulation callbacks; each object is associated with a callback. */
public class CallbackStore implements AutoCloseable {
/** <b>Note: This interface is for simulation classes only. It should not be used by teams!</b> */
@SuppressWarnings("PMD.ImplicitFunctionalInterface")
interface CancelCallbackFunc {
void cancel(int index, int uid);
}
/** <b>Note: This interface is for simulation classes only. It should not be used by teams!</b> */
@SuppressWarnings("PMD.ImplicitFunctionalInterface")
interface CancelCallbackChannelFunc {
void cancel(int index, int channel, int uid);
}
/** <b>Note: This interface is for simulation classes only. It should not be used by teams!</b> */
@SuppressWarnings("PMD.ImplicitFunctionalInterface")
interface CancelCallbackNoIndexFunc {
void cancel(int uid);
}
/**
* Constructs an empty CallbackStore. This constructor is to allow 3rd party sim providers (eg
* vendors) to subclass this class (without needing provide dummy constructing parameters) so that
* the register methods of their sim classes can return CallbackStores like the builtin sims.
* <b>Note: It should not be called by teams that are just using sims!</b>
*/
protected CallbackStore() {
this.m_cancelType = -1;
this.m_index = -1;
this.m_uid = -1;
this.m_cancelCallback = null;
this.m_cancelCallbackChannel = null;
}
/**
* <b>Note: This constructor is for simulation classes only. It should not be called by teams!</b>
*
* @param index TODO
* @param uid TODO
* @param ccf TODO
*/
public CallbackStore(int index, int uid, CancelCallbackFunc ccf) {
this.m_cancelType = kNormalCancel;
this.m_index = index;
this.m_uid = uid;
this.m_cancelCallback = ccf;
}
/**
* <b>Note: This constructor is for simulation classes only. It should not be called by teams!</b>
*
* @param index TODO
* @param channel TODO
* @param uid TODO
* @param ccf TODO
*/
public CallbackStore(int index, int channel, int uid, CancelCallbackChannelFunc ccf) {
this.m_cancelType = kChannelCancel;
this.m_index = index;
this.m_uid = uid;
this.m_channel = channel;
this.m_cancelCallbackChannel = ccf;
}
/**
* <b>Note: This constructor is for simulation classes only. It should not be called by teams!</b>
*
* @param uid TODO
* @param ccf TODO
*/
public CallbackStore(int uid, CancelCallbackNoIndexFunc ccf) {
this.m_cancelType = kNoIndexCancel;
this.m_uid = uid;
this.m_cancelCallbackNoIndex = ccf;
}
private int m_index;
private int m_channel;
private final int m_uid;
private CancelCallbackFunc m_cancelCallback;
private CancelCallbackChannelFunc m_cancelCallbackChannel;
private CancelCallbackNoIndexFunc m_cancelCallbackNoIndex;
private static final int kAlreadyCancelled = -1;
private static final int kNormalCancel = 0;
private static final int kChannelCancel = 1;
private static final int kNoIndexCancel = 2;
private int m_cancelType;
/** Cancel the callback associated with this object. */
@Override
public void close() {
switch (m_cancelType) {
case kAlreadyCancelled -> {
// Already cancelled so do nothing so that close() is idempotent.
return;
}
case kNormalCancel -> m_cancelCallback.cancel(m_index, m_uid);
case kChannelCancel -> m_cancelCallbackChannel.cancel(m_index, m_channel, m_uid);
case kNoIndexCancel -> m_cancelCallbackNoIndex.cancel(m_uid);
default -> {
assert false;
}
}
m_cancelType = kAlreadyCancelled;
}
}

View File

@@ -0,0 +1,186 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N2;
import edu.wpi.first.math.system.LinearSystem;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.wpilibj.RobotController;
/** Represents a simulated DC motor mechanism. */
public class DCMotorSim extends LinearSystemSim<N2, N1, N2> {
// Gearbox for the DC motor.
private final DCMotor m_gearbox;
// The gearing from the motors to the output.
private final double m_gearing;
// The moment of inertia for the DC motor mechanism in kg-m².
private final double m_j;
/**
* Creates a simulated DC motor mechanism.
*
* @param plant The linear system representing the DC motor. This system can be created with
* {@link edu.wpi.first.math.system.plant.LinearSystemId#createDCMotorSystem(DCMotor, double,
* double)} or {@link
* edu.wpi.first.math.system.plant.LinearSystemId#createDCMotorSystem(double, double)}. If
* {@link edu.wpi.first.math.system.plant.LinearSystemId#createDCMotorSystem(double, double)}
* is used, the distance unit must be radians.
* @param gearbox The type of and number of motors in the DC motor gearbox.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 2 elements. The first element is for position. The
* second element is for velocity.
*/
public DCMotorSim(LinearSystem<N2, N1, N2> plant, DCMotor gearbox, double... measurementStdDevs) {
super(plant, measurementStdDevs);
m_gearbox = gearbox;
// By theorem 6.10.1 of https://file.tavsys.net/control/controls-engineering-in-frc.pdf,
// the DC motor state-space model is:
//
// dx/dt = -G²Kₜ/(KᵥRJ)x + (GKₜ)/(RJ)u
// A = -G²Kₜ/(KᵥRJ)
// B = GKₜ/(RJ)
//
// Solve for G.
//
// A/B = -G/Kᵥ
// G = -KᵥA/B
//
// Solve for J.
//
// B = GKₜ/(RJ)
// J = GKₜ/(RB)
m_gearing = -gearbox.Kv * plant.getA(1, 1) / plant.getB(1, 0);
m_j = m_gearing * gearbox.Kt / (gearbox.R * plant.getB(1, 0));
}
/**
* Sets the state of the DC motor.
*
* @param angularPosition The new position in radians.
* @param angularVelocity The new velocity in radians per second.
*/
public void setState(double angularPosition, double angularVelocity) {
setState(VecBuilder.fill(angularPosition, angularVelocity));
}
/**
* Sets the DC motor's angular position.
*
* @param angularPosition The new position in radians.
*/
public void setAngle(double angularPosition) {
setState(angularPosition, getAngularVelocity());
}
/**
* Sets the DC motor's angular velocity.
*
* @param angularVelocity The new velocity in radians per second.
*/
public void setAngularVelocity(double angularVelocity) {
setState(getAngularPosition(), angularVelocity);
}
/**
* Returns the gear ratio of the DC motor.
*
* @return the DC motor's gear ratio.
*/
public double getGearing() {
return m_gearing;
}
/**
* Returns the moment of inertia of the DC motor.
*
* @return The DC motor's moment of inertia in kg-m².
*/
public double getJ() {
return m_j;
}
/**
* Returns the gearbox for the DC motor.
*
* @return The DC motor's gearbox.
*/
public DCMotor getGearbox() {
return m_gearbox;
}
/**
* Returns the DC motor's position.
*
* @return The DC motor's position in radians.
*/
public double getAngularPosition() {
return getOutput(0);
}
/**
* Returns the DC motor's velocity.
*
* @return The DC motor's velocity in radians per second.
*/
public double getAngularVelocity() {
return getOutput(1);
}
/**
* Returns the DC motor's acceleration.
*
* @return The DC motor's acceleration in rad/s².
*/
public double getAngularAcceleration() {
var acceleration = (m_plant.getA().times(m_x)).plus(m_plant.getB().times(m_u));
return acceleration.get(1, 0);
}
/**
* Returns the DC motor's torque in.
*
* @return The DC motor's torque in Newton-meters.
*/
public double getTorque() {
return getAngularAcceleration() * m_j;
}
/**
* Returns the DC motor's current draw.
*
* @return The DC motor's current draw in amps.
*/
public double getCurrentDraw() {
// I = V / R - omega / (Kv * R)
// Reductions are output over input, so a reduction of 2:1 means the motor is spinning
// 2x faster than the output
return m_gearbox.getCurrent(m_x.get(1, 0) * m_gearing, m_u.get(0, 0))
* Math.signum(m_u.get(0, 0));
}
/**
* Gets the input voltage for the DC motor.
*
* @return The DC motor's input voltage.
*/
public double getInputVoltage() {
return getInput(0);
}
/**
* Sets the input voltage for the DC motor.
*
* @param volts The input voltage.
*/
public void setInputVoltage(double volts) {
setInput(volts);
clampInput(RobotController.getBatteryVoltage());
}
}

View File

@@ -0,0 +1,197 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.DIODataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.DigitalOutput;
/** Class to control a simulated digital input or output. */
public class DIOSim {
private final int m_index;
/**
* Constructs from a DigitalInput object.
*
* @param input DigitalInput to simulate
*/
public DIOSim(DigitalInput input) {
m_index = input.getChannel();
}
/**
* Constructs from a DigitalOutput object.
*
* @param output DigitalOutput to simulate
*/
public DIOSim(DigitalOutput output) {
m_index = output.getChannel();
}
/**
* Constructs from a digital I/O channel number.
*
* @param channel Channel number
*/
public DIOSim(int channel) {
m_index = channel;
}
/**
* Register a callback to be run when this DIO is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DIODataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DIODataJNI::cancelInitializedCallback);
}
/**
* Check whether this DIO has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return DIODataJNI.getInitialized(m_index);
}
/**
* Define whether this DIO has been initialized.
*
* @param initialized whether this object is initialized
*/
public void setInitialized(boolean initialized) {
DIODataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run whenever the DIO value changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerValueCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DIODataJNI.registerValueCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DIODataJNI::cancelValueCallback);
}
/**
* Read the value of the DIO port.
*
* @return the DIO value
*/
public boolean getValue() {
return DIODataJNI.getValue(m_index);
}
/**
* Change the DIO value.
*
* @param value the new value
*/
public void setValue(boolean value) {
DIODataJNI.setValue(m_index, value);
}
/**
* Register a callback to be run whenever the pulse length changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerPulseLengthCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DIODataJNI.registerPulseLengthCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DIODataJNI::cancelPulseLengthCallback);
}
/**
* Read the pulse length.
*
* @return the pulse length of this DIO port
*/
public double getPulseLength() {
return DIODataJNI.getPulseLength(m_index);
}
/**
* Change the pulse length of this DIO port.
*
* @param pulseLength the new pulse length
*/
public void setPulseLength(double pulseLength) {
DIODataJNI.setPulseLength(m_index, pulseLength);
}
/**
* Register a callback to be run whenever this DIO changes to be an input.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerIsInputCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DIODataJNI.registerIsInputCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DIODataJNI::cancelIsInputCallback);
}
/**
* Check whether this DIO port is currently an Input.
*
* @return true if Input
*/
public boolean getIsInput() {
return DIODataJNI.getIsInput(m_index);
}
/**
* Define whether this DIO port is an Input.
*
* @param isInput whether this DIO should be an Input
*/
public void setIsInput(boolean isInput) {
DIODataJNI.setIsInput(m_index, isInput);
}
/**
* Register a callback to be run whenever the filter index changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerFilterIndexCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DIODataJNI.registerFilterIndexCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DIODataJNI::cancelFilterIndexCallback);
}
/**
* Read the filter index.
*
* @return the filter index of this DIO port
*/
public int getFilterIndex() {
return DIODataJNI.getFilterIndex(m_index);
}
/**
* Change the filter index of this DIO port.
*
* @param filterIndex the new filter index
*/
public void setFilterIndex(int filterIndex) {
DIODataJNI.setFilterIndex(m_index, filterIndex);
}
/** Reset all simulation data of this object. */
public void resetData() {
DIODataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,493 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.Nat;
import edu.wpi.first.math.StateSpaceUtil;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N2;
import edu.wpi.first.math.numbers.N7;
import edu.wpi.first.math.system.LinearSystem;
import edu.wpi.first.math.system.NumericalIntegration;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.math.system.plant.LinearSystemId;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.RobotController;
/**
* This class simulates the state of the drivetrain. In simulationPeriodic, users should first set
* inputs from motors with {@link #setInputs(double, double)}, call {@link #update(double)} to
* update the simulation, and set estimated encoder and gyro positions, as well as estimated
* odometry pose. Teams can use {@link edu.wpi.first.wpilibj.smartdashboard.Field2d} to visualize
* their robot on the Sim GUI's field.
*
* <p>Our state-space system is:
*
* <p>x = [[x, y, theta, vel_l, vel_r, dist_l, dist_r]]ᵀ in the field coordinate system (dist_* are
* wheel distances.)
*
* <p>u = [[voltage_l, voltage_r]]ᵀ This is typically the control input of the last timestep from a
* LTVDiffDriveController.
*
* <p>y = x
*/
public class DifferentialDrivetrainSim {
private final DCMotor m_motor;
private final double m_originalGearing;
private final Matrix<N7, N1> m_measurementStdDevs;
private double m_currentGearing;
private final double m_wheelRadius;
private Matrix<N2, N1> m_u;
private Matrix<N7, N1> m_x;
private Matrix<N7, N1> m_y;
private final double m_rb;
private final LinearSystem<N2, N2, N2> m_plant;
/**
* Creates a simulated differential drivetrain.
*
* @param driveMotor A {@link DCMotor} representing the left side of the drivetrain.
* @param gearing The gearing ratio between motor and wheel, as output over input. This must be
* the same ratio as the ratio used to identify or create the drivetrainPlant.
* @param j The moment of inertia of the drivetrain about its center in kg-m².
* @param mass The mass of the drivebase in kg.
* @param wheelRadius The radius of the wheels on the drivetrain in meters.
* @param trackwidth The robot's trackwidth, or distance between left and right wheels, in meters.
* @param measurementStdDevs Standard deviations for measurements, in the form [x, y, heading,
* left velocity, right velocity, left distance, right distance]ᵀ. Can be null if no noise is
* desired. Gyro standard deviations of 0.0001 radians, velocity standard deviations of 0.05
* m/s, and position measurement standard deviations of 0.005 meters are a reasonable starting
* point.
*/
public DifferentialDrivetrainSim(
DCMotor driveMotor,
double gearing,
double j,
double mass,
double wheelRadius,
double trackwidth,
Matrix<N7, N1> measurementStdDevs) {
this(
LinearSystemId.createDrivetrainVelocitySystem(
driveMotor, mass, wheelRadius, trackwidth / 2.0, j, gearing),
driveMotor,
gearing,
trackwidth,
wheelRadius,
measurementStdDevs);
}
/**
* Creates a simulated differential drivetrain.
*
* @param plant The {@link LinearSystem} representing the robot's drivetrain. This system can be
* created with {@link
* edu.wpi.first.math.system.plant.LinearSystemId#createDrivetrainVelocitySystem(DCMotor,
* double, double, double, double, double)} or {@link
* edu.wpi.first.math.system.plant.LinearSystemId#identifyDrivetrainSystem(double, double,
* double, double)}.
* @param driveMotor A {@link DCMotor} representing the drivetrain.
* @param gearing The gearingRatio ratio of the robot, as output over input. This must be the same
* ratio as the ratio used to identify or create the drivetrainPlant.
* @param trackwidth The distance between the two sides of the drivetrain in meters. Can be found
* with SysId.
* @param wheelRadius The radius of the wheels on the drivetrain, in meters.
* @param measurementStdDevs Standard deviations for measurements, in the form [x, y, heading,
* left velocity, right velocity, left distance, right distance]ᵀ. Can be null if no noise is
* desired. Gyro standard deviations of 0.0001 radians, velocity standard deviations of 0.05
* m/s, and position measurement standard deviations of 0.005 meters are a reasonable starting
* point.
*/
public DifferentialDrivetrainSim(
LinearSystem<N2, N2, N2> plant,
DCMotor driveMotor,
double gearing,
double trackwidth,
double wheelRadius,
Matrix<N7, N1> measurementStdDevs) {
this.m_plant = plant;
this.m_rb = trackwidth / 2.0;
this.m_motor = driveMotor;
this.m_originalGearing = gearing;
this.m_measurementStdDevs = measurementStdDevs;
m_wheelRadius = wheelRadius;
m_currentGearing = m_originalGearing;
m_x = new Matrix<>(Nat.N7(), Nat.N1());
m_u = VecBuilder.fill(0, 0);
m_y = new Matrix<>(Nat.N7(), Nat.N1());
}
/**
* Sets the applied voltage to the drivetrain. Note that positive voltage must make that side of
* the drivetrain travel forward (+X).
*
* @param leftVoltageVolts The left voltage.
* @param rightVoltageVolts The right voltage.
*/
public void setInputs(double leftVoltageVolts, double rightVoltageVolts) {
m_u = clampInput(VecBuilder.fill(leftVoltageVolts, rightVoltageVolts));
}
/**
* Update the drivetrain states with the current time difference.
*
* @param dt the time difference in seconds
*/
public void update(double dt) {
m_x = NumericalIntegration.rkdp(this::getDynamics, m_x, m_u, dt);
m_y = m_x;
if (m_measurementStdDevs != null) {
m_y = m_y.plus(StateSpaceUtil.makeWhiteNoiseVector(m_measurementStdDevs));
}
}
/** Returns the full simulated state of the drivetrain. */
Matrix<N7, N1> getState() {
return m_x;
}
/**
* Get one of the drivetrain states.
*
* @param state the state to get
* @return the state
*/
double getState(State state) {
return m_x.get(state.value, 0);
}
private double getOutput(State output) {
return m_y.get(output.value, 0);
}
/**
* Returns the direction the robot is pointing.
*
* <p>Note that this angle is counterclockwise-positive, while most gyros are clockwise positive.
*
* @return The direction the robot is pointing.
*/
public Rotation2d getHeading() {
return new Rotation2d(getOutput(State.kHeading));
}
/**
* Returns the current pose.
*
* @return The current pose.
*/
public Pose2d getPose() {
return new Pose2d(getOutput(State.kX), getOutput(State.kY), getHeading());
}
/**
* Get the right encoder position.
*
* @return The encoder position in meters.
*/
public double getRightPosition() {
return getOutput(State.kRightPosition);
}
/**
* Get the right encoder velocity.
*
* @return The encoder velocity in meters per second.
*/
public double getRightVelocity() {
return getOutput(State.kRightVelocity);
}
/**
* Get the left encoder position.
*
* @return The encoder position in meters.
*/
public double getLeftPosition() {
return getOutput(State.kLeftPosition);
}
/**
* Get the left encoder velocity.
*
* @return The encoder velocity in meters per second.
*/
public double getLeftVelocity() {
return getOutput(State.kLeftVelocity);
}
/**
* Get the current draw of the left side of the drivetrain.
*
* @return the drivetrain's left side current draw, in amps
*/
public double getLeftCurrentDraw() {
return m_motor.getCurrent(
getState(State.kLeftVelocity) * m_currentGearing / m_wheelRadius, m_u.get(0, 0))
* Math.signum(m_u.get(0, 0));
}
/**
* Get the current draw of the right side of the drivetrain.
*
* @return the drivetrain's right side current draw, in amps
*/
public double getRightCurrentDraw() {
return m_motor.getCurrent(
getState(State.kRightVelocity) * m_currentGearing / m_wheelRadius, m_u.get(1, 0))
* Math.signum(m_u.get(1, 0));
}
/**
* Get the current draw of the drivetrain.
*
* @return the current draw, in amps
*/
public double getCurrentDraw() {
return getLeftCurrentDraw() + getRightCurrentDraw();
}
/**
* Get the drivetrain gearing.
*
* @return the gearing ration
*/
public double getCurrentGearing() {
return m_currentGearing;
}
/**
* Sets the gearing reduction on the drivetrain. This is commonly used for shifting drivetrains.
*
* @param newGearRatio The new gear ratio, as output over input.
*/
public void setCurrentGearing(double newGearRatio) {
this.m_currentGearing = newGearRatio;
}
/**
* Sets the system state.
*
* @param state The state.
*/
public void setState(Matrix<N7, N1> state) {
m_x = state;
}
/**
* Sets the system pose.
*
* @param pose The pose.
*/
public void setPose(Pose2d pose) {
m_x.set(State.kX.value, 0, pose.getX());
m_x.set(State.kY.value, 0, pose.getY());
m_x.set(State.kHeading.value, 0, pose.getRotation().getRadians());
m_x.set(State.kLeftPosition.value, 0, 0);
m_x.set(State.kRightPosition.value, 0, 0);
}
/**
* The differential drive dynamics function.
*
* @param x The state.
* @param u The input.
* @return The state derivative with respect to time.
*/
protected Matrix<N7, N1> getDynamics(Matrix<N7, N1> x, Matrix<N2, N1> u) {
// Because G can be factored out of B, we can divide by the old ratio and multiply
// by the new ratio to get a new drivetrain model.
var B = new Matrix<>(Nat.N4(), Nat.N2());
B.assignBlock(0, 0, m_plant.getB().times(this.m_currentGearing / this.m_originalGearing));
// Because G² can be factored out of A, we can divide by the old ratio squared and multiply
// by the new ratio squared to get a new drivetrain model.
var A = new Matrix<>(Nat.N4(), Nat.N4());
A.assignBlock(
0,
0,
m_plant
.getA()
.times(
(this.m_currentGearing * this.m_currentGearing)
/ (this.m_originalGearing * this.m_originalGearing)));
A.assignBlock(2, 0, Matrix.eye(Nat.N2()));
var v = (x.get(State.kLeftVelocity.value, 0) + x.get(State.kRightVelocity.value, 0)) / 2.0;
var xdot = new Matrix<>(Nat.N7(), Nat.N1());
xdot.set(0, 0, v * Math.cos(x.get(State.kHeading.value, 0)));
xdot.set(1, 0, v * Math.sin(x.get(State.kHeading.value, 0)));
xdot.set(
2,
0,
(x.get(State.kRightVelocity.value, 0) - x.get(State.kLeftVelocity.value, 0))
/ (2.0 * m_rb));
xdot.assignBlock(3, 0, A.times(x.block(Nat.N4(), Nat.N1(), 3, 0)).plus(B.times(u)));
return xdot;
}
/**
* Clamp the input vector such that no element exceeds the battery voltage. If any does, the
* relative magnitudes of the input will be maintained.
*
* @param u The input vector.
* @return The normalized input.
*/
protected Matrix<N2, N1> clampInput(Matrix<N2, N1> u) {
return StateSpaceUtil.desaturateInputVector(u, RobotController.getBatteryVoltage());
}
/** Represents the different states of the drivetrain. */
enum State {
kX(0),
kY(1),
kHeading(2),
kLeftVelocity(3),
kRightVelocity(4),
kLeftPosition(5),
kRightPosition(6);
public final int value;
State(int i) {
this.value = i;
}
}
/**
* Represents a gearing option of the Toughbox mini. 12.75:1 -- 14:50 and 14:50 10.71:1 -- 14:50
* and 16:48 8.45:1 -- 14:50 and 19:45 7.31:1 -- 14:50 and 21:43 5.95:1 -- 14:50 and 24:40
*/
public enum KitbotGearing {
/** Gear ratio of 12.75:1. */
k12p75(12.75),
/** Gear ratio of 10.71:1. */
k10p71(10.71),
/** Gear ratio of 8.45:1. */
k8p45(8.45),
/** Gear ratio of 7.31:1. */
k7p31(7.31),
/** Gear ratio of 5.95:1. */
k5p95(5.95);
/** KitbotGearing value. */
public final double value;
KitbotGearing(double i) {
this.value = i;
}
}
/** Represents common motor layouts of the kit drivetrain. */
public enum KitbotMotor {
/** One CIM motor per drive side. */
kSingleCIMPerSide(DCMotor.getCIM(1)),
/** Two CIM motors per drive side. */
kDualCIMPerSide(DCMotor.getCIM(2)),
/** One Mini CIM motor per drive side. */
kSingleMiniCIMPerSide(DCMotor.getMiniCIM(1)),
/** Two Mini CIM motors per drive side. */
kDualMiniCIMPerSide(DCMotor.getMiniCIM(2)),
/** One Falcon 500 motor per drive side. */
kSingleFalcon500PerSide(DCMotor.getFalcon500(1)),
/** Two Falcon 500 motors per drive side. */
kDoubleFalcon500PerSide(DCMotor.getFalcon500(2)),
/** One NEO motor per drive side. */
kSingleNEOPerSide(DCMotor.getNEO(1)),
/** Two NEO motors per drive side. */
kDoubleNEOPerSide(DCMotor.getNEO(2));
/** KitbotMotor value. */
public final DCMotor value;
KitbotMotor(DCMotor i) {
this.value = i;
}
}
/** Represents common wheel sizes of the kit drivetrain. */
public enum KitbotWheelSize {
/** Six inch diameter wheels. */
kSixInch(Units.inchesToMeters(6)),
/** Eight inch diameter wheels. */
kEightInch(Units.inchesToMeters(8)),
/** Ten inch diameter wheels. */
kTenInch(Units.inchesToMeters(10));
/** KitbotWheelSize value. */
public final double value;
KitbotWheelSize(double i) {
this.value = i;
}
}
/**
* Create a sim for the standard FRC kitbot.
*
* @param motor The motors installed in the bot.
* @param gearing The gearing reduction used.
* @param wheelSize The wheel size.
* @param measurementStdDevs Standard deviations for measurements, in the form [x, y, heading,
* left velocity, right velocity, left distance, right distance]ᵀ. Can be null if no noise is
* desired. Gyro standard deviations of 0.0001 radians, velocity standard deviations of 0.05
* m/s, and position measurement standard deviations of 0.005 meters are a reasonable starting
* point.
* @return A sim for the standard FRC kitbot.
*/
public static DifferentialDrivetrainSim createKitbotSim(
KitbotMotor motor,
KitbotGearing gearing,
KitbotWheelSize wheelSize,
Matrix<N7, N1> measurementStdDevs) {
// MOI estimation -- note that I = mr² for point masses
var batteryMoi = 12.5 / 2.2 * Math.pow(Units.inchesToMeters(10), 2);
var gearboxMoi =
(2.8 /* CIM motor */ * 2 / 2.2 + 2.0 /* Toughbox Mini- ish */)
* Math.pow(Units.inchesToMeters(26.0 / 2.0), 2);
return createKitbotSim(motor, gearing, wheelSize, batteryMoi + gearboxMoi, measurementStdDevs);
}
/**
* Create a sim for the standard FRC kitbot.
*
* @param motor The motors installed in the bot.
* @param gearing The gearing reduction used.
* @param wheelSize The wheel size.
* @param j The moment of inertia of the drivebase in kg-m². This can be calculated using SysId.
* @param measurementStdDevs Standard deviations for measurements, in the form [x, y, heading,
* left velocity, right velocity, left distance, right distance]ᵀ. Can be null if no noise is
* desired. Gyro standard deviations of 0.0001 radians, velocity standard deviations of 0.05
* m/s, and position measurement standard deviations of 0.005 meters are a reasonable starting
* point.
* @return A sim for the standard FRC kitbot.
*/
public static DifferentialDrivetrainSim createKitbotSim(
KitbotMotor motor,
KitbotGearing gearing,
KitbotWheelSize wheelSize,
double j,
Matrix<N7, N1> measurementStdDevs) {
return new DifferentialDrivetrainSim(
motor.value,
gearing.value,
j,
Units.lbsToKilograms(60),
wheelSize.value / 2.0,
Units.inchesToMeters(26),
measurementStdDevs);
}
}

View File

@@ -0,0 +1,154 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.DigitalPWMDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.DigitalOutput;
import java.util.NoSuchElementException;
/**
* Class to control a simulated digital PWM output.
*
* <p>This is for duty cycle PWM outputs on a DigitalOutput, not for the servo style PWM outputs on
* a PWM channel.
*/
public class DigitalPWMSim {
private final int m_index;
/**
* Constructs from a DigitalOutput object.
*
* @param digitalOutput DigitalOutput to simulate
*/
public DigitalPWMSim(DigitalOutput digitalOutput) {
m_index = digitalOutput.getChannel();
}
private DigitalPWMSim(int index) {
m_index = index;
}
/**
* Creates an DigitalPWMSim for a digital I/O channel.
*
* @param channel DIO channel
* @return Simulated object
* @throws NoSuchElementException if no Digital PWM is configured for that channel
*/
public static DigitalPWMSim createForChannel(int channel) {
int index = DigitalPWMDataJNI.findForChannel(channel);
if (index < 0) {
throw new NoSuchElementException("no digital PWM found for channel " + channel);
}
return new DigitalPWMSim(index);
}
/**
* Creates an DigitalPWMSim for a simulated index. The index is incremented for each simulated
* DigitalPWM.
*
* @param index simulator index
* @return Simulated object
*/
public static DigitalPWMSim createForIndex(int index) {
return new DigitalPWMSim(index);
}
/**
* Register a callback to be run when this PWM output is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DigitalPWMDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DigitalPWMDataJNI::cancelInitializedCallback);
}
/**
* Check whether this PWM output has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return DigitalPWMDataJNI.getInitialized(m_index);
}
/**
* Define whether this PWM output has been initialized.
*
* @param initialized whether this object is initialized
*/
public void setInitialized(boolean initialized) {
DigitalPWMDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run whenever the duty cycle value changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerDutyCycleCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DigitalPWMDataJNI.registerDutyCycleCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DigitalPWMDataJNI::cancelDutyCycleCallback);
}
/**
* Read the duty cycle value.
*
* @return the duty cycle value of this PWM output
*/
public double getDutyCycle() {
return DigitalPWMDataJNI.getDutyCycle(m_index);
}
/**
* Set the duty cycle value of this PWM output.
*
* @param dutyCycle the new value
*/
public void setDutyCycle(double dutyCycle) {
DigitalPWMDataJNI.setDutyCycle(m_index, dutyCycle);
}
/**
* Register a callback to be run whenever the pin changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerPinCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DigitalPWMDataJNI.registerPinCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DigitalPWMDataJNI::cancelPinCallback);
}
/**
* Check the pin number.
*
* @return the pin number
*/
public int getPin() {
return DigitalPWMDataJNI.getPin(m_index);
}
/**
* Change the pin number.
*
* @param pin the new pin number
*/
public void setPin(int pin) {
DigitalPWMDataJNI.setPin(m_index, pin);
}
/** Reset all simulation data. */
public void resetData() {
DigitalPWMDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,88 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.wpilibj.DoubleSolenoid;
import edu.wpi.first.wpilibj.PneumaticsBase;
import edu.wpi.first.wpilibj.PneumaticsModuleType;
/** Class to control a simulated {@link edu.wpi.first.wpilibj.DoubleSolenoid}. */
public class DoubleSolenoidSim {
private final PneumaticsBaseSim m_module;
private final int m_fwd;
private final int m_rev;
/**
* Constructs for a solenoid on the given pneumatics module.
*
* @param moduleSim the PCM the solenoid is connected to.
* @param fwd the forward solenoid channel.
* @param rev the reverse solenoid channel.
*/
public DoubleSolenoidSim(PneumaticsBaseSim moduleSim, int fwd, int rev) {
m_module = moduleSim;
m_fwd = fwd;
m_rev = rev;
}
/**
* Constructs for a solenoid on a pneumatics module of the given type and ID.
*
* @param module the CAN ID of the pneumatics module the solenoid is connected to.
* @param moduleType the module type (PH or PCM)
* @param fwd the forward solenoid channel.
* @param rev the reverse solenoid channel.
*/
public DoubleSolenoidSim(int module, PneumaticsModuleType moduleType, int fwd, int rev) {
this(PneumaticsBaseSim.getForType(module, moduleType), fwd, rev);
}
/**
* Constructs for a solenoid on a pneumatics module of the given type and default ID.
*
* @param moduleType the module type (PH or PCM)
* @param fwd the forward solenoid channel.
* @param rev the reverse solenoid channel.
*/
public DoubleSolenoidSim(PneumaticsModuleType moduleType, int fwd, int rev) {
this(PneumaticsBase.getDefaultForType(moduleType), moduleType, fwd, rev);
}
/**
* Check the value of the double solenoid output.
*
* @return the output value of the double solenoid.
*/
public DoubleSolenoid.Value get() {
boolean fwdState = m_module.getSolenoidOutput(m_fwd);
boolean revState = m_module.getSolenoidOutput(m_rev);
if (fwdState && !revState) {
return DoubleSolenoid.Value.kForward;
} else if (!fwdState && revState) {
return DoubleSolenoid.Value.kReverse;
} else {
return DoubleSolenoid.Value.kOff;
}
}
/**
* Set the value of the double solenoid output.
*
* @param value The value to set (Off, Forward, Reverse)
*/
public void set(final DoubleSolenoid.Value value) {
m_module.setSolenoidOutput(m_fwd, value == DoubleSolenoid.Value.kForward);
m_module.setSolenoidOutput(m_rev, value == DoubleSolenoid.Value.kReverse);
}
/**
* Get the wrapped {@link PneumaticsBaseSim} object.
*
* @return the wrapped {@link PneumaticsBaseSim} object.
*/
public PneumaticsBaseSim getModuleSim() {
return m_module;
}
}

View File

@@ -0,0 +1,523 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.AllianceStationID;
import edu.wpi.first.hal.DriverStationJNI;
import edu.wpi.first.hal.simulation.DriverStationDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.wpilibj.DriverStation;
/** Class to control a simulated driver station. */
public final class DriverStationSim {
private DriverStationSim() {
throw new UnsupportedOperationException("This is a utility class!");
}
/**
* Register a callback on whether the DS is enabled.
*
* @param callback the callback that will be called whenever the enabled state is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerEnabledCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerEnabledCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelEnabledCallback);
}
/**
* Check if the DS is enabled.
*
* @return true if enabled
*/
public static boolean getEnabled() {
return DriverStationDataJNI.getEnabled();
}
/**
* Change whether the DS is enabled.
*
* @param enabled the new value
*/
public static void setEnabled(boolean enabled) {
DriverStationDataJNI.setEnabled(enabled);
}
/**
* Register a callback on whether the DS is in autonomous mode.
*
* @param callback the callback that will be called on autonomous mode entrance/exit
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerAutonomousCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerAutonomousCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelAutonomousCallback);
}
/**
* Check if the DS is in autonomous.
*
* @return true if autonomous
*/
public static boolean getAutonomous() {
return DriverStationDataJNI.getAutonomous();
}
/**
* Change whether the DS is in autonomous.
*
* @param autonomous the new value
*/
public static void setAutonomous(boolean autonomous) {
DriverStationDataJNI.setAutonomous(autonomous);
}
/**
* Register a callback on whether the DS is in test mode.
*
* @param callback the callback that will be called whenever the test mode is entered or left
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerTestCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerTestCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelTestCallback);
}
/**
* Check if the DS is in test.
*
* @return true if test
*/
public static boolean getTest() {
return DriverStationDataJNI.getTest();
}
/**
* Change whether the DS is in test.
*
* @param test the new value
*/
public static void setTest(boolean test) {
DriverStationDataJNI.setTest(test);
}
/**
* Register a callback on the eStop state.
*
* @param callback the callback that will be called whenever the eStop state changes
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerEStopCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerEStopCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelEStopCallback);
}
/**
* Check if eStop has been activated.
*
* @return true if eStopped
*/
public static boolean getEStop() {
return DriverStationDataJNI.getEStop();
}
/**
* Set whether eStop is active.
*
* @param eStop true to activate
*/
public static void setEStop(boolean eStop) {
DriverStationDataJNI.setEStop(eStop);
}
/**
* Register a callback on whether the FMS is connected.
*
* @param callback the callback that will be called whenever the FMS connection changes
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerFmsAttachedCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerFmsAttachedCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelFmsAttachedCallback);
}
/**
* Check if the FMS is connected.
*
* @return true if FMS is connected
*/
public static boolean getFmsAttached() {
return DriverStationDataJNI.getFmsAttached();
}
/**
* Change whether the FMS is connected.
*
* @param fmsAttached the new value
*/
public static void setFmsAttached(boolean fmsAttached) {
DriverStationDataJNI.setFmsAttached(fmsAttached);
}
/**
* Register a callback on whether the DS is connected.
*
* @param callback the callback that will be called whenever the DS connection changes
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerDsAttachedCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerDsAttachedCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelDsAttachedCallback);
}
/**
* Check if the DS is attached.
*
* @return true if attached
*/
public static boolean getDsAttached() {
return DriverStationDataJNI.getDsAttached();
}
/**
* Change whether the DS is attached.
*
* @param dsAttached the new value
*/
public static void setDsAttached(boolean dsAttached) {
DriverStationDataJNI.setDsAttached(dsAttached);
}
/**
* Register a callback on the alliance station ID.
*
* @param callback the callback that will be called whenever the alliance station changes
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerAllianceStationIdCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerAllianceStationIdCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelAllianceStationIdCallback);
}
/**
* Get the alliance station ID (color + number).
*
* @return the alliance station color and number
*/
public static AllianceStationID getAllianceStationId() {
return switch (DriverStationDataJNI.getAllianceStationId()) {
case DriverStationJNI.kUnknownAllianceStation -> AllianceStationID.Unknown;
case DriverStationJNI.kRed1AllianceStation -> AllianceStationID.Red1;
case DriverStationJNI.kRed2AllianceStation -> AllianceStationID.Red2;
case DriverStationJNI.kRed3AllianceStation -> AllianceStationID.Red3;
case DriverStationJNI.kBlue1AllianceStation -> AllianceStationID.Blue1;
case DriverStationJNI.kBlue2AllianceStation -> AllianceStationID.Blue2;
case DriverStationJNI.kBlue3AllianceStation -> AllianceStationID.Blue3;
default -> AllianceStationID.Unknown;
};
}
/**
* Change the alliance station.
*
* @param allianceStationId the new alliance station
*/
public static void setAllianceStationId(AllianceStationID allianceStationId) {
int allianceStation =
switch (allianceStationId) {
case Unknown -> DriverStationJNI.kUnknownAllianceStation;
case Red1 -> DriverStationJNI.kRed1AllianceStation;
case Red2 -> DriverStationJNI.kRed2AllianceStation;
case Red3 -> DriverStationJNI.kRed3AllianceStation;
case Blue1 -> DriverStationJNI.kBlue1AllianceStation;
case Blue2 -> DriverStationJNI.kBlue2AllianceStation;
case Blue3 -> DriverStationJNI.kBlue3AllianceStation;
};
DriverStationDataJNI.setAllianceStationId(allianceStation);
}
/**
* Register a callback on match time.
*
* @param callback the callback that will be called whenever match time changes
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerMatchTimeCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = DriverStationDataJNI.registerMatchTimeCallback(callback, initialNotify);
return new CallbackStore(uid, DriverStationDataJNI::cancelMatchTimeCallback);
}
/**
* Get the current value of the match timer.
*
* @return the current match time
*/
public static double getMatchTime() {
return DriverStationDataJNI.getMatchTime();
}
/**
* Sets the match timer.
*
* @param matchTime the new match time
*/
public static void setMatchTime(double matchTime) {
DriverStationDataJNI.setMatchTime(matchTime);
}
/** Updates DriverStation data so that new values are visible to the user program. */
public static void notifyNewData() {
int handle = WPIUtilJNI.createEvent(false, false);
DriverStationJNI.provideNewDataEventHandle(handle);
DriverStationDataJNI.notifyNewData();
try {
WPIUtilJNI.waitForObject(handle);
} catch (InterruptedException e) {
e.printStackTrace();
}
DriverStationJNI.removeNewDataEventHandle(handle);
WPIUtilJNI.destroyEvent(handle);
DriverStation.refreshData();
}
/**
* Sets suppression of DriverStation.reportError and reportWarning messages.
*
* @param shouldSend If false then messages will be suppressed.
*/
public static void setSendError(boolean shouldSend) {
DriverStationDataJNI.setSendError(shouldSend);
}
/**
* Sets suppression of DriverStation.sendConsoleLine messages.
*
* @param shouldSend If false then messages will be suppressed.
*/
public static void setSendConsoleLine(boolean shouldSend) {
DriverStationDataJNI.setSendConsoleLine(shouldSend);
}
/**
* Gets the joystick outputs.
*
* @param stick The joystick number
* @return The joystick outputs
*/
public static long getJoystickOutputs(int stick) {
return DriverStationDataJNI.getJoystickOutputs(stick);
}
/**
* Gets the joystick rumble.
*
* @param stick The joystick number
* @param rumbleNum Rumble to get (0=left, 1=right)
* @return The joystick rumble value
*/
public static int getJoystickRumble(int stick, int rumbleNum) {
return DriverStationDataJNI.getJoystickRumble(stick, rumbleNum);
}
/**
* Sets the state of one joystick button. Button indexes begin at 0.
*
* @param stick The joystick number
* @param button The button index, beginning at 0
* @param state The state of the joystick button
*/
public static void setJoystickButton(int stick, int button, boolean state) {
DriverStationDataJNI.setJoystickButton(stick, button, state);
}
/**
* Gets the value of the axis on a joystick.
*
* @param stick The joystick number
* @param axis The analog axis number
* @param value The value of the axis on the joystick
*/
public static void setJoystickAxis(int stick, int axis, double value) {
DriverStationDataJNI.setJoystickAxis(stick, axis, value);
}
/**
* Gets the state of a POV on a joystick.
*
* @param stick The joystick number
* @param pov The POV number
* @param value the angle of the POV
*/
public static void setJoystickPOV(int stick, int pov, DriverStation.POVDirection value) {
DriverStationDataJNI.setJoystickPOV(stick, pov, value.value);
}
/**
* Sets the maximum index that an axis is available at.
*
* @param stick The joystick number
* @param maximumIndex The maximum index an axis is available at.
*/
public static void setJoystickAxesMaximumIndex(int stick, int maximumIndex) {
setJoystickAxesAvailable(stick, (1 << maximumIndex) - 1);
}
/**
* Sets the number of axes for a joystick.
*
* @param stick The joystick number
* @param count The number of axes on the indicated joystick
*/
public static void setJoystickAxesAvailable(int stick, int count) {
DriverStationDataJNI.setJoystickAxesAvailable(stick, count);
}
/**
* Sets the maximum index that a pov is available at.
*
* @param stick The joystick number
* @param maximumIndex The maximum index a pov is available at.
*/
public static void setJoystickPOVsMaximumIndex(int stick, int maximumIndex) {
setJoystickPOVsAvailable(stick, (1 << maximumIndex) - 1);
}
/**
* Sets the number of POVs for a joystick.
*
* @param stick The joystick number
* @param count The number of POVs on the indicated joystick
*/
public static void setJoystickPOVsAvailable(int stick, int count) {
DriverStationDataJNI.setJoystickPOVsAvailable(stick, count);
}
/**
* Sets the maximum index that a button is available at.
*
* @param stick The joystick number
* @param maximumIndex The maximum index a button is available at.
*/
public static void setJoystickButtonsMaximumIndex(int stick, int maximumIndex) {
if (maximumIndex >= 64) {
setJoystickButtonsAvailable(stick, 0xFFFFFFFFFFFFFFFFL);
} else {
setJoystickButtonsAvailable(stick, (1L << maximumIndex) - 1);
}
}
/**
* Sets the number of buttons for a joystick.
*
* @param stick The joystick number
* @param count The number of buttons on the indicated joystick
*/
public static void setJoystickButtonsAvailable(int stick, long count) {
DriverStationDataJNI.setJoystickButtonsAvailable(stick, count);
}
/**
* Sets the value of isGamepad for a joystick.
*
* @param stick The joystick number
* @param isGamepad The value of isGamepad
*/
public static void setJoystickIsGamepad(int stick, boolean isGamepad) {
DriverStationDataJNI.setJoystickIsGamepad(stick, isGamepad);
}
/**
* Sets the value of type for a joystick.
*
* @param stick The joystick number
* @param type The value of type
*/
public static void setJoystickType(int stick, int type) {
DriverStationDataJNI.setJoystickType(stick, type);
}
/**
* Sets the name of a joystick.
*
* @param stick The joystick number
* @param name The value of name
*/
public static void setJoystickName(int stick, String name) {
DriverStationDataJNI.setJoystickName(stick, name);
}
/**
* Sets the game specific message.
*
* @param message the game specific message
*/
public static void setGameSpecificMessage(String message) {
DriverStationDataJNI.setGameSpecificMessage(message);
}
/**
* Sets the event name.
*
* @param name the event name
*/
public static void setEventName(String name) {
DriverStationDataJNI.setEventName(name);
}
/**
* Sets the match type.
*
* @param type the match type
*/
public static void setMatchType(DriverStation.MatchType type) {
int matchType =
switch (type) {
case Practice -> 1;
case Qualification -> 2;
case Elimination -> 3;
case None -> 0;
};
DriverStationDataJNI.setMatchType(matchType);
}
/**
* Sets the match number.
*
* @param matchNumber the match number
*/
public static void setMatchNumber(int matchNumber) {
DriverStationDataJNI.setMatchNumber(matchNumber);
}
/**
* Sets the replay number.
*
* @param replayNumber the replay number
*/
public static void setReplayNumber(int replayNumber) {
DriverStationDataJNI.setReplayNumber(replayNumber);
}
/** Reset all simulation data for the Driver Station. */
public static void resetData() {
DriverStationDataJNI.resetData();
}
}

View File

@@ -0,0 +1,71 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimBoolean;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.wpilibj.DutyCycleEncoder;
/** Class to control a simulated duty cycle encoder. */
public class DutyCycleEncoderSim {
private final SimDouble m_simPosition;
private final SimBoolean m_simIsConnected;
/**
* Constructs from an DutyCycleEncoder object.
*
* @param encoder DutyCycleEncoder to simulate
*/
public DutyCycleEncoderSim(DutyCycleEncoder encoder) {
this(encoder.getSourceChannel());
}
/**
* Constructs from a digital input channel.
*
* @param channel digital input channel.
*/
public DutyCycleEncoderSim(int channel) {
SimDeviceSim wrappedSimDevice = new SimDeviceSim("DutyCycle:DutyCycleEncoder", channel);
m_simPosition = wrappedSimDevice.getDouble("Position");
m_simIsConnected = wrappedSimDevice.getBoolean("Connected");
}
/**
* Get the position in turns.
*
* @return The position.
*/
public double get() {
return m_simPosition.get();
}
/**
* Set the position.
*
* @param value The position.
*/
public void set(double value) {
m_simPosition.set(value);
}
/**
* Get if the encoder is connected.
*
* @return true if the encoder is connected.
*/
public boolean getConnected() {
return m_simIsConnected.get();
}
/**
* Set if the encoder is connected.
*
* @param isConnected Whether or not the sensor is connected.
*/
public void setConnected(boolean isConnected) {
m_simIsConnected.set(isConnected);
}
}

View File

@@ -0,0 +1,132 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.DutyCycleDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.DutyCycle;
/** Class to control a simulated duty cycle digital input. */
public class DutyCycleSim {
private final int m_index;
/**
* Constructs from a DutyCycle object.
*
* @param dutyCycle DutyCycle to simulate
*/
public DutyCycleSim(DutyCycle dutyCycle) {
m_index = dutyCycle.getSourceChannel();
}
private DutyCycleSim(int index) {
m_index = index;
}
/**
* Creates a DutyCycleSim for a SmartIO channel.
*
* @param channel SmartIO channel
* @return Simulated object
*/
public static DutyCycleSim createForChannel(int channel) {
return new DutyCycleSim(channel);
}
/**
* Register a callback to be run when this duty cycle input is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DutyCycleDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DutyCycleDataJNI::cancelInitializedCallback);
}
/**
* Check whether this duty cycle input has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return DutyCycleDataJNI.getInitialized(m_index);
}
/**
* Define whether this duty cycle input has been initialized.
*
* @param initialized whether this object is initialized
*/
public void setInitialized(boolean initialized) {
DutyCycleDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run whenever the frequency changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerFrequencyCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DutyCycleDataJNI.registerFrequencyCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DutyCycleDataJNI::cancelFrequencyCallback);
}
/**
* Measure the frequency.
*
* @return the duty cycle frequency
*/
public double getFrequency() {
return DutyCycleDataJNI.getFrequency(m_index);
}
/**
* Change the duty cycle frequency.
*
* @param frequency the new frequency
*/
public void setFrequency(double frequency) {
DutyCycleDataJNI.setFrequency(m_index, frequency);
}
/**
* Register a callback to be run whenever the output changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerOutputCallback(NotifyCallback callback, boolean initialNotify) {
int uid = DutyCycleDataJNI.registerOutputCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, DutyCycleDataJNI::cancelOutputCallback);
}
/**
* Measure the output from this duty cycle port.
*
* @return the output value
*/
public double getOutput() {
return DutyCycleDataJNI.getOutput(m_index);
}
/**
* Change the duty cycle output.
*
* @param output the new output value
*/
public void setOutput(double output) {
DutyCycleDataJNI.setOutput(m_index, output);
}
/** Reset all simulation data for the duty cycle output. */
public void resetData() {
DutyCycleDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,255 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N2;
import edu.wpi.first.math.system.LinearSystem;
import edu.wpi.first.math.system.NumericalIntegration;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.math.system.plant.LinearSystemId;
import edu.wpi.first.wpilibj.RobotController;
/** Represents a simulated elevator mechanism. */
public class ElevatorSim extends LinearSystemSim<N2, N1, N2> {
// Gearbox for the elevator.
private final DCMotor m_gearbox;
// The min allowable height for the elevator.
private final double m_minHeight;
// The max allowable height for the elevator.
private final double m_maxHeight;
// Whether the simulator should simulate gravity.
private final boolean m_simulateGravity;
/**
* Creates a simulated elevator mechanism.
*
* @param plant The linear system that represents the elevator. This system can be created with
* {@link edu.wpi.first.math.system.plant.LinearSystemId#createElevatorSystem(DCMotor, double,
* double, double)}.
* @param gearbox The type of and number of motors in the elevator gearbox.
* @param minHeight The min allowable height of the elevator in meters.
* @param maxHeight The max allowable height of the elevator in meters.
* @param simulateGravity Whether gravity should be simulated or not.
* @param startingHeight The starting height of the elevator in meters.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for position.
*/
@SuppressWarnings("this-escape")
public ElevatorSim(
LinearSystem<N2, N1, N2> plant,
DCMotor gearbox,
double minHeight,
double maxHeight,
boolean simulateGravity,
double startingHeight,
double... measurementStdDevs) {
super(plant, measurementStdDevs);
m_gearbox = gearbox;
m_minHeight = minHeight;
m_maxHeight = maxHeight;
m_simulateGravity = simulateGravity;
setState(startingHeight, 0);
}
/**
* Creates a simulated elevator mechanism.
*
* @param kV The velocity gain.
* @param kA The acceleration gain.
* @param gearbox The type of and number of motors in the elevator gearbox.
* @param minHeight The min allowable height of the elevator in meters.
* @param maxHeight The max allowable height of the elevator in meters.
* @param simulateGravity Whether gravity should be simulated or not.
* @param startingHeight The starting height of the elevator in meters.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for position.
*/
public ElevatorSim(
double kV,
double kA,
DCMotor gearbox,
double minHeight,
double maxHeight,
boolean simulateGravity,
double startingHeight,
double... measurementStdDevs) {
this(
LinearSystemId.identifyPositionSystem(kV, kA),
gearbox,
minHeight,
maxHeight,
simulateGravity,
startingHeight,
measurementStdDevs);
}
/**
* Creates a simulated elevator mechanism.
*
* @param gearbox The type of and number of motors in the elevator gearbox.
* @param gearing The gearing of the elevator (numbers greater than 1 represent reductions).
* @param carriageMass The mass of the elevator carriage in kg.
* @param drumRadius The radius of the drum that the elevator spool is wrapped around in meters.
* @param minHeight The min allowable height of the elevator in meters.
* @param maxHeight The max allowable height of the elevator in meters.
* @param simulateGravity Whether gravity should be simulated or not.
* @param startingHeight The starting height of the elevator in meters.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for position.
*/
public ElevatorSim(
DCMotor gearbox,
double gearing,
double carriageMass,
double drumRadius,
double minHeight,
double maxHeight,
boolean simulateGravity,
double startingHeight,
double... measurementStdDevs) {
this(
LinearSystemId.createElevatorSystem(gearbox, carriageMass, drumRadius, gearing),
gearbox,
minHeight,
maxHeight,
simulateGravity,
startingHeight,
measurementStdDevs);
}
/**
* Sets the elevator's state. The new position will be limited between the minimum and maximum
* allowed heights.
*
* @param position The new position in meters.
* @param velocity New velocity in meters per second.
*/
public final void setState(double position, double velocity) {
setState(VecBuilder.fill(Math.clamp(position, m_minHeight, m_maxHeight), velocity));
}
/**
* Returns whether the elevator would hit the lower limit.
*
* @param elevatorHeight The elevator height in meters.
* @return Whether the elevator would hit the lower limit.
*/
public boolean wouldHitLowerLimit(double elevatorHeight) {
return elevatorHeight <= this.m_minHeight;
}
/**
* Returns whether the elevator would hit the upper limit.
*
* @param elevatorHeight The elevator height in meters.
* @return Whether the elevator would hit the upper limit.
*/
public boolean wouldHitUpperLimit(double elevatorHeight) {
return elevatorHeight >= this.m_maxHeight;
}
/**
* Returns whether the elevator has hit the lower limit.
*
* @return Whether the elevator has hit the lower limit.
*/
public boolean hasHitLowerLimit() {
return wouldHitLowerLimit(getPosition());
}
/**
* Returns whether the elevator has hit the upper limit.
*
* @return Whether the elevator has hit the upper limit.
*/
public boolean hasHitUpperLimit() {
return wouldHitUpperLimit(getPosition());
}
/**
* Returns the position of the elevator.
*
* @return The position of the elevator in meters.
*/
public double getPosition() {
return getOutput(0);
}
/**
* Returns the velocity of the elevator.
*
* @return The velocity of the elevator in meters per second.
*/
public double getVelocity() {
return getOutput(1);
}
/**
* Returns the elevator current draw.
*
* @return The elevator current draw in amps.
*/
public double getCurrentDraw() {
// I = V / R - omega / (Kv * R)
// Reductions are greater than 1, so a reduction of 10:1 would mean the motor is
// spinning 10x faster than the output
// v = r w, so w = v/r
double kA = 1 / m_plant.getB().get(1, 0);
double kV = -m_plant.getA().get(1, 1) * kA;
double motorVelocity = m_x.get(1, 0) * kV * m_gearbox.Kv;
var appliedVoltage = m_u.get(0, 0);
return m_gearbox.getCurrent(motorVelocity, appliedVoltage) * Math.signum(appliedVoltage);
}
/**
* Sets the input voltage for the elevator.
*
* @param volts The input voltage.
*/
public void setInputVoltage(double volts) {
setInput(volts);
clampInput(RobotController.getBatteryVoltage());
}
/**
* Updates the state of the elevator.
*
* @param currentXhat The current state estimate.
* @param u The system inputs (voltage).
* @param dt The time difference between controller updates in seconds.
*/
@Override
protected Matrix<N2, N1> updateX(Matrix<N2, N1> currentXhat, Matrix<N1, N1> u, double dt) {
// Calculate updated x-hat from Runge-Kutta.
var updatedXhat =
NumericalIntegration.rkdp(
(x, _u) -> {
Matrix<N2, N1> xdot = m_plant.getA().times(x).plus(m_plant.getB().times(_u));
if (m_simulateGravity) {
xdot = xdot.plus(VecBuilder.fill(0, -9.8));
}
return xdot;
},
currentXhat,
u,
dt);
// We check for collisions after updating x-hat.
if (wouldHitLowerLimit(updatedXhat.get(0, 0))) {
return VecBuilder.fill(m_minHeight, 0);
}
if (wouldHitUpperLimit(updatedXhat.get(0, 0))) {
return VecBuilder.fill(m_maxHeight, 0);
}
return updatedXhat;
}
}

View File

@@ -0,0 +1,369 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.EncoderDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.Encoder;
import java.util.NoSuchElementException;
/** Class to control a simulated encoder. */
public class EncoderSim {
private final int m_index;
/**
* Constructs from an Encoder object.
*
* @param encoder Encoder to simulate
*/
public EncoderSim(Encoder encoder) {
m_index = encoder.getFPGAIndex();
}
private EncoderSim(int index) {
m_index = index;
}
/**
* Creates an EncoderSim for a digital input channel. Encoders take two channels, so either one
* may be specified.
*
* @param channel digital input channel
* @return Simulated object
* @throws NoSuchElementException if no Encoder is configured for that channel
*/
public static EncoderSim createForChannel(int channel) {
int index = EncoderDataJNI.findForChannel(channel);
if (index < 0) {
throw new NoSuchElementException("no encoder found for channel " + channel);
}
return new EncoderSim(index);
}
/**
* Creates an EncoderSim for a simulated index. The index is incremented for each simulated
* Encoder.
*
* @param index simulator index
* @return Simulated object
*/
public static EncoderSim createForIndex(int index) {
return new EncoderSim(index);
}
/**
* Register a callback on the Initialized property of the encoder.
*
* @param callback the callback that will be called whenever the Initialized property is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelInitializedCallback);
}
/**
* Read the Initialized value of the encoder.
*
* @return true if initialized
*/
public boolean getInitialized() {
return EncoderDataJNI.getInitialized(m_index);
}
/**
* Change the Initialized value of the encoder.
*
* @param initialized the new value
*/
public void setInitialized(boolean initialized) {
EncoderDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback on the count property of the encoder.
*
* @param callback the callback that will be called whenever the count property is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerCountCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerCountCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelCountCallback);
}
/**
* Read the count of the encoder.
*
* @return the count
*/
public int getCount() {
return EncoderDataJNI.getCount(m_index);
}
/**
* Change the count of the encoder.
*
* @param count the new count
*/
public void setCount(int count) {
EncoderDataJNI.setCount(m_index, count);
}
/**
* Register a callback on the period of the encoder.
*
* @param callback the callback that will be called whenever the period is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerPeriodCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerPeriodCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelPeriodCallback);
}
/**
* Read the period of the encoder.
*
* @return the encoder period
*/
public double getPeriod() {
return EncoderDataJNI.getPeriod(m_index);
}
/**
* Change the encoder period.
*
* @param period the new period
*/
public void setPeriod(double period) {
EncoderDataJNI.setPeriod(m_index, period);
}
/**
* Register a callback to be called whenever the encoder is reset.
*
* @param callback the callback
* @param initialNotify whether to run the callback on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerResetCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerResetCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelResetCallback);
}
/**
* Check if the encoder has been reset.
*
* @return true if reset
*/
public boolean getReset() {
return EncoderDataJNI.getReset(m_index);
}
/**
* Change the reset property of the encoder.
*
* @param reset the new value
*/
public void setReset(boolean reset) {
EncoderDataJNI.setReset(m_index, reset);
}
/**
* Register a callback to be run whenever the max period of the encoder is changed.
*
* @param callback the callback
* @param initialNotify whether to run the callback on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerMaxPeriodCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerMaxPeriodCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelMaxPeriodCallback);
}
/**
* Get the max period of the encoder.
*
* @return the max period of the encoder
*/
public double getMaxPeriod() {
return EncoderDataJNI.getMaxPeriod(m_index);
}
/**
* Change the max period of the encoder.
*
* @param maxPeriod the new value
*/
public void setMaxPeriod(double maxPeriod) {
EncoderDataJNI.setMaxPeriod(m_index, maxPeriod);
}
/**
* Register a callback on the direction of the encoder.
*
* @param callback the callback that will be called whenever the direction is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerDirectionCallback(NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerDirectionCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelDirectionCallback);
}
/**
* Get the direction of the encoder.
*
* @return the direction of the encoder
*/
public boolean getDirection() {
return EncoderDataJNI.getDirection(m_index);
}
/**
* Set the direction of the encoder.
*
* @param direction the new direction
*/
public void setDirection(boolean direction) {
EncoderDataJNI.setDirection(m_index, direction);
}
/**
* Register a callback on the reverse direction.
*
* @param callback the callback that will be called whenever the reverse direction is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerReverseDirectionCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerReverseDirectionCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelReverseDirectionCallback);
}
/**
* Get the reverse direction of the encoder.
*
* @return the reverse direction of the encoder
*/
public boolean getReverseDirection() {
return EncoderDataJNI.getReverseDirection(m_index);
}
/**
* Set the reverse direction.
*
* @param reverseDirection the new value
*/
public void setReverseDirection(boolean reverseDirection) {
EncoderDataJNI.setReverseDirection(m_index, reverseDirection);
}
/**
* Register a callback on the samples-to-average value of this encoder.
*
* @param callback the callback that will be called whenever the samples-to-average is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerSamplesToAverageCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerSamplesToAverageCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelSamplesToAverageCallback);
}
/**
* Get the samples-to-average value.
*
* @return the samples-to-average value
*/
public int getSamplesToAverage() {
return EncoderDataJNI.getSamplesToAverage(m_index);
}
/**
* Set the samples-to-average value.
*
* @param samplesToAverage the new value
*/
public void setSamplesToAverage(int samplesToAverage) {
EncoderDataJNI.setSamplesToAverage(m_index, samplesToAverage);
}
/**
* Register a callback on the distance per pulse value of this encoder.
*
* @param callback the callback that will be called whenever the distance per pulse is changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerDistancePerPulseCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = EncoderDataJNI.registerDistancePerPulseCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, EncoderDataJNI::cancelDistancePerPulseCallback);
}
/**
* Get the distance per pulse value.
*
* @return the distance per pulse value
*/
public double getDistancePerPulse() {
return EncoderDataJNI.getDistancePerPulse(m_index);
}
/**
* Set the distance per pulse value.
*
* @param distancePerPulse the new distancePerPulse
*/
public void setDistancePerPulse(double distancePerPulse) {
EncoderDataJNI.setDistancePerPulse(m_index, distancePerPulse);
}
/**
* Change the encoder distance.
*
* @param distance the new distance
*/
public void setDistance(double distance) {
EncoderDataJNI.setDistance(m_index, distance);
}
/**
* Read the distance of the encoder.
*
* @return the encoder distance
*/
public double getDistance() {
return EncoderDataJNI.getDistance(m_index);
}
/**
* Change the rate of the encoder.
*
* @param rate the new rate
*/
public void setRate(double rate) {
EncoderDataJNI.setRate(m_index, rate);
}
/**
* Get the rate of the encoder.
*
* @return the rate of change
*/
public double getRate() {
return EncoderDataJNI.getRate(m_index);
}
/** Resets all simulation data for this encoder. */
public void resetData() {
EncoderDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,156 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.system.LinearSystem;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.math.system.plant.LinearSystemId;
import edu.wpi.first.wpilibj.RobotController;
/** Represents a simulated flywheel mechanism. */
public class FlywheelSim extends LinearSystemSim<N1, N1, N1> {
// Gearbox for the flywheel.
private final DCMotor m_gearbox;
// The gearing from the motors to the output.
private final double m_gearing;
// The moment of inertia for the flywheel mechanism.
private final double m_j;
/**
* Creates a simulated flywheel mechanism.
*
* @param plant The linear system that represents the flywheel. Use either {@link
* LinearSystemId#createFlywheelSystem(DCMotor, double, double)} if using physical constants
* or {@link LinearSystemId#identifyVelocitySystem(double, double)} if using system
* characterization.
* @param gearbox The type of and number of motors in the flywheel gearbox.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for velocity.
*/
public FlywheelSim(
LinearSystem<N1, N1, N1> plant, DCMotor gearbox, double... measurementStdDevs) {
super(plant, measurementStdDevs);
m_gearbox = gearbox;
// By theorem 6.10.1 of https://file.tavsys.net/control/controls-engineering-in-frc.pdf,
// the flywheel state-space model is:
//
// dx/dt = -G²Kₜ/(KᵥRJ)x + (GKₜ)/(RJ)u
// A = -G²Kₜ/(KᵥRJ)
// B = GKₜ/(RJ)
//
// Solve for G.
//
// A/B = -G/Kᵥ
// G = -KᵥA/B
//
// Solve for J.
//
// B = GKₜ/(RJ)
// J = GKₜ/(RB)
m_gearing = -gearbox.Kv * plant.getA(0, 0) / plant.getB(0, 0);
m_j = m_gearing * gearbox.Kt / (gearbox.R * plant.getB(0, 0));
}
/**
* Sets the flywheel's angular velocity.
*
* @param velocity The new velocity in radians per second.
*/
public void setAngularVelocity(double velocity) {
setState(VecBuilder.fill(velocity));
}
/**
* Returns the gear ratio of the flywheel.
*
* @return the flywheel's gear ratio.
*/
public double getGearing() {
return m_gearing;
}
/**
* Returns the moment of inertia.
*
* @return The flywheel's moment of inertia in kg-m².
*/
public double getJ() {
return m_j;
}
/**
* Returns the gearbox for the flywheel.
*
* @return The flywheel's gearbox.
*/
public DCMotor getGearbox() {
return m_gearbox;
}
/**
* Returns the flywheel's velocity.
*
* @return The flywheel's velocity in rad/s.
*/
public double getAngularVelocity() {
return getOutput(0);
}
/**
* Returns the flywheel's acceleration.
*
* @return The flywheel's acceleration in rad/s².
*/
public double getAngularAcceleration() {
var acceleration = (m_plant.getA().times(m_x)).plus(m_plant.getB().times(m_u));
return acceleration.get(0, 0);
}
/**
* Returns the flywheel's torque.
*
* @return The flywheel's torque in Newton-meters.
*/
public double getTorque() {
return getAngularAcceleration() * m_j;
}
/**
* Returns the flywheel's current draw.
*
* @return The flywheel's current draw in amps.
*/
public double getCurrentDraw() {
// I = V / R - omega / (Kv * R)
// Reductions are output over input, so a reduction of 2:1 means the motor is spinning
// 2x faster than the flywheel
return m_gearbox.getCurrent(m_x.get(0, 0) * m_gearing, m_u.get(0, 0))
* Math.signum(m_u.get(0, 0));
}
/**
* Gets the input voltage for the flywheel.
*
* @return The flywheel's input voltage.
*/
public double getInputVoltage() {
return getInput(0);
}
/**
* Sets the input voltage for the flywheel.
*
* @param volts The input voltage.
*/
public void setInputVoltage(double volts) {
setInput(volts);
clampInput(RobotController.getBatteryVoltage());
}
}

View File

@@ -0,0 +1,324 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.wpilibj.Gamepad;
/** Class to control a simulated Gamepad controller. */
public class GamepadSim extends GenericHIDSim {
/**
* Constructs from a Gamepad object.
*
* @param joystick controller to simulate
*/
@SuppressWarnings("this-escape")
public GamepadSim(Gamepad joystick) {
super(joystick);
setAxesMaximumIndex(6);
setButtonsMaximumIndex(26);
setPOVsMaximumIndex(1);
}
/**
* Constructs from a joystick port number.
*
* @param port port number
*/
@SuppressWarnings("this-escape")
public GamepadSim(int port) {
super(port);
setAxesMaximumIndex(6);
setButtonsMaximumIndex(26);
setPOVsMaximumIndex(1);
}
/**
* Change the left X value of the controller's joystick.
*
* @param value the new value
*/
public void setLeftX(double value) {
setRawAxis(Gamepad.Axis.kLeftX.value, value);
}
/**
* Change the left Y value of the controller's joystick.
*
* @param value the new value
*/
public void setLeftY(double value) {
setRawAxis(Gamepad.Axis.kLeftY.value, value);
}
/**
* Change the right X value of the controller's joystick.
*
* @param value the new value
*/
public void setRightX(double value) {
setRawAxis(Gamepad.Axis.kRightX.value, value);
}
/**
* Change the right Y value of the controller's joystick.
*
* @param value the new value
*/
public void setRightY(double value) {
setRawAxis(Gamepad.Axis.kRightY.value, value);
}
/**
* Change the value of the left trigger axis on the controller.
*
* @param value the new value
*/
public void setLeftTriggerAxis(double value) {
setRawAxis(Gamepad.Axis.kLeftTrigger.value, value);
}
/**
* Change the value of the right trigger axis on the controller.
*
* @param value the new value
*/
public void setRightTriggerAxis(double value) {
setRawAxis(Gamepad.Axis.kRightTrigger.value, value);
}
/**
* Change the value of the South Face button on the controller.
*
* @param value the new value
*/
public void setSouthFaceButton(boolean value) {
setRawButton(Gamepad.Button.kSouthFace.value, value);
}
/**
* Change the value of the East Face button on the controller.
*
* @param value the new value
*/
public void setEastFaceButton(boolean value) {
setRawButton(Gamepad.Button.kEastFace.value, value);
}
/**
* Change the value of the West Face button on the controller.
*
* @param value the new value
*/
public void setWestFaceButton(boolean value) {
setRawButton(Gamepad.Button.kWestFace.value, value);
}
/**
* Change the value of the North Face button on the controller.
*
* @param value the new value
*/
public void setNorthFaceButton(boolean value) {
setRawButton(Gamepad.Button.kNorthFace.value, value);
}
/**
* Change the value of the Back button on the controller.
*
* @param value the new value
*/
public void setBackButton(boolean value) {
setRawButton(Gamepad.Button.kBack.value, value);
}
/**
* Change the value of the Guide button on the controller.
*
* @param value the new value
*/
public void setGuideButton(boolean value) {
setRawButton(Gamepad.Button.kGuide.value, value);
}
/**
* Change the value of the Start button on the controller.
*
* @param value the new value
*/
public void setStartButton(boolean value) {
setRawButton(Gamepad.Button.kStart.value, value);
}
/**
* Change the value of the left stick button on the controller.
*
* @param value the new value
*/
public void setLeftStickButton(boolean value) {
setRawButton(Gamepad.Button.kLeftStick.value, value);
}
/**
* Change the value of the right stick button on the controller.
*
* @param value the new value
*/
public void setRightStickButton(boolean value) {
setRawButton(Gamepad.Button.kRightStick.value, value);
}
/**
* Change the value of the right shoulder button on the controller.
*
* @param value the new value
*/
public void setLeftShoulderButton(boolean value) {
setRawButton(Gamepad.Button.kLeftShoulder.value, value);
}
/**
* Change the value of the right shoulder button on the controller.
*
* @param value the new value
*/
public void setRightShoulderButton(boolean value) {
setRawButton(Gamepad.Button.kRightShoulder.value, value);
}
/**
* Change the value of the D-pad up button on the controller.
*
* @param value the new value
*/
public void setDpadUpButton(boolean value) {
setRawButton(Gamepad.Button.kDpadUp.value, value);
}
/**
* Change the value of the D-pad down button on the controller.
*
* @param value the new value
*/
public void setDpadDownButton(boolean value) {
setRawButton(Gamepad.Button.kDpadDown.value, value);
}
/**
* Change the value of the D-pad left button on the controller.
*
* @param value the new value
*/
public void setDpadLeftButton(boolean value) {
setRawButton(Gamepad.Button.kDpadLeft.value, value);
}
/**
* Change the value of the D-pad right button on the controller.
*
* @param value the new value
*/
public void setDpadRightButton(boolean value) {
setRawButton(Gamepad.Button.kDpadRight.value, value);
}
/**
* Change the value of the Miscellaneous 1 button on the controller.
*
* @param value the new value
*/
public void setMisc1Button(boolean value) {
setRawButton(Gamepad.Button.kMisc1.value, value);
}
/**
* Change the value of the Right Paddle 1 button on the controller.
*
* @param value the new value
*/
public void setRightPaddle1Button(boolean value) {
setRawButton(Gamepad.Button.kRightPaddle1.value, value);
}
/**
* Change the value of the Left Paddle 1 button on the controller.
*
* @param value the new value
*/
public void setLeftPaddle1Button(boolean value) {
setRawButton(Gamepad.Button.kLeftPaddle1.value, value);
}
/**
* Change the value of the Right Paddle 2 button on the controller.
*
* @param value the new value
*/
public void setRightPaddle2Button(boolean value) {
setRawButton(Gamepad.Button.kRightPaddle2.value, value);
}
/**
* Change the value of the Left Paddle 2 button on the controller.
*
* @param value the new value
*/
public void setLeftPaddle2Button(boolean value) {
setRawButton(Gamepad.Button.kLeftPaddle2.value, value);
}
/**
* Change the value of the Touchpad button on the controller.
*
* @param value the new value
*/
public void setTouchpadButton(boolean value) {
setRawButton(Gamepad.Button.kTouchpad.value, value);
}
/**
* Change the value of the Miscellaneous 2 button on the controller.
*
* @param value the new value
*/
public void setMisc2Button(boolean value) {
setRawButton(Gamepad.Button.kMisc2.value, value);
}
/**
* Change the value of the Miscellaneous 3 button on the controller.
*
* @param value the new value
*/
public void setMisc3Button(boolean value) {
setRawButton(Gamepad.Button.kMisc3.value, value);
}
/**
* Change the value of the Miscellaneous 4 button on the controller.
*
* @param value the new value
*/
public void setMisc4Button(boolean value) {
setRawButton(Gamepad.Button.kMisc4.value, value);
}
/**
* Change the value of the Miscellaneous 5 button on the controller.
*
* @param value the new value
*/
public void setMisc5Button(boolean value) {
setRawButton(Gamepad.Button.kMisc5.value, value);
}
/**
* Change the value of the Miscellaneous 6 button on the controller.
*
* @param value the new value
*/
public void setMisc6Button(boolean value) {
setRawButton(Gamepad.Button.kMisc6.value, value);
}
}

View File

@@ -0,0 +1,181 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.GenericHID;
/** Class to control a simulated generic joystick. */
public class GenericHIDSim {
/** GenericHID port. */
protected final int m_port;
/**
* Constructs from a GenericHID object.
*
* @param joystick joystick to simulate
*/
public GenericHIDSim(GenericHID joystick) {
m_port = joystick.getPort();
}
/**
* Constructs from a joystick port number.
*
* @param port port number
*/
public GenericHIDSim(int port) {
m_port = port;
}
/** Updates joystick data so that new values are visible to the user program. */
public void notifyNewData() {
DriverStationSim.notifyNewData();
}
/**
* Set the value of a given button.
*
* @param button the button to set
* @param value the new value
*/
public void setRawButton(int button, boolean value) {
DriverStationSim.setJoystickButton(m_port, button, value);
}
/**
* Set the value of a given axis.
*
* @param axis the axis to set
* @param value the new value
*/
public void setRawAxis(int axis, double value) {
DriverStationSim.setJoystickAxis(m_port, axis, value);
}
/**
* Set the value of a given POV.
*
* @param pov the POV to set
* @param value the new value
*/
public void setPOV(int pov, DriverStation.POVDirection value) {
DriverStationSim.setJoystickPOV(m_port, pov, value);
}
/**
* Set the value of the default POV (port 0).
*
* @param value the new value
*/
public void setPOV(DriverStation.POVDirection value) {
setPOV(0, value);
}
/**
* Set the maximum axis index for this device.
*
* @param maximumIndex the new maximum axis index
*/
public void setAxesMaximumIndex(int maximumIndex) {
DriverStationSim.setJoystickAxesMaximumIndex(m_port, maximumIndex);
}
/**
* Set the axis count of this device.
*
* @param count the new axis count
*/
public void setAxesAvailable(int count) {
DriverStationSim.setJoystickAxesAvailable(m_port, count);
}
/**
* Set the maximum POV index for this device.
*
* @param maximumIndex the new maximum POV index
*/
public void setPOVsMaximumIndex(int maximumIndex) {
DriverStationSim.setJoystickPOVsMaximumIndex(m_port, maximumIndex);
}
/**
* Set the POV count of this device.
*
* @param count the new POV count
*/
public void setPOVsAvailable(int count) {
DriverStationSim.setJoystickPOVsAvailable(m_port, count);
}
/**
* Set the maximum button index for this device.
*
* @param maximumIndex the new maximum button index
*/
public void setButtonsMaximumIndex(int maximumIndex) {
DriverStationSim.setJoystickButtonsMaximumIndex(m_port, maximumIndex);
}
/**
* Set the button count of this device.
*
* @param count the new button count
*/
public void setButtonsAvailable(long count) {
DriverStationSim.setJoystickButtonsAvailable(m_port, count);
}
/**
* Set the type of this device.
*
* @param type the new device type
*/
public void setType(GenericHID.HIDType type) {
DriverStationSim.setJoystickType(m_port, type.value);
}
/**
* Set the name of this device.
*
* @param name the new device name
*/
public void setName(String name) {
DriverStationSim.setJoystickName(m_port, name);
}
/**
* Read the output of a button.
*
* @param outputNumber the button number
* @return the value of the button (true = pressed)
*/
public boolean getOutput(int outputNumber) {
long outputs = getOutputs();
return (outputs & (1L << (outputNumber - 1))) != 0;
}
/**
* Get the encoded 16-bit integer that passes button values.
*
* @return the button values
*/
public long getOutputs() {
return DriverStationSim.getJoystickOutputs(m_port);
}
/**
* Get the joystick rumble.
*
* @param type the rumble to read
* @return the rumble value
*/
public double getRumble(GenericHID.RumbleType type) {
int value =
DriverStationSim.getJoystickRumble(
m_port, type == GenericHID.RumbleType.kLeftRumble ? 0 : 1);
return value / 65535.0;
}
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.BufferCallback;
import edu.wpi.first.hal.simulation.ConstBufferCallback;
import edu.wpi.first.hal.simulation.I2CDataJNI;
import edu.wpi.first.hal.simulation.NotifyCallback;
/** A class to control a simulated I2C device. */
public class I2CSim {
private final int m_index;
/**
* Construct a new I2C simulation object.
*
* @param index the HAL index of the I2C object
*/
public I2CSim(int index) {
m_index = index;
}
/**
* Register a callback to be run when this I2C device is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = I2CDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, I2CDataJNI::cancelInitializedCallback);
}
/**
* Check whether this I2C device has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return I2CDataJNI.getInitialized(m_index);
}
/**
* Define whether this I2C device has been initialized.
*
* @param initialized whether this device is initialized
*/
public void setInitialized(boolean initialized) {
I2CDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run whenever a `read` operation is done.
*
* @param callback the callback that is run on `read` operations
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerReadCallback(BufferCallback callback) {
int uid = I2CDataJNI.registerReadCallback(m_index, callback);
return new CallbackStore(m_index, uid, I2CDataJNI::cancelReadCallback);
}
/**
* Register a callback to be run whenever a `write` operation is done.
*
* @param callback the callback that is run on `write` operations
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerWriteCallback(ConstBufferCallback callback) {
int uid = I2CDataJNI.registerWriteCallback(m_index, callback);
return new CallbackStore(m_index, uid, I2CDataJNI::cancelWriteCallback);
}
/** Reset all I2C simulation data. */
public void resetData() {
I2CDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,107 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.wpilibj.Joystick;
/** Class to control a simulated joystick. */
public class JoystickSim extends GenericHIDSim {
private Joystick m_joystick;
/**
* Constructs from a Joystick object.
*
* @param joystick joystick to simulate
*/
@SuppressWarnings("this-escape")
public JoystickSim(Joystick joystick) {
super(joystick);
m_joystick = joystick;
// default to a reasonable joystick configuration
setAxesMaximumIndex(5);
setButtonsMaximumIndex(12);
setPOVsMaximumIndex(1);
}
/**
* Constructs from a joystick port number.
*
* @param port port number
*/
@SuppressWarnings("this-escape")
public JoystickSim(int port) {
super(port);
// default to a reasonable joystick configuration
setAxesMaximumIndex(5);
setButtonsMaximumIndex(12);
setPOVsMaximumIndex(1);
}
/**
* Set the X value of the joystick.
*
* @param value the new X value
*/
public void setX(double value) {
setRawAxis(m_joystick != null ? m_joystick.getXChannel() : Joystick.kDefaultXChannel, value);
}
/**
* Set the Y value of the joystick.
*
* @param value the new Y value
*/
public void setY(double value) {
setRawAxis(m_joystick != null ? m_joystick.getYChannel() : Joystick.kDefaultYChannel, value);
}
/**
* Set the Z value of the joystick.
*
* @param value the new Z value
*/
public void setZ(double value) {
setRawAxis(m_joystick != null ? m_joystick.getZChannel() : Joystick.kDefaultZChannel, value);
}
/**
* Set the twist value of the joystick.
*
* @param value the new twist value
*/
public void setTwist(double value) {
setRawAxis(
m_joystick != null ? m_joystick.getTwistChannel() : Joystick.kDefaultTwistChannel, value);
}
/**
* Set the throttle value of the joystick.
*
* @param value the new throttle value
*/
public void setThrottle(double value) {
setRawAxis(
m_joystick != null ? m_joystick.getThrottleChannel() : Joystick.kDefaultThrottleChannel,
value);
}
/**
* Set the trigger value of the joystick.
*
* @param state the new value
*/
public void setTrigger(boolean state) {
setRawButton(1, state);
}
/**
* Set the top state of the joystick.
*
* @param state the new state
*/
public void setTop(boolean state) {
setRawButton(2, state);
}
}

View File

@@ -0,0 +1,196 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.Num;
import edu.wpi.first.math.StateSpaceUtil;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.system.LinearSystem;
import org.ejml.MatrixDimensionException;
import org.ejml.simple.SimpleMatrix;
/**
* This class helps simulate linear systems. To use this class, do the following in the {@link
* edu.wpi.first.wpilibj.IterativeRobotBase#simulationPeriodic} method.
*
* <p>Call {@link #setInput(double...)} with the inputs to the system (usually voltage).
*
* <p>Call {@link #update} to update the simulation.
*
* <p>Set simulated sensor readings with the simulated positions in {@link #getOutput()}
*
* @param <States> Number of states of the system.
* @param <Inputs> Number of inputs to the system.
* @param <Outputs> Number of outputs of the system.
*/
public class LinearSystemSim<States extends Num, Inputs extends Num, Outputs extends Num> {
/** The plant that represents the linear system. */
protected final LinearSystem<States, Inputs, Outputs> m_plant;
/** State vector. */
protected Matrix<States, N1> m_x;
/** Input vector. */
protected Matrix<Inputs, N1> m_u;
/** Output vector. */
protected Matrix<Outputs, N1> m_y;
/** The standard deviations of measurements, used for adding noise to the measurements. */
protected final Matrix<Outputs, N1> m_measurementStdDevs;
/**
* Creates a simulated generic linear system with measurement noise.
*
* @param system The system being controlled.
* @param measurementStdDevs Standard deviations of measurements. Can be empty if no noise is
* desired. If present must have same number of items as Outputs
*/
public LinearSystemSim(
LinearSystem<States, Inputs, Outputs> system, double... measurementStdDevs) {
if (measurementStdDevs.length != 0 && measurementStdDevs.length != system.getC().getNumRows()) {
throw new MatrixDimensionException(
"Malformed measurementStdDevs! Got "
+ measurementStdDevs.length
+ " elements instead of "
+ system.getC().getNumRows());
}
this.m_plant = system;
if (measurementStdDevs.length == 0) {
m_measurementStdDevs = new Matrix<>(new SimpleMatrix(system.getC().getNumRows(), 1));
} else {
m_measurementStdDevs = new Matrix<>(new SimpleMatrix(measurementStdDevs));
}
m_x = new Matrix<>(new SimpleMatrix(system.getA().getNumRows(), 1));
m_u = new Matrix<>(new SimpleMatrix(system.getB().getNumCols(), 1));
m_y = new Matrix<>(new SimpleMatrix(system.getC().getNumRows(), 1));
}
/**
* Updates the simulation.
*
* @param dt The time between updates in seconds.
*/
public void update(double dt) {
// Update x. By default, this is the linear system dynamics xₖ₊₁ = Axₖ + Buₖ.
m_x = updateX(m_x, m_u, dt);
// yₖ = Cxₖ + Duₖ
m_y = m_plant.calculateY(m_x, m_u);
// Add measurement noise.
if (m_measurementStdDevs != null) {
m_y = m_y.plus(StateSpaceUtil.makeWhiteNoiseVector(m_measurementStdDevs));
}
}
/**
* Returns the current output of the plant.
*
* @return The current output of the plant.
*/
public Matrix<Outputs, N1> getOutput() {
return m_y;
}
/**
* Returns an element of the current output of the plant.
*
* @param row The row to return.
* @return An element of the current output of the plant.
*/
public double getOutput(int row) {
return m_y.get(row, 0);
}
/**
* Sets the system inputs (usually voltages).
*
* @param u The system inputs.
*/
public void setInput(Matrix<Inputs, N1> u) {
this.m_u = u;
}
/**
* Sets the system inputs.
*
* @param row The row in the input matrix to set.
* @param value The value to set the row to.
*/
public void setInput(int row, double value) {
m_u.set(row, 0, value);
}
/**
* Sets the system inputs.
*
* @param u An array of doubles that represent the inputs of the system.
*/
public void setInput(double... u) {
if (u.length != m_u.getNumRows()) {
throw new MatrixDimensionException(
"Malformed input! Got " + u.length + " elements instead of " + m_u.getNumRows());
}
m_u = new Matrix<>(new SimpleMatrix(m_u.getNumRows(), 1, true, u));
}
/**
* Returns the current input of the plant.
*
* @return The current input of the plant.
*/
public Matrix<Inputs, N1> getInput() {
return m_u;
}
/**
* Returns an element of the current input of the plant.
*
* @param row The row to return.
* @return An element of the current input of the plant.
*/
public double getInput(int row) {
return m_u.get(row, 0);
}
/**
* Sets the system state.
*
* @param state The new state.
*/
public void setState(Matrix<States, N1> state) {
m_x = state;
// Update the output to reflect the new state.
//
// yₖ = Cxₖ + Duₖ
m_y = m_plant.calculateY(m_x, m_u);
}
/**
* Updates the state estimate of the system.
*
* @param currentXhat The current state estimate.
* @param u The system inputs (usually voltage).
* @param dt The time difference between controller updates in seconds.
* @return The new state.
*/
protected Matrix<States, N1> updateX(
Matrix<States, N1> currentXhat, Matrix<Inputs, N1> u, double dt) {
return m_plant.calculateX(currentXhat, u, dt);
}
/**
* Clamp the input vector such that no element exceeds the maximum allowed value. If any does, the
* relative magnitudes of the input will be maintained.
*
* @param maxInput The maximum magnitude of the input vector after clamping.
*/
protected void clampInput(double maxInput) {
m_u = StateSpaceUtil.desaturateInputVector(m_u, maxInput);
}
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifierDataJNI;
/** Class to control simulated notifiers. */
public final class NotifierSim {
private NotifierSim() {}
/**
* Gets the timeout of the next notifier.
*
* @return Timestamp
*/
public static long getNextTimeout() {
return NotifierDataJNI.getNextTimeout();
}
/**
* Gets the total number of notifiers.
*
* @return Count
*/
public static int getNumNotifiers() {
return NotifierDataJNI.getNumNotifiers();
}
}

View File

@@ -0,0 +1,170 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.hal.simulation.PowerDistributionDataJNI;
import edu.wpi.first.wpilibj.PowerDistribution;
/** Class to control a simulated Power Distribution Panel (PDP). */
public class PDPSim {
private final int m_index;
/** Constructs for the default PDP. */
public PDPSim() {
m_index = 0;
}
/**
* Constructs from a PDP module number (CAN ID).
*
* @param module module number
*/
public PDPSim(int module) {
m_index = module;
}
/**
* Constructs from a PowerDistribution object.
*
* @param pdp PowerDistribution to simulate
*/
public PDPSim(PowerDistribution pdp) {
m_index = pdp.getModule();
}
/**
* Register a callback to be run when the PDP is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid =
PowerDistributionDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PowerDistributionDataJNI::cancelInitializedCallback);
}
/**
* Check whether the PDP has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return PowerDistributionDataJNI.getInitialized(m_index);
}
/**
* Define whether the PDP has been initialized.
*
* @param initialized whether this object is initialized
*/
public void setInitialized(boolean initialized) {
PowerDistributionDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run whenever the PDP temperature changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerTemperatureCallback(NotifyCallback callback, boolean initialNotify) {
int uid =
PowerDistributionDataJNI.registerTemperatureCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PowerDistributionDataJNI::cancelTemperatureCallback);
}
/**
* Check the temperature of the PDP.
*
* @return the PDP temperature
*/
public double getTemperature() {
return PowerDistributionDataJNI.getTemperature(m_index);
}
/**
* Define the PDP temperature.
*
* @param temperature the new PDP temperature
*/
public void setTemperature(double temperature) {
PowerDistributionDataJNI.setTemperature(m_index, temperature);
}
/**
* Register a callback to be run whenever the PDP voltage changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerVoltageCallback(NotifyCallback callback, boolean initialNotify) {
int uid = PowerDistributionDataJNI.registerVoltageCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PowerDistributionDataJNI::cancelVoltageCallback);
}
/**
* Check the PDP voltage.
*
* @return the PDP voltage.
*/
public double getVoltage() {
return PowerDistributionDataJNI.getVoltage(m_index);
}
/**
* Set the PDP voltage.
*
* @param voltage the new PDP voltage
*/
public void setVoltage(double voltage) {
PowerDistributionDataJNI.setVoltage(m_index, voltage);
}
/**
* Register a callback to be run whenever the current of a specific channel changes.
*
* @param channel the channel
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerCurrentCallback(
int channel, NotifyCallback callback, boolean initialNotify) {
int uid =
PowerDistributionDataJNI.registerCurrentCallback(m_index, channel, callback, initialNotify);
return new CallbackStore(
m_index, channel, uid, PowerDistributionDataJNI::cancelCurrentCallback);
}
/**
* Read the current in one of the PDP channels.
*
* @param channel the channel to check
* @return the current in the given channel
*/
public double getCurrent(int channel) {
return PowerDistributionDataJNI.getCurrent(m_index, channel);
}
/**
* Change the current in the given channel.
*
* @param channel the channel to edit
* @param current the new current for the channel
*/
public void setCurrent(int channel, double current) {
PowerDistributionDataJNI.setCurrent(m_index, channel, current);
}
/** Reset all PDP simulation data. */
public void resetData() {
PowerDistributionDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.wpilibj.motorcontrol.PWMMotorController;
/** Class to control a simulated PWM motor controller. */
public class PWMMotorControllerSim {
private final SimDouble m_simSpeed;
/**
* Constructor.
*
* @param motorctrl The device to simulate
*/
public PWMMotorControllerSim(PWMMotorController motorctrl) {
this(motorctrl.getChannel());
}
/**
* Constructor.
*
* @param channel The channel the motor controller is attached to.
*/
public PWMMotorControllerSim(int channel) {
SimDeviceSim simDevice = new SimDeviceSim("PWMMotorController", channel);
m_simSpeed = simDevice.getDouble("Speed");
}
/**
* Gets the motor speed set.
*
* @return Speed
*/
public double getSpeed() {
return m_simSpeed.get();
}
}

View File

@@ -0,0 +1,129 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.hal.simulation.PWMDataJNI;
import edu.wpi.first.wpilibj.PWM;
/** Class to control a simulated PWM output. */
public class PWMSim {
private final int m_index;
/**
* Constructs from a PWM object.
*
* @param pwm PWM to simulate
*/
public PWMSim(PWM pwm) {
m_index = pwm.getChannel();
}
/**
* Constructs from a PWM channel number.
*
* @param channel Channel number
*/
public PWMSim(int channel) {
m_index = channel;
}
/**
* Register a callback to be run when the PWM is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = PWMDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PWMDataJNI::cancelInitializedCallback);
}
/**
* Check whether the PWM has been initialized.
*
* @return true if initialized
*/
public boolean getInitialized() {
return PWMDataJNI.getInitialized(m_index);
}
/**
* Define whether the PWM has been initialized.
*
* @param initialized whether this object is initialized
*/
public void setInitialized(boolean initialized) {
PWMDataJNI.setInitialized(m_index, initialized);
}
/**
* Register a callback to be run when the PWM raw value changes.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerPulseMicrosecondCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = PWMDataJNI.registerPulseMicrosecondCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PWMDataJNI::cancelPulseMicrosecondCallback);
}
/**
* Get the PWM pulse microsecond value.
*
* @return the PWM pulse microsecond value
*/
public int getPulseMicrosecond() {
return PWMDataJNI.getPulseMicrosecond(m_index);
}
/**
* Set the PWM pulse microsecond value.
*
* @param microsecondPulseTime the PWM pulse microsecond value
*/
public void setPulseMicrosecond(int microsecondPulseTime) {
PWMDataJNI.setPulseMicrosecond(m_index, microsecondPulseTime);
}
/**
* Register a callback to be run when the PWM period scale changes.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerOutputPeriodCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = PWMDataJNI.registerOutputPeriodCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, PWMDataJNI::cancelOutputPeriodCallback);
}
/**
* Get the PWM period scale.
*
* @return the PWM period scale
*/
public int getOutputPeriod() {
return PWMDataJNI.getOutputPeriod(m_index);
}
/**
* Set the PWM period scale.
*
* @param period the PWM period scale
*/
public void setOutputPeriod(int period) {
PWMDataJNI.setOutputPeriod(m_index, period);
}
/** Reset all simulation data. */
public void resetData() {
PWMDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,173 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.PneumaticsBase;
import edu.wpi.first.wpilibj.PneumaticsModuleType;
/** Common base class for pneumatics module simulation classes. */
public abstract class PneumaticsBaseSim {
/** PneumaticsBase index. */
protected final int m_index;
/**
* Get a module sim for a specific type.
*
* @param module the module number / CAN ID.
* @param type the module type.
* @return the module object.
*/
public static PneumaticsBaseSim getForType(int module, PneumaticsModuleType type) {
return switch (type) {
case CTREPCM -> new CTREPCMSim(module);
case REVPH -> new REVPHSim(module);
};
}
/**
* Constructs a PneumaticsBaseSim with the given index.
*
* @param index The index.
*/
protected PneumaticsBaseSim(int index) {
m_index = index;
}
/**
* Constructs a PneumaticsBaseSim for the given module.
*
* @param module The module.
*/
protected PneumaticsBaseSim(PneumaticsBase module) {
this(module.getModuleNumber());
}
/**
* Check whether the PCM/PH has been initialized.
*
* @return true if initialized
*/
public abstract boolean getInitialized();
/**
* Define whether the PCM/PH has been initialized.
*
* @param initialized true for initialized
*/
public abstract void setInitialized(boolean initialized);
/**
* Register a callback to be run when the PCM/PH is initialized.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public abstract CallbackStore registerInitializedCallback(
NotifyCallback callback, boolean initialNotify);
/**
* Check if the compressor is on.
*
* @return true if the compressor is active
*/
public abstract boolean getCompressorOn();
/**
* Set whether the compressor is active.
*
* @param compressorOn the new value
*/
public abstract void setCompressorOn(boolean compressorOn);
/**
* Register a callback to be run when the compressor activates.
*
* @param callback the callback
* @param initialNotify whether to run the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public abstract CallbackStore registerCompressorOnCallback(
NotifyCallback callback, boolean initialNotify);
/**
* Check the solenoid output on a specific channel.
*
* @param channel the channel to check
* @return the solenoid output
*/
public abstract boolean getSolenoidOutput(int channel);
/**
* Change the solenoid output on a specific channel.
*
* @param channel the channel to check
* @param solenoidOutput the new solenoid output
*/
public abstract void setSolenoidOutput(int channel, boolean solenoidOutput);
/**
* Register a callback to be run when the solenoid output on a channel changes.
*
* @param channel the channel to monitor
* @param callback the callback
* @param initialNotify should the callback be run with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public abstract CallbackStore registerSolenoidOutputCallback(
int channel, NotifyCallback callback, boolean initialNotify);
/**
* Check the value of the pressure switch.
*
* @return the pressure switch value
*/
public abstract boolean getPressureSwitch();
/**
* Set the value of the pressure switch.
*
* @param pressureSwitch the new value
*/
public abstract void setPressureSwitch(boolean pressureSwitch);
/**
* Register a callback to be run whenever the pressure switch value changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public abstract CallbackStore registerPressureSwitchCallback(
NotifyCallback callback, boolean initialNotify);
/**
* Read the compressor current.
*
* @return the current of the compressor connected to this module
*/
public abstract double getCompressorCurrent();
/**
* Set the compressor current.
*
* @param compressorCurrent the new compressor current
*/
public abstract void setCompressorCurrent(double compressorCurrent);
/**
* Register a callback to be run whenever the compressor current changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public abstract CallbackStore registerCompressorCurrentCallback(
NotifyCallback callback, boolean initialNotify);
/** Reset all simulation data for this object. */
public abstract void resetData();
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.hal.simulation.REVPHDataJNI;
import edu.wpi.first.wpilibj.PneumaticHub;
import edu.wpi.first.wpilibj.SensorUtil;
/** Class to control a simulated PneumaticHub (PH). */
public class REVPHSim extends PneumaticsBaseSim {
/** Constructs for the default PH. */
public REVPHSim() {
super(SensorUtil.getDefaultREVPHModule());
}
/**
* Constructs from a PH module number (CAN ID).
*
* @param module module number
*/
public REVPHSim(int module) {
super(module);
}
/**
* Constructs from a PneumaticHum object.
*
* @param module PCM module to simulate
*/
public REVPHSim(PneumaticHub module) {
super(module);
}
/**
* Check whether the closed loop compressor control is active.
*
* @return config type
*/
public int getCompressorConfigType() {
return REVPHDataJNI.getCompressorConfigType(m_index);
}
/**
* Turn on/off the closed loop control of the compressor.
*
* @param compressorConfigType compressor config type
*/
public void setCompressorConfigType(int compressorConfigType) {
REVPHDataJNI.setCompressorConfigType(m_index, compressorConfigType);
}
/**
* Register a callback to be run whenever the closed loop state changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerCompressorConfigTypeCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = REVPHDataJNI.registerCompressorConfigTypeCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelCompressorConfigTypeCallback);
}
@Override
public boolean getInitialized() {
return REVPHDataJNI.getInitialized(m_index);
}
@Override
public void setInitialized(boolean initialized) {
REVPHDataJNI.setInitialized(m_index, initialized);
}
@Override
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
int uid = REVPHDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelInitializedCallback);
}
@Override
public boolean getCompressorOn() {
return REVPHDataJNI.getCompressorOn(m_index);
}
@Override
public void setCompressorOn(boolean compressorOn) {
REVPHDataJNI.setCompressorOn(m_index, compressorOn);
}
@Override
public CallbackStore registerCompressorOnCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = REVPHDataJNI.registerCompressorOnCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelCompressorOnCallback);
}
@Override
public boolean getSolenoidOutput(int channel) {
return REVPHDataJNI.getSolenoidOutput(m_index, channel);
}
@Override
public void setSolenoidOutput(int channel, boolean solenoidOutput) {
REVPHDataJNI.setSolenoidOutput(m_index, channel, solenoidOutput);
}
@Override
public CallbackStore registerSolenoidOutputCallback(
int channel, NotifyCallback callback, boolean initialNotify) {
int uid =
REVPHDataJNI.registerSolenoidOutputCallback(m_index, channel, callback, initialNotify);
return new CallbackStore(m_index, channel, uid, REVPHDataJNI::cancelSolenoidOutputCallback);
}
@Override
public boolean getPressureSwitch() {
return REVPHDataJNI.getPressureSwitch(m_index);
}
@Override
public void setPressureSwitch(boolean pressureSwitch) {
REVPHDataJNI.setPressureSwitch(m_index, pressureSwitch);
}
@Override
public CallbackStore registerPressureSwitchCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = REVPHDataJNI.registerPressureSwitchCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelPressureSwitchCallback);
}
@Override
public double getCompressorCurrent() {
return REVPHDataJNI.getCompressorCurrent(m_index);
}
@Override
public void setCompressorCurrent(double compressorCurrent) {
REVPHDataJNI.setCompressorCurrent(m_index, compressorCurrent);
}
@Override
public CallbackStore registerCompressorCurrentCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = REVPHDataJNI.registerCompressorCurrentCallback(m_index, callback, initialNotify);
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelCompressorCurrentCallback);
}
@Override
public void resetData() {
REVPHDataJNI.resetData(m_index);
}
}

View File

@@ -0,0 +1,304 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.hal.simulation.RoboRioDataJNI;
/** A utility class to control a simulated RoboRIO. */
public final class RoboRioSim {
private RoboRioSim() {
// Utility class
}
/**
* Register a callback to be run whenever the Vin voltage changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerVInVoltageCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerVInVoltageCallback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelVInVoltageCallback);
}
/**
* Measure the Vin voltage.
*
* @return the Vin voltage
*/
public static double getVInVoltage() {
return RoboRioDataJNI.getVInVoltage();
}
/**
* Define the Vin voltage.
*
* @param vInVoltage the new voltage
*/
public static void setVInVoltage(double vInVoltage) {
RoboRioDataJNI.setVInVoltage(vInVoltage);
}
/**
* Register a callback to be run whenever the 3.3V rail voltage changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerUserVoltage3V3Callback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerUserVoltage3V3Callback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelUserVoltage3V3Callback);
}
/**
* Measure the 3.3V rail voltage.
*
* @return the 3.3V rail voltage
*/
public static double getUserVoltage3V3() {
return RoboRioDataJNI.getUserVoltage3V3();
}
/**
* Define the 3.3V rail voltage.
*
* @param userVoltage3V3 the new voltage
*/
public static void setUserVoltage3V3(double userVoltage3V3) {
RoboRioDataJNI.setUserVoltage3V3(userVoltage3V3);
}
/**
* Register a callback to be run whenever the 3.3V rail current changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerUserCurrent3V3Callback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerUserCurrent3V3Callback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelUserCurrent3V3Callback);
}
/**
* Measure the 3.3V rail current.
*
* @return the 3.3V rail current
*/
public static double getUserCurrent3V3() {
return RoboRioDataJNI.getUserCurrent3V3();
}
/**
* Define the 3.3V rail current.
*
* @param userCurrent3V3 the new current
*/
public static void setUserCurrent3V3(double userCurrent3V3) {
RoboRioDataJNI.setUserCurrent3V3(userCurrent3V3);
}
/**
* Register a callback to be run whenever the 3.3V rail active state changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerUserActive3V3Callback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerUserActive3V3Callback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelUserActive3V3Callback);
}
/**
* Get the 3.3V rail active state.
*
* @return true if the 3.3V rail is active
*/
public static boolean getUserActive3V3() {
return RoboRioDataJNI.getUserActive3V3();
}
/**
* Set the 3.3V rail active state.
*
* @param userActive3V3 true to make rail active
*/
public static void setUserActive3V3(boolean userActive3V3) {
RoboRioDataJNI.setUserActive3V3(userActive3V3);
}
/**
* Register a callback to be run whenever the 3.3V rail number of faults changes.
*
* @param callback the callback
* @param initialNotify whether the callback should be called with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerUserFaults3V3Callback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerUserFaults3V3Callback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelUserFaults3V3Callback);
}
/**
* Get the 3.3V rail number of faults.
*
* @return number of faults
*/
public static int getUserFaults3V3() {
return RoboRioDataJNI.getUserFaults3V3();
}
/**
* Set the 3.3V rail number of faults.
*
* @param userFaults3V3 number of faults
*/
public static void setUserFaults3V3(int userFaults3V3) {
RoboRioDataJNI.setUserFaults3V3(userFaults3V3);
}
/**
* Register a callback to be run whenever the Brownout voltage changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerBrownoutVoltageCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerBrownoutVoltageCallback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelBrownoutVoltageCallback);
}
/**
* Measure the Brownout voltage.
*
* @return the Brownout voltage
*/
public static double getBrownoutVoltage() {
return RoboRioDataJNI.getBrownoutVoltage();
}
/**
* Define the Brownout voltage.
*
* @param vInVoltage the new voltage
*/
public static void setBrownoutVoltage(double vInVoltage) {
RoboRioDataJNI.setBrownoutVoltage(vInVoltage);
}
/**
* Register a callback to be run whenever the cpu temp changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerCPUTempCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerCPUTempCallback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelCPUTempCallback);
}
/**
* Get the cpu temp.
*
* @return the cpu temp.
*/
public static double getCPUTemp() {
return RoboRioDataJNI.getCPUTemp();
}
/**
* Set the cpu temp.
*
* @param cpuTemp the new cpu temp.
*/
public static void setCPUTemp(double cpuTemp) {
RoboRioDataJNI.setCPUTemp(cpuTemp);
}
/**
* Register a callback to be run whenever the team number changes.
*
* @param callback the callback
* @param initialNotify whether to call the callback with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerTeamNumberCallback(
NotifyCallback callback, boolean initialNotify) {
int uid = RoboRioDataJNI.registerTeamNumberCallback(callback, initialNotify);
return new CallbackStore(uid, RoboRioDataJNI::cancelTeamNumberCallback);
}
/**
* Get the team number.
*
* @return the team number.
*/
public static int getTeamNumber() {
return RoboRioDataJNI.getTeamNumber();
}
/**
* Set the team number.
*
* @param teamNumber the new team number.
*/
public static void setTeamNumber(int teamNumber) {
RoboRioDataJNI.setTeamNumber(teamNumber);
}
/**
* Get the serial number.
*
* @return The serial number.
*/
public static String getSerialNumber() {
return RoboRioDataJNI.getSerialNumber();
}
/**
* Set the serial number.
*
* @param serialNumber The serial number.
*/
public static void setSerialNumber(String serialNumber) {
RoboRioDataJNI.setSerialNumber(serialNumber);
}
/**
* Get the comments string.
*
* @return The comments string.
*/
public static String getComments() {
return RoboRioDataJNI.getComments();
}
/**
* Set the comments string.
*
* @param comments The comments string.
*/
public static void setComments(String comments) {
RoboRioDataJNI.setComments(comments);
}
/** Reset all simulation data. */
public static void resetData() {
RoboRioDataJNI.resetData();
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StringPublisher;
import edu.wpi.first.wpilibj.RobotBase;
/** Class that facilitates control of a SendableChooser's selected option in simulation. */
public class SendableChooserSim implements AutoCloseable {
private StringPublisher m_publisher;
/**
* Constructs a SendableChooserSim.
*
* @param path The path where the SendableChooser is published.
*/
public SendableChooserSim(String path) {
this(NetworkTableInstance.getDefault(), path);
}
/**
* Constructs a SendableChooserSim.
*
* @param inst The NetworkTables instance.
* @param path The path where the SendableChooser is published.
*/
public SendableChooserSim(NetworkTableInstance inst, String path) {
if (RobotBase.isSimulation()) {
m_publisher = inst.getStringTopic(path + "selected").publish();
}
}
@Override
public void close() {
if (RobotBase.isSimulation()) {
m_publisher.close();
}
}
/**
* Set the selected option.
*
* @param option The option.
*/
public void setSelected(String option) {
if (RobotBase.isSimulation()) {
m_publisher.set(option);
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.wpilibj.SharpIR;
/** Simulation class for Sharp IR sensors. */
public class SharpIRSim {
private final SimDouble m_simRange;
/**
* Constructor.
*
* @param sharpIR The real sensor to simulate
*/
public SharpIRSim(SharpIR sharpIR) {
this(sharpIR.getChannel());
}
/**
* Constructor.
*
* @param channel Analog channel for this sensor
*/
public SharpIRSim(int channel) {
SimDeviceSim simDevice = new SimDeviceSim("SharpIR", channel);
m_simRange = simDevice.getDouble("Range (m)");
}
/**
* Set the range in meters returned by the distance sensor.
*
* @param range range in meters of the target returned by the sensor
*/
public void setRange(double range) {
m_simRange.set(range);
}
}

View File

@@ -0,0 +1,270 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.SimBoolean;
import edu.wpi.first.hal.SimDouble;
import edu.wpi.first.hal.SimEnum;
import edu.wpi.first.hal.SimInt;
import edu.wpi.first.hal.SimLong;
import edu.wpi.first.hal.SimValue;
import edu.wpi.first.hal.simulation.SimDeviceCallback;
import edu.wpi.first.hal.simulation.SimDeviceDataJNI;
import edu.wpi.first.hal.simulation.SimValueCallback;
/** Class to control the simulation side of a SimDevice. */
public class SimDeviceSim {
private final int m_handle;
/**
* Constructs a SimDeviceSim.
*
* @param name name of the SimDevice
*/
public SimDeviceSim(String name) {
this(SimDeviceDataJNI.getSimDeviceHandle(name));
}
/**
* Constructs a SimDeviceSim.
*
* @param name name of the SimDevice
* @param index device index number to append to name
*/
public SimDeviceSim(String name, int index) {
this(name + "[" + index + "]");
}
/**
* Constructs a SimDeviceSim.
*
* @param name name of the SimDevice
* @param index device index number to append to name
* @param channel device channel number to append to name
*/
public SimDeviceSim(String name, int index, int channel) {
this(name + "[" + index + "," + channel + "]");
}
/**
* Constructs a SimDeviceSim.
*
* @param handle the low level handle for the corresponding SimDevice
*/
public SimDeviceSim(int handle) {
m_handle = handle;
}
/**
* Get the name of this object.
*
* @return name
*/
public String getName() {
return SimDeviceDataJNI.getSimDeviceName(m_handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimValue getValue(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimValue(handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimInt getInt(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimInt(handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimLong getLong(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimLong(handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimDouble getDouble(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimDouble(handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimEnum getEnum(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimEnum(handle);
}
/**
* Get the property object with the given name.
*
* @param name the property name
* @return the property object
*/
public SimBoolean getBoolean(String name) {
int handle = SimDeviceDataJNI.getSimValueHandle(m_handle, name);
if (handle <= 0) {
return null;
}
return new SimBoolean(handle);
}
/**
* Get all options for the given enum.
*
* @param val the enum
* @return names of the different values for that enum
*/
public static String[] getEnumOptions(SimEnum val) {
return SimDeviceDataJNI.getSimValueEnumOptions(val.getNativeHandle());
}
/**
* Get all data of this object.
*
* @return all data and fields of this object
*/
public SimDeviceDataJNI.SimValueInfo[] enumerateValues() {
return SimDeviceDataJNI.enumerateSimValues(m_handle);
}
/**
* Get the native handle of this object.
*
* @return the handle used to refer to this object through JNI
*/
public int getNativeHandle() {
return m_handle;
}
/**
* Register a callback to be run every time a new value is added to this device.
*
* @param callback the callback
* @param initialNotify should the callback be run with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerValueCreatedCallback(
SimValueCallback callback, boolean initialNotify) {
int uid = SimDeviceDataJNI.registerSimValueCreatedCallback(m_handle, callback, initialNotify);
return new CallbackStore(uid, SimDeviceDataJNI::cancelSimValueCreatedCallback);
}
/**
* Register a callback to be run every time a value is changed on this device.
*
* @param value simulated value
* @param callback the callback
* @param initialNotify should the callback be run with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerValueChangedCallback(
SimValue value, SimValueCallback callback, boolean initialNotify) {
int uid =
SimDeviceDataJNI.registerSimValueChangedCallback(
value.getNativeHandle(), callback, initialNotify);
return new CallbackStore(uid, SimDeviceDataJNI::cancelSimValueChangedCallback);
}
/**
* Register a callback for SimDouble.reset() and similar functions. The callback is called with
* the old value.
*
* @param value simulated value
* @param callback callback
* @param initialNotify ignored (present for consistency)
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerValueResetCallback(
SimValue value, SimValueCallback callback, boolean initialNotify) {
int uid =
SimDeviceDataJNI.registerSimValueResetCallback(
value.getNativeHandle(), callback, initialNotify);
return new CallbackStore(uid, SimDeviceDataJNI::cancelSimValueResetCallback);
}
/**
* Get all sim devices with the given prefix.
*
* @param prefix the prefix to filter sim devices
* @return all sim devices
*/
public static SimDeviceDataJNI.SimDeviceInfo[] enumerateDevices(String prefix) {
return SimDeviceDataJNI.enumerateSimDevices(prefix);
}
/**
* Register a callback to be run every time a new {@link edu.wpi.first.hal.SimDevice} is created.
*
* @param prefix the prefix to filter sim devices
* @param callback the callback
* @param initialNotify should the callback be run with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerDeviceCreatedCallback(
String prefix, SimDeviceCallback callback, boolean initialNotify) {
int uid = SimDeviceDataJNI.registerSimDeviceCreatedCallback(prefix, callback, initialNotify);
return new CallbackStore(uid, SimDeviceDataJNI::cancelSimDeviceCreatedCallback);
}
/**
* Register a callback to be run every time a {@link edu.wpi.first.hal.SimDevice} is
* freed/destroyed.
*
* @param prefix the prefix to filter sim devices
* @param callback the callback
* @param initialNotify should the callback be run with the initial state
* @return the {@link CallbackStore} object associated with this callback.
*/
public static CallbackStore registerDeviceFreedCallback(
String prefix, SimDeviceCallback callback, boolean initialNotify) {
int uid = SimDeviceDataJNI.registerSimDeviceFreedCallback(prefix, callback, initialNotify);
return new CallbackStore(uid, SimDeviceDataJNI::cancelSimDeviceFreedCallback);
}
/** Reset all SimDevice data. */
public static void resetData() {
SimDeviceDataJNI.resetSimDeviceData();
}
}

View File

@@ -0,0 +1,82 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.SimulatorJNI;
/** Simulation hooks. */
public final class SimHooks {
private SimHooks() {}
/**
* Override the HAL runtime type (simulated/real).
*
* @param type runtime type
*/
public static void setHALRuntimeType(int type) {
SimulatorJNI.setRuntimeType(type);
}
/** Waits until the user program has started. */
public static void waitForProgramStart() {
SimulatorJNI.waitForProgramStart();
}
/** Sets that the user program has started. */
public static void setProgramStarted() {
SimulatorJNI.setProgramStarted();
}
/**
* Returns true if the user program has started.
*
* @return True if the user program has started.
*/
public static boolean getProgramStarted() {
return SimulatorJNI.getProgramStarted();
}
/** Restart the simulator time. */
public static void restartTiming() {
SimulatorJNI.restartTiming();
}
/** Pause the simulator time. */
public static void pauseTiming() {
SimulatorJNI.pauseTiming();
}
/** Resume the simulator time. */
public static void resumeTiming() {
SimulatorJNI.resumeTiming();
}
/**
* Check if the simulator time is paused.
*
* @return true if paused
*/
public static boolean isTimingPaused() {
return SimulatorJNI.isTimingPaused();
}
/**
* Advance the simulator time and wait for all notifiers to run.
*
* @param delta the amount to advance in seconds
*/
public static void stepTiming(double delta) {
SimulatorJNI.stepTiming((long) (delta * 1e6));
}
/**
* Advance the simulator time and return immediately.
*
* @param delta the amount to advance in seconds
*/
public static void stepTimingAsync(double delta) {
SimulatorJNI.stepTimingAsync((long) (delta * 1e6));
}
}

View File

@@ -0,0 +1,267 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N2;
import edu.wpi.first.math.system.LinearSystem;
import edu.wpi.first.math.system.NumericalIntegration;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.math.system.plant.LinearSystemId;
import edu.wpi.first.wpilibj.RobotController;
/** Represents a simulated single jointed arm mechanism. */
public class SingleJointedArmSim extends LinearSystemSim<N2, N1, N2> {
// The gearbox for the arm.
private final DCMotor m_gearbox;
// The gearing between the motors and the output.
private final double m_gearing;
// The length of the arm.
private final double m_armLength;
// The minimum angle that the arm is capable of.
private final double m_minAngle;
// The maximum angle that the arm is capable of.
private final double m_maxAngle;
// Whether the simulator should simulate gravity.
private final boolean m_simulateGravity;
/**
* Creates a simulated arm mechanism.
*
* @param plant The linear system that represents the arm. This system can be created with {@link
* edu.wpi.first.math.system.plant.LinearSystemId#createSingleJointedArmSystem(DCMotor,
* double, double)}.
* @param gearbox The type of and number of motors in the arm gearbox.
* @param gearing The gearing of the arm (numbers greater than 1 represent reductions).
* @param armLength The length of the arm in meters.
* @param minAngleRads The minimum angle that the arm is capable of.
* @param maxAngleRads The maximum angle that the arm is capable of.
* @param simulateGravity Whether gravity should be simulated or not.
* @param startingAngleRads The initial position of the Arm simulation in radians.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for position.
*/
@SuppressWarnings("this-escape")
public SingleJointedArmSim(
LinearSystem<N2, N1, N2> plant,
DCMotor gearbox,
double gearing,
double armLength,
double minAngleRads,
double maxAngleRads,
boolean simulateGravity,
double startingAngleRads,
double... measurementStdDevs) {
super(plant, measurementStdDevs);
m_gearbox = gearbox;
m_gearing = gearing;
m_armLength = armLength;
m_minAngle = minAngleRads;
m_maxAngle = maxAngleRads;
m_simulateGravity = simulateGravity;
setState(startingAngleRads, 0.0);
}
/**
* Creates a simulated arm mechanism.
*
* @param gearbox The type of and number of motors in the arm gearbox.
* @param gearing The gearing of the arm (numbers greater than 1 represent reductions).
* @param j The moment of inertia of the arm in kg-m²; can be calculated from CAD software.
* @param armLength The length of the arm in meters.
* @param minAngleRads The minimum angle that the arm is capable of.
* @param maxAngleRads The maximum angle that the arm is capable of.
* @param simulateGravity Whether gravity should be simulated or not.
* @param startingAngleRads The initial position of the Arm simulation in radians.
* @param measurementStdDevs The standard deviations of the measurements. Can be omitted if no
* noise is desired. If present must have 1 element for position.
*/
public SingleJointedArmSim(
DCMotor gearbox,
double gearing,
double j,
double armLength,
double minAngleRads,
double maxAngleRads,
boolean simulateGravity,
double startingAngleRads,
double... measurementStdDevs) {
this(
LinearSystemId.createSingleJointedArmSystem(gearbox, j, gearing),
gearbox,
gearing,
armLength,
minAngleRads,
maxAngleRads,
simulateGravity,
startingAngleRads,
measurementStdDevs);
}
/**
* Sets the arm's state. The new angle will be limited between the minimum and maximum allowed
* limits.
*
* @param angleRadians The new angle in radians.
* @param velocityRadPerSec The new angular velocity in radians per second.
*/
public final void setState(double angleRadians, double velocityRadPerSec) {
setState(VecBuilder.fill(Math.clamp(angleRadians, m_minAngle, m_maxAngle), velocityRadPerSec));
}
/**
* Returns whether the arm would hit the lower limit.
*
* @param currentAngleRads The current arm height.
* @return Whether the arm would hit the lower limit.
*/
public boolean wouldHitLowerLimit(double currentAngleRads) {
return currentAngleRads <= this.m_minAngle;
}
/**
* Returns whether the arm would hit the upper limit.
*
* @param currentAngleRads The current arm height.
* @return Whether the arm would hit the upper limit.
*/
public boolean wouldHitUpperLimit(double currentAngleRads) {
return currentAngleRads >= this.m_maxAngle;
}
/**
* Returns whether the arm has hit the lower limit.
*
* @return Whether the arm has hit the lower limit.
*/
public boolean hasHitLowerLimit() {
return wouldHitLowerLimit(getAngle());
}
/**
* Returns whether the arm has hit the upper limit.
*
* @return Whether the arm has hit the upper limit.
*/
public boolean hasHitUpperLimit() {
return wouldHitUpperLimit(getAngle());
}
/**
* Returns the current arm angle.
*
* @return The current arm angle in radians.
*/
public double getAngle() {
return getOutput(0);
}
/**
* Returns the current arm velocity.
*
* @return The current arm velocity in radians per second.
*/
public double getVelocity() {
return getOutput(1);
}
/**
* Returns the arm current draw.
*
* @return The arm current draw in amps.
*/
public double getCurrentDraw() {
// Reductions are greater than 1, so a reduction of 10:1 would mean the motor is
// spinning 10x faster than the output
var motorVelocity = m_x.get(1, 0) * m_gearing;
return m_gearbox.getCurrent(motorVelocity, m_u.get(0, 0)) * Math.signum(m_u.get(0, 0));
}
/**
* Sets the input voltage for the arm.
*
* @param volts The input voltage.
*/
public void setInputVoltage(double volts) {
setInput(volts);
clampInput(RobotController.getBatteryVoltage());
}
/**
* Calculates a rough estimate of the moment of inertia of an arm given its length and mass.
*
* @param length The length of the arm in m.
* @param mass The mass of the arm in kg.
* @return The calculated moment of inertia in kg-m².
*/
public static double estimateMOI(double length, double mass) {
return 1.0 / 3.0 * mass * length * length;
}
/**
* Updates the state of the arm.
*
* @param currentXhat The current state estimate.
* @param u The system inputs (voltage).
* @param dt The time difference between controller updates in seconds.
*/
@Override
protected Matrix<N2, N1> updateX(Matrix<N2, N1> currentXhat, Matrix<N1, N1> u, double dt) {
// The torque on the arm is given by τ = F⋅r, where F is the force applied by
// gravity and r the distance from pivot to center of mass. Recall from
// dynamics that the sum of torques for a rigid body is τ = J⋅α, were τ is
// torque on the arm, J is the mass-moment of inertia about the pivot axis,
// and α is the angular acceleration in rad/s². Rearranging yields: α = F⋅r/J
//
// We substitute in F = m⋅g⋅cos(θ), where θ is the angle from horizontal:
//
// α = (m⋅g⋅cos(θ))⋅r/J
//
// Multiply RHS by cos(θ) to account for the arm angle. Further, we know the
// arm mass-moment of inertia J of our arm is given by J=1/3 mL², modeled as a
// rod rotating about it's end, where L is the overall rod length. The mass
// distribution is assumed to be uniform. Substitute r=L/2 to find:
//
// α = (m⋅g⋅cos(θ))⋅r/(1/3 mL²)
// α = (m⋅g⋅cos(θ))⋅(L/2)/(1/3 mL²)
// α = 3/2⋅g⋅cos(θ)/L
//
// This acceleration is next added to the linear system dynamics ẋ=Ax+Bu
//
// f(x, u) = Ax + Bu + [0 α]ᵀ
// f(x, u) = Ax + Bu + [0 3/2⋅g⋅cos(θ)/L]ᵀ
Matrix<N2, N1> updatedXhat =
NumericalIntegration.rkdp(
(Matrix<N2, N1> x, Matrix<N1, N1> _u) -> {
Matrix<N2, N1> xdot = m_plant.getA().times(x).plus(m_plant.getB().times(_u));
if (m_simulateGravity) {
double alphaGrav = 3.0 / 2.0 * -9.8 * Math.cos(x.get(0, 0)) / m_armLength;
xdot = xdot.plus(VecBuilder.fill(0, alphaGrav));
}
return xdot;
},
currentXhat,
u,
dt);
// We check for collision after updating xhat
if (wouldHitLowerLimit(updatedXhat.get(0, 0))) {
return VecBuilder.fill(m_minAngle, 0);
}
if (wouldHitUpperLimit(updatedXhat.get(0, 0))) {
return VecBuilder.fill(m_maxAngle, 0);
}
return updatedXhat;
}
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.simulation;
import edu.wpi.first.hal.simulation.NotifyCallback;
import edu.wpi.first.wpilibj.PneumaticsBase;
import edu.wpi.first.wpilibj.PneumaticsModuleType;
/** Class to control a simulated {@link edu.wpi.first.wpilibj.Solenoid}. */
public class SolenoidSim {
private final PneumaticsBaseSim m_module;
private final int m_channel;
/**
* Constructs for a solenoid on the given pneumatics module.
*
* @param moduleSim the PCM the solenoid is connected to.
* @param channel the solenoid channel.
*/
public SolenoidSim(PneumaticsBaseSim moduleSim, int channel) {
m_module = moduleSim;
m_channel = channel;
}
/**
* Constructs for a solenoid on a pneumatics module of the given type and ID.
*
* @param module the CAN ID of the pneumatics module the solenoid is connected to.
* @param moduleType the module type (PH or PCM)
* @param channel the solenoid channel.
*/
public SolenoidSim(int module, PneumaticsModuleType moduleType, int channel) {
this(PneumaticsBaseSim.getForType(module, moduleType), channel);
}
/**
* Constructs for a solenoid on a pneumatics module of the given type and default ID.
*
* @param moduleType the module type (PH or PCM)
* @param channel the solenoid channel.
*/
public SolenoidSim(PneumaticsModuleType moduleType, int channel) {
this(PneumaticsBase.getDefaultForType(moduleType), moduleType, channel);
}
/**
* Check the solenoid output.
*
* @return the solenoid output
*/
public boolean getOutput() {
return m_module.getSolenoidOutput(m_channel);
}
/**
* Change the solenoid output.
*
* @param output the new solenoid output
*/
public void setOutput(boolean output) {
m_module.setSolenoidOutput(m_channel, output);
}
/**
* Register a callback to be run when the output of this solenoid has changed.
*
* @param callback the callback
* @param initialNotify should the callback be run with the initial value
* @return the {@link CallbackStore} object associated with this callback.
*/
public CallbackStore registerOutputCallback(NotifyCallback callback, boolean initialNotify) {
return m_module.registerSolenoidOutputCallback(m_channel, callback, initialNotify);
}
/**
* Get the wrapped {@link PneumaticsBaseSim} object.
*
* @return the wrapped {@link PneumaticsBaseSim} object.
*/
public PneumaticsBaseSim getPCMSim() {
return m_module;
}
}