Signals represent live data reported by a device; these can be yaw, position, etc. To make use of the live data, users need to know the value, timestamp, latency, units, and error condition of the data. Additionally, users may need to synchronize with fresh data to minimize latency.
The device object provides getters for all available signals. Each getter returns a
StatusSignal that is typed appropriately for the signal.
The device getters return a cached
StatusSignal. As a result, frequently calling the getter does not influence RAM performance.
var supplyVoltageSignal = m_device.getSupplyVoltage();
auto& supplyVoltageSignal = m_device.GetSupplyVoltage();
The value of the signal can be retrieved from the
StatusSignal by calling
var supplyVoltage = supplyVoltageSignal.getValue();
auto supplyVoltage = supplyVoltageSignal.GetValue();
Phoenix 6 utilizes the C++ units library when applicable.
If a status signal is not available on the CAN bus, an error will be reported to the Driver Station.
Refreshing the Signal Value#
StatusSignal getters implicitly refresh the cached signal values. However, if the user application caches the
StatusSignal object, the
refresh() method must be called to fetch fresh data. Multiple signals can be refreshed in one call using
BaseStatusSignal.refreshAll() (Java, C++).
refresh() method can be method-chained. As a result, you can call
getValue() on one line.
// refresh the supply voltage signal supplyVoltageSignal.refresh(); // refresh the position and velocity signals BaseStatusSignal.refreshAll(positionSignal, velocitySignal);
// refresh the supply voltage signal supplyVoltageSignal.Refresh(); // refresh the position and velocity signals BaseStatusSignal::RefreshAll(positionSignal, velocitySignal);
Waiting for Signal Updates#
Instead of using the latest value, the user can instead opt to synchronously wait for a signal update.
StatusSignal provides a
waitForUpdate(timeoutSec) method that will block the current robot loop until the signal is retrieved or the timeout has been exceeded. This replaces the need to call
refresh() on cached
If you want to zero your sensors, you can use this API to ensure the set operation has completed before continuing program flow.
waitForUpdate() method can be method-chained. As a result, you can call
getValue() on one line.
// wait up to 1 robot loop iteration (20ms) for fresh data supplyVoltageSignal.waitForUpdate(0.020);
// wait up to 1 robot loop iteration (20ms) for fresh data supplyVoltageSignal.WaitForUpdate(20_ms);
Changing Update Frequency#
All signals can have their update frequency configured via the
setUpdateFrequency() method. Additionally, the update frequency of multiple signals can be specified at once using
BaseStatusSignal.setUpdateFrequencyForAll() (Java, C++).
Increasing signal frequency will also increase CAN bus utilization, which can cause indeterminate behavior at high utilization rates (>90%). This is less of a concern when using CANivore, which uses the higher-bandwidth CAN FD bus.
// disable supply voltage reporting (0 Hz) supplyVoltageSignal.setUpdateFrequency(0); // speed up position and velocity reporting to 200 Hz BaseStatusSignal.setUpdateFrequencyForAll(200, positionSignal, velocitySignal);
// disable supply voltage reporting (0 Hz) supplyVoltageSignal.SetUpdateFrequency(0_Hz); // speed up position and velocity reporting to 200 Hz BaseStatusSignal::SetUpdateFrequencyForAll(200_Hz, positionSignal, velocitySignal);
When different update frequencies are specified for signals that share a status frame, the highest update frequency of all the relevant signals will be applied to the entire frame. Users can get a signal’s applied update frequency using the
Signal update frequencies are automatically reapplied by the robot program on device reset.
Optimizing Bus Utilization#
For users that wish to disable every unused status signal for their devices to reduce bus utilization, device objects have an
optimizeBusUtilization() method (Java, C++). Additionally, multiple devices can be optimized at once using
ParentDevice.optimizeBusUtilizationForAll() (Java, C++).
When optimizing the bus utilization for devices, all status signals that have not been given an update frequency using
setUpdateFrequency() will be disabled. This results in an opt-in model for status signals, maximizing the reduction in bus utilization.
m_pigeon.optimizeBusUtilization(); ParentDevice.optimizeBusUtilizationForAll(m_leftMotor, m_rightMotor, m_cancoder);
m_pigeon.OptimizeBusUtilization(); hardware::ParentDevice::OptimizeBusUtilizationForAll(m_leftMotor, m_rightMotor, m_cancoder);
The timestamps of a
StatusSignal can be retrieved by calling
getAllTimestamps(), which returns a collection of
Timestamp (Java, C++) objects. The
Timestamp objects can be used to perform latency compensation math.
CANivore Timesync requires the devices or the CANivore to be Pro licensed.
When using CANivore, the attached CAN devices will automatically synchronize their time bases. This allows devices to sample and publish their signals in a synchronized manner.
waitForAll() with a timeout of zero matches the behavior of
refreshAll(), performing a non-blocking refresh on all signals passed in.
Because the devices are synchronized, time-critical signals are sampled and published on the same schedule. This combined with the
waitForAll() routine means applications can considerably reduce the latency of the timesync signals. This is particularly useful for multi-device mechanisms, such as swerve odometry.
When using a non-zero timeout, the signals passed into
waitForAll() should have the same update frequency for synchronous data acquisition. This can be done by calling
setUpdateFrequency() or by referring to the API documentation.
The diagram below demonstrates the benefits of using timesync to synchronously acquire signals from multiple devices.
The following signals are time-synchronized:
Yaw, Pitch, & Roll
var talonFXPositionSignal = m_talonFX.getPosition(); var cancoderPositionSignal = m_cancoder.getPosition(); var pigeon2YawSignal = m_pigeon2.getYaw(); BaseStatusSignal.waitForAll(0.020, talonFXPositionSignal, cancoderPositionSignal, pigeon2YawSignal);
auto& talonFXPositionSignal = m_talonFX.GetPosition(); auto& cancoderPositionSignal = m_cancoder.GetPosition(); auto& pigeon2YawSignal = m_pigeon2.GetYaw(); BaseStatusSignal::WaitForAll(20_ms, talonFXPositionSignal, cancoderPositionSignal, pigeon2YawSignal);
getLatencyCompensatedValue() does not automatically refresh the signals. As a result, the user must ensure the
signalSlope parameters are refreshed before retrieving a compensated value.
double compensatedTurns = BaseStatusSignal.getLatencyCompensatedValue(m_motor.getPosition(), m_motor.getVelocity());
auto compensatedTurns = BaseStatusSignal::GetLatencyCompensatedValue(m_motor.GetPosition(), m_motor.GetVelocity());
StatusSignal objects have a
getDataCopy() method that returns a new
SignalMeasurement (Java, C++) object.
SignalMeasurement is a Passive Data Structure that provides all the information about a signal at the time of the
getDataCopy() call, which can be useful for data logging.
getDataCopy() returns a new
SignalMeasurement object every call. Java users should avoid using this API in RAM-constrained applications.