Status Signals#
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.
StatusSignalValue
#
The StatusSignalValue
(Java, C++) is a signal object that provides APIs to address all of the requirements listed above.
The device object provides getters for all available signals. Each getter returns a StatusSignalValue
that is typed appropriately for the signal.
Note
The device getters return a cached StatusSignalValue
. 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 StatusSignalValue
by calling getValue()
.
var supplyVoltage = supplyVoltageSignal.getValue();
auto supplyVoltage = supplyVoltageSignal.GetValue();
Note
Phoenix Pro utilizes the C++ units library when applicable.
The StatusCode
(Java, C++) of the signal can be retrieved by calling getError()
.
This can be used to determine if the device is not present on the CAN bus.
Note
If a status signal is not available on the CAN bus, an error will be reported to the Driver Station.
Refreshing the Signal Value#
The device StatusSignalValue
getters implicitly refresh the cached signal values. However, if the user application caches the StatusSignalValue
object, the refresh()
method must be called to fetch fresh data.
Tip
The refresh()
method can be method-chained. As a result, you can call refresh()
and getValue()
on one line.
supplyVoltageSignal.refresh();
supplyVoltageSignal.Refresh();
Waiting for Signal Updates#
Instead of using the latest value, the user can instead opt to synchronously wait for a signal update. StatusSignalValue
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 StatusSignalValue
objects.
Tip
If you want to zero your sensors, you can use this API to ensure the set operation has completed before continuing program flow.
Tip
The waitForUpdate()
method can be method-chained. As a result, you can call waitForUpdate()
and 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.
Warning
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.
// slow down supply voltage reporting to 10 Hz
supplyVoltageSignal.setUpdateFrequency(10);
// slow down supply voltage reporting to 10 Hz
supplyVoltageSignal.SetUpdateFrequency(10_Hz);
Timestamps#
The timestamps of a StatusSignalValue
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#
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.
Users can synchronously wait for these signals to update using BaseStatusSignalValue.waitForAll()
(Java, C++).
Tip
waitForAll()
can be used with a timeout of zero to perform 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.
Note
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 following signals are time-synchronized:
TalonFX
All Signals
CANcoder
All Signals
Pigeon 2.0
Yaw, Pitch, & Roll
Quaternion
Gravity Vector
Accum Gyro
Angular Rate
Accelerometer
Temperature
var talonFXPositionSignal = m_talonFX.getPosition();
var cancoderPositionSignal = m_cancoder.getPosition();
var pigeon2YawSignal = m_pigeon2.getYaw();
BaseStatusSignalValue.waitForAll(0.020, talonFXPositionSignal, cancoderPositionSignal, pigeon2YawSignal);
auto& talonFXPositionSignal = m_talonFX.GetPosition();
auto& cancoderPositionSignal = m_cancoder.GetPosition();
auto& pigeon2YawSignal = m_pigeon2.GetYaw();
BaseStatusSignalValue::WaitForAll(20_ms, {&talonFXPositionSignal, &cancoderPositionSignal, &pigeon2YawSignal});
Latency Compensation#
Users can perform latency compensation using BaseStatusSignalValue.getLatencyCompensatedValue()
(Java, C++).
Important
getLatencyCompensatedValue()
does not automatically refresh the signals. As a result, the user must ensure the signal
and signalSlope
parameters are refreshed before retrieving a compensated value.
double compensatedTurns = BaseStatusSignalValue.getLatencyCompensatedValue(m_motor.getPosition(), m_motor.getVelocity());
auto compensatedTurns = BaseStatusSignalValue::GetLatencyCompensatedValue(m_motor.GetPosition(), m_motor.GetVelocity());
SignalMeasurement
#
All StatusSignalValue
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.
Warning
getDataCopy()
returns a new SignalMeasurement
object every call. Java users should avoid using this API in RAM-constrained applications.