[hal,wpilib] Rename FPGA clock to monotonic clock (#8672)

- Remove status return from HAL level (clock getting should never fail)
- Remove 32-bit timestamp expand function
- Make monotonic_clock.hpp (formerly fpga_clock.hpp) header-only and
move to root hal include directory
This commit is contained in:
Peter Johnson
2026-03-15 15:08:41 -07:00
committed by GitHub
parent 1a5b023235
commit e944ae9aca
59 changed files with 233 additions and 358 deletions

View File

@@ -71,11 +71,11 @@ public final class HALUtil extends JNIWrapper {
public static native int getTeamNumber(); public static native int getTeamNumber();
/** /**
* Reads the microsecond-resolution timer on the FPGA. * Reads the microsecond-resolution monotonic timer.
* *
* @return The current time in microseconds according to the FPGA (since FPGA reset). * @return The current monotonic time in microseconds.
*/ */
public static native long getFPGATime(); public static native long getMonotonicTime();
/** /**
* Returns the runtime type of the HAL. * Returns the runtime type of the HAL.

View File

@@ -1,32 +0,0 @@
// 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.
#include "wpi/hal/cpp/fpga_clock.hpp"
#include <cstdio>
#include <limits>
#include "wpi/hal/HAL.h"
#include "wpi/util/print.hpp"
namespace wpi::hal {
const fpga_clock::time_point fpga_clock::min_time =
fpga_clock::time_point(fpga_clock::duration(
std::numeric_limits<fpga_clock::duration::rep>::min()));
fpga_clock::time_point fpga_clock::now() noexcept {
int32_t status = 0;
uint64_t currentTime = HAL_GetFPGATime(&status);
if (status != 0) {
wpi::util::print(
stderr,
"Call to HAL_GetFPGATime failed in fpga_clock::now() with status {}. "
"Initialization might have failed. Time will not be correct\n",
status);
std::fflush(stderr);
return epoch();
}
return time_point(std::chrono::microseconds(currentTime));
}
} // namespace wpi::hal

View File

@@ -473,17 +473,14 @@ Java_org_wpilib_hardware_hal_HALUtil_getTeamNumber
/* /*
* Class: org_wpilib_hardware_hal_HALUtil * Class: org_wpilib_hardware_hal_HALUtil
* Method: getFPGATime * Method: getMonotonicTime
* Signature: ()J * Signature: ()J
*/ */
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_org_wpilib_hardware_hal_HALUtil_getFPGATime Java_org_wpilib_hardware_hal_HALUtil_getMonotonicTime
(JNIEnv* env, jclass) (JNIEnv* env, jclass)
{ {
int32_t status = 0; return HAL_GetMonotonicTime();
jlong returnValue = HAL_GetFPGATime(&status);
CheckStatus(env, status);
return returnValue;
} }
/* /*

View File

@@ -111,29 +111,11 @@ HAL_Bool HAL_GetBrownedOut(int32_t* status);
int32_t HAL_GetCommsDisableCount(int32_t* status); int32_t HAL_GetCommsDisableCount(int32_t* status);
/** /**
* Reads the microsecond-resolution timer on the FPGA. * Reads the microsecond-resolution monotonic timer.
* *
* @param[out] status the error code, or 0 for success * @return The current monotonic time in microseconds.
* @return The current time in microseconds according to the FPGA (since FPGA
* reset).
*/ */
uint64_t HAL_GetFPGATime(int32_t* status); uint64_t HAL_GetMonotonicTime(void);
/**
* Given an 32 bit FPGA time, expand it to the nearest likely 64 bit FPGA time.
*
* Note: This is making the assumption that the timestamp being converted is
* always in the past. If you call this with a future timestamp, it probably
* will make it in the past. If you wait over 70 minutes between capturing the
* bottom 32 bits of the timestamp and expanding it, you will be off by
* multiples of 1<<32 microseconds.
*
* @param[in] unexpandedLower 32 bit FPGA time
* @param[out] status the error code, or 0 for success
* @return The current time in microseconds according to the FPGA (since FPGA
* reset) as a 64 bit number.
*/
uint64_t HAL_ExpandFPGATime(uint32_t unexpandedLower, int32_t* status);
/** /**
* Gets the current state of the Robot Signal Light (RSL). * Gets the current state of the Robot Signal Light (RSL).

View File

@@ -1,31 +0,0 @@
// 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.
#pragma once
#include <chrono>
/** WPILib Hardware Abstraction Layer (HAL) namespace */
namespace wpi::hal {
/**
* A std::chrono compatible wrapper around the FPGA Timer.
*/
class fpga_clock {
public:
using rep = std::chrono::microseconds::rep;
using period = std::chrono::microseconds::period;
using duration = std::chrono::microseconds;
using time_point = std::chrono::time_point<fpga_clock>;
static fpga_clock::time_point now() noexcept;
static constexpr bool is_steady = true;
static fpga_clock::time_point epoch() noexcept { return time_point(zero()); }
static fpga_clock::duration zero() noexcept { return duration(0); }
static const time_point min_time;
};
} // namespace wpi::hal

View File

@@ -0,0 +1,38 @@
// 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.
#pragma once
#include <chrono>
#include <limits>
#include "wpi/hal/HAL.h"
/** WPILib Hardware Abstraction Layer (HAL) namespace */
namespace wpi::hal {
/**
* A std::chrono compatible wrapper around the HAL monotonic timer.
*/
class monotonic_clock {
public:
using rep = std::chrono::microseconds::rep;
using period = std::chrono::microseconds::period;
using duration = std::chrono::microseconds;
using time_point = std::chrono::time_point<monotonic_clock>;
static time_point now() noexcept {
uint64_t currentTime = HAL_GetMonotonicTime();
return time_point(std::chrono::microseconds(currentTime));
}
static constexpr bool is_steady = true;
static time_point epoch() noexcept { return time_point(zero()); }
static duration zero() noexcept { return duration(0); }
static constexpr time_point min_time =
time_point(duration(std::numeric_limits<duration::rep>::min()));
};
} // namespace wpi::hal

View File

@@ -75,7 +75,7 @@ void HAL_SetAlertActive(HAL_AlertHandle alertHandle, HAL_Bool active,
// Already active, do nothing (avoids cost of getting time) // Already active, do nothing (avoids cost of getting time)
return; return;
} }
int64_t now = HAL_GetFPGATime(status); int64_t now = HAL_GetMonotonicTime();
int64_t expected = 0; int64_t expected = 0;
// use compare-exchange to avoid potential race // use compare-exchange to avoid potential race
alert->activeStartTime.compare_exchange_strong(expected, now); alert->activeStartTime.compare_exchange_strong(expected, now);

View File

@@ -20,7 +20,7 @@
#include "mockdata/DriverStationDataInternal.hpp" #include "mockdata/DriverStationDataInternal.hpp"
#include "wpi/hal/DriverStationTypes.h" #include "wpi/hal/DriverStationTypes.h"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp" #include "wpi/hal/monotonic_clock.hpp"
#include "wpi/hal/simulation/MockHooks.h" #include "wpi/hal/simulation/MockHooks.h"
#include "wpi/util/EventVector.hpp" #include "wpi/util/EventVector.hpp"
#include "wpi/util/mutex.hpp" #include "wpi/util/mutex.hpp"
@@ -155,16 +155,16 @@ int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
static constexpr int KEEP_MSGS = 5; static constexpr int KEEP_MSGS = 5;
std::scoped_lock lock(msgMutex); std::scoped_lock lock(msgMutex);
static std::string prevMsg[KEEP_MSGS]; static std::string prevMsg[KEEP_MSGS];
static fpga_clock::time_point prevMsgTime[KEEP_MSGS]; static monotonic_clock::time_point prevMsgTime[KEEP_MSGS];
static bool initialized = false; static bool initialized = false;
if (!initialized) { if (!initialized) {
for (int i = 0; i < KEEP_MSGS; i++) { for (int i = 0; i < KEEP_MSGS; i++) {
prevMsgTime[i] = fpga_clock::now() - std::chrono::seconds(2); prevMsgTime[i] = monotonic_clock::now() - std::chrono::seconds(2);
} }
initialized = true; initialized = true;
} }
auto curTime = fpga_clock::now(); auto curTime = monotonic_clock::now();
int i; int i;
for (i = 0; i < KEEP_MSGS; ++i) { for (i = 0; i < KEEP_MSGS; ++i) {
if (prevMsg[i] == details) { if (prevMsg[i] == details) {

View File

@@ -254,32 +254,8 @@ int32_t HAL_GetTeamNumber(void) {
return HALSIM_GetRoboRioTeamNumber(); return HALSIM_GetRoboRioTeamNumber();
} }
uint64_t HAL_GetFPGATime(int32_t* status) { uint64_t HAL_GetMonotonicTime(void) {
return wpi::hal::GetFPGATime(); return wpi::hal::GetMonotonicTime();
}
uint64_t HAL_ExpandFPGATime(uint32_t unexpandedLower, int32_t* status) {
// Capture the current FPGA time. This will give us the upper half of the
// clock.
uint64_t fpgaTime = HAL_GetFPGATime(status);
if (*status != 0) {
return 0;
}
// Now, we need to detect the case where the lower bits rolled over after we
// sampled. In that case, the upper bits will be 1 bigger than they should
// be.
// Break it into lower and upper portions.
uint32_t lower = fpgaTime & 0xffffffffull;
uint64_t upper = (fpgaTime >> 32) & 0xffffffff;
// The time was sampled *before* the current time, so roll it back.
if (lower < unexpandedLower) {
--upper;
}
return (upper << 32) + static_cast<uint64_t>(unexpandedLower);
} }
HAL_Bool HAL_GetSystemActive(int32_t* status) { HAL_Bool HAL_GetSystemActive(int32_t* status) {

View File

@@ -25,7 +25,7 @@ static std::atomic<uint64_t> programStepTime{0};
namespace wpi::hal::init { namespace wpi::hal::init {
void InitializeMockHooks() { void InitializeMockHooks() {
wpi::util::SetNowImpl(GetFPGATime); wpi::util::SetNowImpl(GetMonotonicTime);
} }
} // namespace wpi::hal::init } // namespace wpi::hal::init
@@ -59,7 +59,7 @@ void StepTiming(uint64_t delta) {
programStepTime += delta; programStepTime += delta;
} }
uint64_t GetFPGATime() { uint64_t GetMonotonicTime() {
uint64_t curTime = programPauseTime; uint64_t curTime = programPauseTime;
if (curTime == 0) { if (curTime == 0) {
curTime = wpi::util::NowDefault(); curTime = wpi::util::NowDefault();
@@ -67,10 +67,6 @@ uint64_t GetFPGATime() {
return curTime + programStepTime - programStartTime; return curTime + programStepTime - programStartTime;
} }
double GetFPGATimestamp() {
return GetFPGATime() * 1.0e-6;
}
void SetProgramStarted(bool started) { void SetProgramStarted(bool started) {
programStarted = started; programStarted = started;
} }
@@ -131,8 +127,7 @@ void HALSIM_StepTiming(uint64_t delta) {
WaitNotifiers(); WaitNotifiers();
while (delta > 0) { while (delta > 0) {
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
uint64_t nextTimeout = HALSIM_GetNextNotifierTimeout(); uint64_t nextTimeout = HALSIM_GetNextNotifierTimeout();
uint64_t step = (std::min)(delta, nextTimeout - curTime); uint64_t step = (std::min)(delta, nextTimeout - curTime);

View File

@@ -17,9 +17,7 @@ bool IsTimingPaused();
void StepTiming(uint64_t delta); void StepTiming(uint64_t delta);
uint64_t GetFPGATime(); uint64_t GetMonotonicTime();
double GetFPGATimestamp();
void SetProgramStarted(); void SetProgramStarted();
} // namespace wpi::hal } // namespace wpi::hal

View File

@@ -93,8 +93,7 @@ void NotifierThread::Main() {
// Wait until next alarm // Wait until next alarm
const Alarm& alarm = m_alarmQueue.top(); const Alarm& alarm = m_alarmQueue.top();
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
if (alarm.notifier->alarmTime > curTime) { if (alarm.notifier->alarmTime > curTime) {
m_cond.wait_for( m_cond.wait_for(
lock, std::chrono::microseconds{alarm.notifier->alarmTime - curTime}); lock, std::chrono::microseconds{alarm.notifier->alarmTime - curTime});
@@ -114,8 +113,7 @@ void NotifierThread::Main() {
void NotifierThread::ProcessAlarms( void NotifierThread::ProcessAlarms(
wpi::util::SmallVectorImpl<HAL_NotifierHandle>* signaled) { wpi::util::SmallVectorImpl<HAL_NotifierHandle>* signaled) {
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
// Process alarms // Process alarms
while (!m_alarmQueue.empty() && while (!m_alarmQueue.empty() &&
@@ -264,7 +262,7 @@ void HAL_SetNotifierAlarm(HAL_NotifierHandle notifierHandle, uint64_t alarmTime,
} }
if (!absolute) { if (!absolute) {
alarmTime += HAL_GetFPGATime(status); alarmTime += HAL_GetMonotonicTime();
} }
uint64_t prevWakeup = UINT64_MAX; uint64_t prevWakeup = UINT64_MAX;

View File

@@ -20,8 +20,8 @@
#include "SystemServerInternal.hpp" #include "SystemServerInternal.hpp"
#include "wpi/hal/AddressableLEDTypes.h" #include "wpi/hal/AddressableLEDTypes.h"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp"
#include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/handles/HandlesInternal.hpp"
#include "wpi/hal/monotonic_clock.hpp"
#include "wpi/nt/NetworkTableInstance.hpp" #include "wpi/nt/NetworkTableInstance.hpp"
#include "wpi/nt/RawTopic.hpp" #include "wpi/nt/RawTopic.hpp"
@@ -133,9 +133,9 @@ void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
smartIoHandles->Free(handle, HAL_HandleEnum::AddressableLED); smartIoHandles->Free(handle, HAL_HandleEnum::AddressableLED);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("AddressableLED handle free timeout"); std::puts("AddressableLED handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -75,7 +75,7 @@ void HAL_SetAlertActive(HAL_AlertHandle alertHandle, HAL_Bool active,
// Already active, do nothing (avoids cost of getting time) // Already active, do nothing (avoids cost of getting time)
return; return;
} }
int64_t now = HAL_GetFPGATime(status); int64_t now = HAL_GetMonotonicTime();
int64_t expected = 0; int64_t expected = 0;
// use compare-exchange to avoid potential race // use compare-exchange to avoid potential race
alert->activeStartTime.compare_exchange_strong(expected, now); alert->activeStartTime.compare_exchange_strong(expected, now);

View File

@@ -12,8 +12,8 @@
#include "PortsInternal.hpp" #include "PortsInternal.hpp"
#include "SmartIo.hpp" #include "SmartIo.hpp"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp"
#include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/handles/HandlesInternal.hpp"
#include "wpi/hal/monotonic_clock.hpp"
namespace wpi::hal::init { namespace wpi::hal::init {
void InitializeAnalogInput() {} void InitializeAnalogInput() {}
@@ -73,9 +73,9 @@ void HAL_FreeAnalogInputPort(HAL_AnalogInputHandle analogPortHandle) {
smartIoHandles->Free(analogPortHandle, HAL_HandleEnum::AnalogInput); smartIoHandles->Free(analogPortHandle, HAL_HandleEnum::AnalogInput);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("DIO handle free timeout"); std::puts("DIO handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -13,7 +13,7 @@
#include "HALInternal.hpp" #include "HALInternal.hpp"
#include "PortsInternal.hpp" #include "PortsInternal.hpp"
#include "SmartIo.hpp" #include "SmartIo.hpp"
#include "wpi/hal/cpp/fpga_clock.hpp" #include "wpi/hal/monotonic_clock.hpp"
using namespace wpi::hal; using namespace wpi::hal;
@@ -74,9 +74,9 @@ void HAL_FreeCounter(HAL_CounterHandle counterHandle) {
smartIoHandles->Free(counterHandle, HAL_HandleEnum::Counter); smartIoHandles->Free(counterHandle, HAL_HandleEnum::Counter);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("DIO handle free timeout"); std::puts("DIO handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -13,8 +13,8 @@
#include "PortsInternal.hpp" #include "PortsInternal.hpp"
#include "SmartIo.hpp" #include "SmartIo.hpp"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp"
#include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/handles/HandlesInternal.hpp"
#include "wpi/hal/monotonic_clock.hpp"
using namespace wpi::hal; using namespace wpi::hal;
@@ -79,9 +79,9 @@ void HAL_FreeDIOPort(HAL_DigitalHandle dioPortHandle) {
smartIoHandles->Free(dioPortHandle, HAL_HandleEnum::DIO); smartIoHandles->Free(dioPortHandle, HAL_HandleEnum::DIO);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("DIO handle free timeout"); std::puts("DIO handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -12,8 +12,8 @@
#include "PortsInternal.hpp" #include "PortsInternal.hpp"
#include "SmartIo.hpp" #include "SmartIo.hpp"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp"
#include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/handles/HandlesInternal.hpp"
#include "wpi/hal/monotonic_clock.hpp"
using namespace wpi::hal; using namespace wpi::hal;
@@ -71,9 +71,9 @@ void HAL_FreeDutyCycle(HAL_DutyCycleHandle dutyCycleHandle) {
smartIoHandles->Free(dutyCycleHandle, HAL_HandleEnum::DutyCycle); smartIoHandles->Free(dutyCycleHandle, HAL_HandleEnum::DutyCycle);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("DIO handle free timeout"); std::puts("DIO handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -205,35 +205,11 @@ int32_t HAL_GetTeamNumber(void) {
return teamNumber; return teamNumber;
} }
uint64_t HAL_GetFPGATime(int32_t* status) { uint64_t HAL_GetMonotonicTime(void) {
wpi::hal::init::CheckInit(); wpi::hal::init::CheckInit();
return wpi::util::NowDefault(); return wpi::util::NowDefault();
} }
uint64_t HAL_ExpandFPGATime(uint32_t unexpandedLower, int32_t* status) {
// Capture the current FPGA time. This will give us the upper half of the
// clock.
uint64_t fpgaTime = HAL_GetFPGATime(status);
if (*status != 0) {
return 0;
}
// Now, we need to detect the case where the lower bits rolled over after we
// sampled. In that case, the upper bits will be 1 bigger than they should
// be.
// Break it into lower and upper portions.
uint32_t lower = fpgaTime & 0xffffffffull;
uint64_t upper = (fpgaTime >> 32) & 0xffffffff;
// The time was sampled *before* the current time, so roll it back.
if (lower < unexpandedLower) {
--upper;
}
return (upper << 32) + static_cast<uint64_t>(unexpandedLower);
}
HAL_Bool HAL_GetSystemActive(int32_t* status) { HAL_Bool HAL_GetSystemActive(int32_t* status) {
wpi::hal::init::CheckInit(); wpi::hal::init::CheckInit();
*status = HAL_HANDLE_ERROR; *status = HAL_HANDLE_ERROR;
@@ -296,14 +272,9 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
// WPILIB_NetworkCommunication_Reserve(nullptr); // WPILIB_NetworkCommunication_Reserve(nullptr);
int32_t status = 0;
wpi::hal::InitializeDriverStation(); wpi::hal::InitializeDriverStation();
dsStartTime = HAL_GetFPGATime(&status); dsStartTime = HAL_GetMonotonicTime();
if (status != 0) {
return false;
}
wpi::hal::WaitForInitialPacket(); wpi::hal::WaitForInitialPacket();

View File

@@ -84,8 +84,7 @@ void NotifierThread::Main() {
// Wait until next alarm // Wait until next alarm
const Alarm& alarm = m_alarmQueue.top(); const Alarm& alarm = m_alarmQueue.top();
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
if (alarm.notifier->alarmTime > curTime) { if (alarm.notifier->alarmTime > curTime) {
m_cond.wait_for( m_cond.wait_for(
lock, std::chrono::microseconds{alarm.notifier->alarmTime - curTime}); lock, std::chrono::microseconds{alarm.notifier->alarmTime - curTime});
@@ -99,8 +98,7 @@ void NotifierThread::Main() {
} }
void NotifierThread::ProcessAlarms() { void NotifierThread::ProcessAlarms() {
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
while (!m_alarmQueue.empty() && while (!m_alarmQueue.empty() &&
m_alarmQueue.top().notifier->alarmTime <= curTime) { m_alarmQueue.top().notifier->alarmTime <= curTime) {
@@ -190,7 +188,7 @@ void HAL_SetNotifierAlarm(HAL_NotifierHandle notifierHandle, uint64_t alarmTime,
} }
if (!absolute) { if (!absolute) {
alarmTime += HAL_GetFPGATime(status); alarmTime += HAL_GetMonotonicTime();
} }
uint64_t prevWakeup = UINT64_MAX; uint64_t prevWakeup = UINT64_MAX;

View File

@@ -15,8 +15,8 @@
#include "PortsInternal.hpp" #include "PortsInternal.hpp"
#include "SmartIo.hpp" #include "SmartIo.hpp"
#include "wpi/hal/Errors.h" #include "wpi/hal/Errors.h"
#include "wpi/hal/cpp/fpga_clock.hpp"
#include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/handles/HandlesInternal.hpp"
#include "wpi/hal/monotonic_clock.hpp"
using namespace wpi::hal; using namespace wpi::hal;
@@ -83,9 +83,9 @@ void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle) {
smartIoHandles->Free(pwmPortHandle, HAL_HandleEnum::PWM); smartIoHandles->Free(pwmPortHandle, HAL_HandleEnum::PWM);
// Wait for no other object to hold this handle. // Wait for no other object to hold this handle.
auto start = wpi::hal::fpga_clock::now(); auto start = wpi::hal::monotonic_clock::now();
while (port.use_count() != 1) { while (port.use_count() != 1) {
auto current = wpi::hal::fpga_clock::now(); auto current = wpi::hal::monotonic_clock::now();
if (start + std::chrono::seconds(1) < current) { if (start + std::chrono::seconds(1) < current) {
std::puts("PWM handle free timeout"); std::puts("PWM handle free timeout");
std::fflush(stdout); std::fflush(stdout);

View File

@@ -34,10 +34,7 @@ HAL_PowerDistributionHandle HAL_InitializePowerDistribution(
// Ensure we have been alive for long enough to receive a few Power packets. // Ensure we have been alive for long enough to receive a few Power packets.
do { do {
uint64_t currentTime = HAL_GetFPGATime(status); uint64_t currentTime = HAL_GetMonotonicTime();
if (*status != 0) {
return HAL_kInvalidHandle;
}
if (currentTime >= waitTime) { if (currentTime >= waitTime) {
break; break;
} }

View File

@@ -33,8 +33,7 @@ functions:
HAL_GetRuntimeType: HAL_GetRuntimeType:
HAL_GetSystemActive: HAL_GetSystemActive:
HAL_GetBrownedOut: HAL_GetBrownedOut:
HAL_GetFPGATime: HAL_GetMonotonicTime:
HAL_ExpandFPGATime:
HAL_GetRSLState: HAL_GetRSLState:
HAL_GetSystemTimeValid: HAL_GetSystemTimeValid:
HAL_Initialize: HAL_Initialize:

View File

@@ -30,7 +30,7 @@ class DriveTime(commands2.Command):
def initialize(self) -> None: def initialize(self) -> None:
"""Called when the command is initially scheduled.""" """Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp() self.startTime = wpilib.Timer.getTimestamp()
self.drive.arcadeDrive(0, 0) self.drive.arcadeDrive(0, 0)
def execute(self) -> None: def execute(self) -> None:
@@ -43,4 +43,4 @@ class DriveTime(commands2.Command):
def isFinished(self) -> bool: def isFinished(self) -> bool:
"""Returns true when the command should end""" """Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration return wpilib.Timer.getTimestamp() - self.startTime >= self.duration

View File

@@ -32,7 +32,7 @@ class TurnTime(commands2.Command):
def initialize(self) -> None: def initialize(self) -> None:
"""Called when the command is initially scheduled.""" """Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp() self.startTime = wpilib.Timer.getTimestamp()
self.drive.arcadeDrive(0, 0) self.drive.arcadeDrive(0, 0)
def execute(self) -> None: def execute(self) -> None:
@@ -45,4 +45,4 @@ class TurnTime(commands2.Command):
def isFinished(self) -> bool: def isFinished(self) -> bool:
"""Returns true when the command should end""" """Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration return wpilib.Timer.getTimestamp() - self.startTime >= self.duration

View File

@@ -30,7 +30,7 @@ class DriveTime(commands2.Command):
def initialize(self) -> None: def initialize(self) -> None:
"""Called when the command is initially scheduled.""" """Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp() self.startTime = wpilib.Timer.getTimestamp()
self.drive.arcadeDrive(0, 0) self.drive.arcadeDrive(0, 0)
def execute(self) -> None: def execute(self) -> None:
@@ -43,4 +43,4 @@ class DriveTime(commands2.Command):
def isFinished(self) -> bool: def isFinished(self) -> bool:
"""Returns true when the command should end""" """Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration return wpilib.Timer.getTimestamp() - self.startTime >= self.duration

View File

@@ -32,7 +32,7 @@ class TurnTime(commands2.Command):
def initialize(self) -> None: def initialize(self) -> None:
"""Called when the command is initially scheduled.""" """Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp() self.startTime = wpilib.Timer.getTimestamp()
self.drive.arcadeDrive(0, 0) self.drive.arcadeDrive(0, 0)
def execute(self) -> None: def execute(self) -> None:
@@ -45,4 +45,4 @@ class TurnTime(commands2.Command):
def isFinished(self) -> bool: def isFinished(self) -> bool:
"""Returns true when the command should end""" """Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration return wpilib.Timer.getTimestamp() - self.startTime >= self.duration

View File

@@ -1333,8 +1333,7 @@ void FMSSimModel::Update() {
double matchTime = HALSIM_GetDriverStationMatchTime(); double matchTime = HALSIM_GetDriverStationMatchTime();
if (!IsDSDisabled() && enabled) { if (!IsDSDisabled() && enabled) {
int32_t status = 0; double curTime = HAL_GetMonotonicTime() * 1.0e-6;
double curTime = HAL_GetFPGATime(&status) * 1.0e-6;
if (m_startMatchTime == -1.0) { if (m_startMatchTime == -1.0) {
m_startMatchTime = matchTime + curTime; m_startMatchTime = matchTime + curTime;
} }

View File

@@ -29,8 +29,7 @@ class TimingModel : public wpi::glass::Model {
} // namespace } // namespace
static void DisplayTiming() { static void DisplayTiming() {
int32_t status = 0; uint64_t curTime = HAL_GetMonotonicTime();
uint64_t curTime = HAL_GetFPGATime(&status);
if (ImGui::Button("Run")) { if (ImGui::Button("Run")) {
HALSIM_ResumeTiming(); HALSIM_ResumeTiming();
@@ -50,7 +49,7 @@ static void DisplayTiming() {
} }
ImGui::PopButtonRepeat(); ImGui::PopButtonRepeat();
ImGui::PushItemWidth(ImGui::GetFontSize() * 4); ImGui::PushItemWidth(ImGui::GetFontSize() * 4);
ImGui::LabelText("FPGA Time", "%.3f", curTime / 1000000.0); ImGui::LabelText("Time", "%.3f", curTime / 1000000.0);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
static std::vector<HALSIM_NotifierInfo> notifiers; static std::vector<HALSIM_NotifierInfo> notifiers;

View File

@@ -41,7 +41,7 @@ void TimedRobot::StartCompetition() {
break; break;
} }
m_loopStartTimeUs = RobotController::GetFPGATime(); m_loopStartTimeUs = RobotController::GetMonotonicTime();
std::chrono::microseconds currentTime{m_loopStartTimeUs}; std::chrono::microseconds currentTime{m_loopStartTimeUs};
callback.func(); callback.func();
@@ -76,7 +76,7 @@ void TimedRobot::EndCompetition() {
TimedRobot::TimedRobot(wpi::units::second_t period) TimedRobot::TimedRobot(wpi::units::second_t period)
: IterativeRobotBase(period) { : IterativeRobotBase(period) {
m_startTime = std::chrono::microseconds{RobotController::GetFPGATime()}; m_startTime = std::chrono::microseconds{RobotController::GetMonotonicTime()};
AddPeriodic([=, this] { LoopFunc(); }, period); AddPeriodic([=, this] { LoopFunc(); }, period);
int32_t status = 0; int32_t status = 0;

View File

@@ -115,7 +115,7 @@ MotorSafety& MotorSafety::operator=(MotorSafety&& rhs) {
void MotorSafety::Feed() { void MotorSafety::Feed() {
std::scoped_lock lock(m_thisMutex); std::scoped_lock lock(m_thisMutex);
m_stopTime = Timer::GetFPGATimestamp() + m_expiration; m_stopTime = Timer::GetMonotonicTimestamp() + m_expiration;
} }
void MotorSafety::SetExpiration(wpi::units::second_t expirationTime) { void MotorSafety::SetExpiration(wpi::units::second_t expirationTime) {
@@ -130,7 +130,7 @@ wpi::units::second_t MotorSafety::GetExpiration() const {
bool MotorSafety::IsAlive() const { bool MotorSafety::IsAlive() const {
std::scoped_lock lock(m_thisMutex); std::scoped_lock lock(m_thisMutex);
return !m_enabled || m_stopTime > Timer::GetFPGATimestamp(); return !m_enabled || m_stopTime > Timer::GetMonotonicTimestamp();
} }
void MotorSafety::SetSafetyEnabled(bool enabled) { void MotorSafety::SetSafetyEnabled(bool enabled) {
@@ -157,7 +157,7 @@ void MotorSafety::Check() {
return; return;
} }
if (stopTime < Timer::GetFPGATimestamp()) { if (stopTime < Timer::GetMonotonicTimestamp()) {
WPILIB_ReportError( WPILIB_ReportError(
err::Timeout, err::Timeout,
"{}... Output not updated often enough. See " "{}... Output not updated often enough. See "

View File

@@ -25,7 +25,7 @@ PeriodicOpMode::Callback::Callback(std::function<void()> func,
period{period}, period{period},
expirationTime( expirationTime(
startTime + offset + period + startTime + offset + period +
(std::chrono::microseconds{wpi::RobotController::GetFPGATime()} - (std::chrono::microseconds{wpi::RobotController::GetMonotonicTime()} -
startTime) / startTime) /
period * period) {} period * period) {}
@@ -38,7 +38,7 @@ PeriodicOpMode::~PeriodicOpMode() {
PeriodicOpMode::PeriodicOpMode(wpi::units::second_t period) PeriodicOpMode::PeriodicOpMode(wpi::units::second_t period)
: m_period{period}, : m_period{period},
m_watchdog(period, [this] { PrintLoopOverrunMessage(); }) { m_watchdog(period, [this] { PrintLoopOverrunMessage(); }) {
m_startTime = std::chrono::microseconds{RobotController::GetFPGATime()}; m_startTime = std::chrono::microseconds{RobotController::GetMonotonicTime()};
AddPeriodic([=, this] { LoopFunc(); }, period); AddPeriodic([=, this] { LoopFunc(); }, period);
int32_t status = 0; int32_t status = 0;
@@ -116,7 +116,7 @@ void PeriodicOpMode::OpModeRun(int64_t opModeId) {
break; break;
} }
m_loopStartTimeUs = RobotController::GetFPGATime(); m_loopStartTimeUs = RobotController::GetMonotonicTime();
std::chrono::microseconds currentTime{m_loopStartTimeUs}; std::chrono::microseconds currentTime{m_loopStartTimeUs};
callback.func(); callback.func();

View File

@@ -16,7 +16,7 @@
using namespace wpi; using namespace wpi;
std::function<uint64_t()> RobotController::m_timeSource = [] { std::function<uint64_t()> RobotController::m_timeSource = [] {
return RobotController::GetFPGATime(); return RobotController::GetMonotonicTime();
}; };
std::string RobotController::GetSerialNumber() { std::string RobotController::GetSerialNumber() {
@@ -47,11 +47,8 @@ uint64_t RobotController::GetTime() {
return m_timeSource(); return m_timeSource();
} }
uint64_t RobotController::GetFPGATime() { uint64_t RobotController::GetMonotonicTime() {
int32_t status = 0; return HAL_GetMonotonicTime();
uint64_t time = HAL_GetFPGATime(&status);
WPILIB_CheckErrorStatus(status, "GetFPGATime");
return time;
} }
wpi::units::volt_t RobotController::GetBatteryVoltage() { wpi::units::volt_t RobotController::GetBatteryVoltage() {

View File

@@ -16,7 +16,7 @@ void Wait(wpi::units::second_t seconds) {
std::this_thread::sleep_for(std::chrono::duration<double>(seconds.value())); std::this_thread::sleep_for(std::chrono::duration<double>(seconds.value()));
} }
wpi::units::second_t GetTime() { wpi::units::second_t GetSystemTime() {
using std::chrono::duration; using std::chrono::duration;
using std::chrono::duration_cast; using std::chrono::duration_cast;
using std::chrono::system_clock; using std::chrono::system_clock;
@@ -92,9 +92,10 @@ wpi::units::second_t Timer::GetTimestamp() {
return wpi::units::second_t{wpi::RobotController::GetTime() * 1.0e-6}; return wpi::units::second_t{wpi::RobotController::GetTime() * 1.0e-6};
} }
wpi::units::second_t Timer::GetFPGATimestamp() { wpi::units::second_t Timer::GetMonotonicTimestamp() {
// FPGA returns the timestamp in microseconds // Monotonic timestamp is in microseconds
return wpi::units::second_t{wpi::RobotController::GetFPGATime() * 1.0e-6}; return wpi::units::second_t{wpi::RobotController::GetMonotonicTime() *
1.0e-6};
} }
wpi::units::second_t Timer::GetMatchTime() { wpi::units::second_t Timer::GetMatchTime() {

View File

@@ -17,7 +17,7 @@ Tracer::Tracer() {
} }
void Tracer::ResetTimer() { void Tracer::ResetTimer() {
m_startTime = wpi::hal::fpga_clock::now(); m_startTime = wpi::hal::monotonic_clock::now();
} }
void Tracer::ClearEpochs() { void Tracer::ClearEpochs() {
@@ -26,7 +26,7 @@ void Tracer::ClearEpochs() {
} }
void Tracer::AddEpoch(std::string_view epochName) { void Tracer::AddEpoch(std::string_view epochName) {
auto currentTime = wpi::hal::fpga_clock::now(); auto currentTime = wpi::hal::monotonic_clock::now();
m_epochs[epochName] = currentTime - m_startTime; m_epochs[epochName] = currentTime - m_startTime;
m_startTime = currentTime; m_startTime = currentTime;
} }
@@ -44,7 +44,7 @@ void Tracer::PrintEpochs(wpi::util::raw_ostream& os) {
using std::chrono::duration_cast; using std::chrono::duration_cast;
using std::chrono::microseconds; using std::chrono::microseconds;
auto now = wpi::hal::fpga_clock::now(); auto now = wpi::hal::monotonic_clock::now();
if (now - m_lastEpochsPrintTime > kMinPrintPeriod) { if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
m_lastEpochsPrintTime = now; m_lastEpochsPrintTime = now;
for (const auto& epoch : m_epochs) { for (const auto& epoch : m_epochs) {

View File

@@ -87,7 +87,6 @@ void Watchdog::Impl::UpdateAlarm() {
void Watchdog::Impl::Main() { void Watchdog::Impl::Main() {
for (;;) { for (;;) {
int32_t status = 0;
HAL_NotifierHandle notifier = m_notifier.load(); HAL_NotifierHandle notifier = m_notifier.load();
if (notifier == 0) { if (notifier == 0) {
break; break;
@@ -95,7 +94,7 @@ void Watchdog::Impl::Main() {
if (WPI_WaitForObject(notifier) == 0) { if (WPI_WaitForObject(notifier) == 0) {
break; break;
} }
uint64_t curTime = HAL_GetFPGATime(&status); uint64_t curTime = HAL_GetMonotonicTime();
std::unique_lock lock(m_mutex); std::unique_lock lock(m_mutex);
@@ -163,11 +162,11 @@ Watchdog& Watchdog::operator=(Watchdog&& rhs) {
} }
wpi::units::second_t Watchdog::GetTime() const { wpi::units::second_t Watchdog::GetTime() const {
return Timer::GetFPGATimestamp() - m_startTime; return Timer::GetMonotonicTimestamp() - m_startTime;
} }
void Watchdog::SetTimeout(wpi::units::second_t timeout) { void Watchdog::SetTimeout(wpi::units::second_t timeout) {
m_startTime = Timer::GetFPGATimestamp(); m_startTime = Timer::GetMonotonicTimestamp();
m_tracer.ClearEpochs(); m_tracer.ClearEpochs();
std::scoped_lock lock(m_impl->m_mutex); std::scoped_lock lock(m_impl->m_mutex);
@@ -203,7 +202,7 @@ void Watchdog::Reset() {
} }
void Watchdog::Enable() { void Watchdog::Enable() {
m_startTime = Timer::GetFPGATimestamp(); m_startTime = Timer::GetMonotonicTimestamp();
m_tracer.ClearEpochs(); m_tracer.ClearEpochs();
std::scoped_lock lock(m_impl->m_mutex); std::scoped_lock lock(m_impl->m_mutex);

View File

@@ -64,9 +64,10 @@ class TimedRobot : public IterativeRobotBase {
/** /**
* Return the system clock time in microseconds for the start of the current * Return the system clock time in microseconds for the start of the current
* periodic loop. This is in the same time base as Timer.GetFPGATimestamp(), * periodic loop. This is in the same time base as
* but is stable through a loop. It is updated at the beginning of every * Timer.GetMonotonicTimestamp(), but is stable through a loop. It is updated
* periodic callback (including the normal periodic loop). * at the beginning of every periodic callback (including the normal periodic
* loop).
* *
* @return Robot running time in microseconds, as of the start of the current * @return Robot running time in microseconds, as of the start of the current
* periodic function. * periodic function.
@@ -107,11 +108,11 @@ class TimedRobot : public IterativeRobotBase {
std::chrono::microseconds period, std::chrono::microseconds offset) std::chrono::microseconds period, std::chrono::microseconds offset)
: func{std::move(func)}, : func{std::move(func)},
period{period}, period{period},
expirationTime( expirationTime(startTime + offset + period +
startTime + offset + period + (std::chrono::microseconds{
(std::chrono::microseconds{wpi::RobotController::GetFPGATime()} - wpi::RobotController::GetMonotonicTime()} -
startTime) / startTime) /
period * period) {} period * period) {}
bool operator>(const Callback& rhs) const { bool operator>(const Callback& rhs) const {
return expirationTime > rhs.expirationTime; return expirationTime > rhs.expirationTime;

View File

@@ -114,7 +114,7 @@ class MotorSafety {
bool m_enabled = false; bool m_enabled = false;
// The FPGA clock value when the motor has expired // The FPGA clock value when the motor has expired
wpi::units::second_t m_stopTime = Timer::GetFPGATimestamp(); wpi::units::second_t m_stopTime = Timer::GetMonotonicTimestamp();
mutable wpi::util::mutex m_thisMutex; mutable wpi::util::mutex m_thisMutex;
}; };

View File

@@ -91,9 +91,10 @@ class PeriodicOpMode : public OpMode {
/** /**
* Return the system clock time in microseconds for the start of the current * Return the system clock time in microseconds for the start of the current
* periodic loop. This is in the same time base as Timer.getFPGATimestamp(), * periodic loop. This is in the same time base as
* but is stable through a loop. It is updated at the beginning of every * Timer::GetMonotonicTimestamp(), but is stable through a loop. It is updated
* periodic callback (including the normal periodic loop). * at the beginning of every periodic callback (including the normal periodic
* loop).
* *
* @return Robot running time in microseconds, as of the start of the current * @return Robot running time in microseconds, as of the start of the current
* periodic function. * periodic function.

View File

@@ -60,22 +60,21 @@ class RobotController {
static void SetTimeSource(std::function<uint64_t()> supplier); static void SetTimeSource(std::function<uint64_t()> supplier);
/** /**
* Read the microsecond timestamp. By default, the time is based on the FPGA * Read the microsecond timestamp. By default, the time is based on the
* hardware clock in microseconds since the FPGA started. However, the return * monotonic clock. However, the return value of this method may be modified
* value of this method may be modified to use any time base, including * to use any time base, including non-monotonic and non-continuous time
* non-monotonic and non-continuous time bases. * bases.
* *
* @return The current time in microseconds. * @return The current time in microseconds.
*/ */
static uint64_t GetTime(); static uint64_t GetTime();
/** /**
* Read the microsecond-resolution timer on the FPGA. * Read the microsecond-resolution monotonic timer.
* *
* @return The current time in microseconds according to the FPGA (since FPGA * @return The current monotonic time in microseconds.
* reset).
*/ */
static uint64_t GetFPGATime(); static uint64_t GetMonotonicTime();
/** /**
* Read the battery voltage. * Read the battery voltage.

View File

@@ -25,7 +25,7 @@ void Wait(wpi::units::second_t seconds);
* @return The time, just in case you want the robot to start autonomous at 8pm * @return The time, just in case you want the robot to start autonomous at 8pm
* on Saturday. * on Saturday.
*/ */
wpi::units::second_t GetTime(); wpi::units::second_t GetSystemTime();
/** /**
* A timer class. * A timer class.
@@ -119,24 +119,22 @@ class Timer {
bool IsRunning() const; bool IsRunning() const;
/** /**
* Return the clock time in seconds. By default, the time is based on the FPGA * Return the clock time in seconds. By default, the time is the time returned
* hardware clock in seconds since the FPGA started. However, the return value * by GetMonotonicTimestamp(). However, the return value of this method may be
* of this method may be modified to use any time base, including * modified to use any time base, including non-monotonic time bases.
* non-monotonic time bases.
* *
* @returns Robot running time in seconds. * @returns Robot running time in seconds.
*/ */
static wpi::units::second_t GetTimestamp(); static wpi::units::second_t GetTimestamp();
/** /**
* Return the FPGA system clock time in seconds. * Return the monotonic clock time in seconds.
* *
* Return the time from the FPGA hardware clock in seconds since the FPGA * Return the time from the monotonic clock in seconds.
* started. Rolls over after 71 minutes.
* *
* @returns Robot running time in seconds. * @returns Monotonic time in seconds.
*/ */
static wpi::units::second_t GetFPGATimestamp(); static wpi::units::second_t GetMonotonicTimestamp();
/** /**
* Return the approximate match time. The FMS does not send an official match * Return the approximate match time. The FMS does not send an official match

View File

@@ -7,7 +7,7 @@
#include <chrono> #include <chrono>
#include <string_view> #include <string_view>
#include "wpi/hal/cpp/fpga_clock.hpp" #include "wpi/hal/monotonic_clock.hpp"
#include "wpi/util/StringMap.hpp" #include "wpi/util/StringMap.hpp"
namespace wpi::util { namespace wpi::util {
@@ -65,9 +65,9 @@ class Tracer {
private: private:
static constexpr std::chrono::milliseconds kMinPrintPeriod{1000}; static constexpr std::chrono::milliseconds kMinPrintPeriod{1000};
wpi::hal::fpga_clock::time_point m_startTime; wpi::hal::monotonic_clock::time_point m_startTime;
wpi::hal::fpga_clock::time_point m_lastEpochsPrintTime = wpi::hal::monotonic_clock::time_point m_lastEpochsPrintTime =
wpi::hal::fpga_clock::epoch(); wpi::hal::monotonic_clock::epoch();
wpi::util::StringMap<std::chrono::nanoseconds> m_epochs; wpi::util::StringMap<std::chrono::nanoseconds> m_epochs;
}; };

View File

@@ -14,7 +14,7 @@ classes:
GetTeamNumber: GetTeamNumber:
SetTimeSource: SetTimeSource:
GetTime: GetTime:
GetFPGATime: GetMonotonicTime:
IsSysActive: IsSysActive:
IsBrownedOut: IsBrownedOut:
GetRSLState: GetRSLState:

View File

@@ -1,6 +1,6 @@
functions: functions:
Wait: Wait:
GetTime: GetSystemTime:
classes: classes:
wpi::Timer: wpi::Timer:
methods: methods:
@@ -10,7 +10,7 @@ classes:
Start: Start:
Restart: Restart:
Stop: Stop:
GetFPGATimestamp: GetMonotonicTimestamp:
GetMatchTime: GetMatchTime:
HasElapsed: HasElapsed:
AdvanceIfElapsed: AdvanceIfElapsed:

View File

@@ -99,7 +99,7 @@ from ._wpilib import (
getDeployDirectory, getDeployDirectory,
getErrorMessage, getErrorMessage,
getOperatingDirectory, getOperatingDirectory,
getTime, getSystemTime,
setCurrentThreadPriority, setCurrentThreadPriority,
wait, wait,
) )
@@ -202,7 +202,7 @@ __all__ = [
"getDeployDirectory", "getDeployDirectory",
"getErrorMessage", "getErrorMessage",
"getOperatingDirectory", "getOperatingDirectory",
"getTime", "getSystemTime",
"setCurrentThreadPriority", "setCurrentThreadPriority",
"wait", "wait",
] ]

View File

@@ -112,9 +112,9 @@ TEST_F(TimerTest, AdvanceIfElapsed) {
EXPECT_FALSE(timer.AdvanceIfElapsed(400_ms)); EXPECT_FALSE(timer.AdvanceIfElapsed(400_ms));
} }
TEST_F(TimerTest, GetFPGATimestamp) { TEST_F(TimerTest, GetMonotonicTimestamp) {
auto start = wpi::Timer::GetFPGATimestamp(); auto start = wpi::Timer::GetMonotonicTimestamp();
wpi::sim::StepTiming(500_ms); wpi::sim::StepTiming(500_ms);
auto end = wpi::Timer::GetFPGATimestamp(); auto end = wpi::Timer::GetMonotonicTimestamp();
EXPECT_EQ(start + 500_ms, end); EXPECT_EQ(start + 500_ms, end);
} }

View File

@@ -44,7 +44,7 @@ public class TimedRobot extends IterativeRobotBase {
startTime startTime
+ offset + offset
+ this.period + this.period
+ (RobotController.getFPGATime() - startTime) / this.period * this.period; + (RobotController.getMonotonicTime() - startTime) / this.period * this.period;
} }
@Override @Override
@@ -89,7 +89,7 @@ public class TimedRobot extends IterativeRobotBase {
*/ */
protected TimedRobot(double period) { protected TimedRobot(double period) {
super(period); super(period);
m_startTimeUs = RobotController.getFPGATime(); m_startTimeUs = RobotController.getMonotonicTime();
addPeriodic(this::loopFunc, period); addPeriodic(this::loopFunc, period);
NotifierJNI.setNotifierName(m_notifier, "TimedRobot"); NotifierJNI.setNotifierName(m_notifier, "TimedRobot");
@@ -146,7 +146,7 @@ public class TimedRobot extends IterativeRobotBase {
break; break;
} }
long currentTime = RobotController.getFPGATime(); long currentTime = RobotController.getMonotonicTime();
m_loopStartTimeUs = currentTime; m_loopStartTimeUs = currentTime;
callback.func.run(); callback.func.run();
@@ -182,7 +182,7 @@ public class TimedRobot extends IterativeRobotBase {
/** /**
* Return the system clock time in microseconds for the start of the current periodic loop. This * Return the system clock time in microseconds for the start of the current periodic loop. This
* is in the same time base as Timer.getFPGATimestamp(), but is stable through a loop. It is * is in the same time base as Timer.getMonotonicTimestamp(), but is stable through a loop. It is
* updated at the beginning of every periodic callback (including the normal periodic loop). * updated at the beginning of every periodic callback (including the normal periodic loop).
* *
* @return Robot running time in microseconds, as of the start of the current periodic function. * @return Robot running time in microseconds, as of the start of the current periodic function.

View File

@@ -25,7 +25,7 @@ public abstract class MotorSafety {
private double m_expiration = kDefaultSafetyExpiration; private double m_expiration = kDefaultSafetyExpiration;
private boolean m_enabled; private boolean m_enabled;
private double m_stopTime = Timer.getFPGATimestamp(); private double m_stopTime = Timer.getMonotonicTimestamp();
private final Object m_thisMutex = new Object(); private final Object m_thisMutex = new Object();
private static final Set<MotorSafety> m_instanceList = new LinkedHashSet<>(); private static final Set<MotorSafety> m_instanceList = new LinkedHashSet<>();
private static final Object m_listMutex = new Object(); private static final Object m_listMutex = new Object();
@@ -83,7 +83,7 @@ public abstract class MotorSafety {
*/ */
public void feed() { public void feed() {
synchronized (m_thisMutex) { synchronized (m_thisMutex) {
m_stopTime = Timer.getFPGATimestamp() + m_expiration; m_stopTime = Timer.getMonotonicTimestamp() + m_expiration;
} }
} }
@@ -116,7 +116,7 @@ public abstract class MotorSafety {
*/ */
public boolean isAlive() { public boolean isAlive() {
synchronized (m_thisMutex) { synchronized (m_thisMutex) {
return !m_enabled || m_stopTime > Timer.getFPGATimestamp(); return !m_enabled || m_stopTime > Timer.getMonotonicTimestamp();
} }
} }
@@ -138,7 +138,7 @@ public abstract class MotorSafety {
return; return;
} }
if (stopTime < Timer.getFPGATimestamp()) { if (stopTime < Timer.getMonotonicTimestamp()) {
DriverStation.reportError( DriverStation.reportError(
getDescription() getDescription()
+ "... Output not updated often enough. See https://docs.wpilib.org/motorsafety for more information.", + "... Output not updated often enough. See https://docs.wpilib.org/motorsafety for more information.",

View File

@@ -66,7 +66,7 @@ public abstract class PeriodicOpMode implements OpMode {
startTime startTime
+ offset + offset
+ this.period + this.period
+ (RobotController.getFPGATime() - startTime) / this.period * this.period; + (RobotController.getMonotonicTime() - startTime) / this.period * this.period;
} }
@Override @Override
@@ -120,7 +120,7 @@ public abstract class PeriodicOpMode implements OpMode {
* @param period period (in seconds) for callbacks to the periodic() function * @param period period (in seconds) for callbacks to the periodic() function
*/ */
protected PeriodicOpMode(double period) { protected PeriodicOpMode(double period) {
m_startTimeUs = RobotController.getFPGATime(); m_startTimeUs = RobotController.getMonotonicTime();
m_period = period; m_period = period;
m_watchdog = new Watchdog(period, this::printLoopOverrunMessage); m_watchdog = new Watchdog(period, this::printLoopOverrunMessage);
@@ -157,8 +157,8 @@ public abstract class PeriodicOpMode implements OpMode {
/** /**
* Return the system clock time in micrseconds for the start of the current periodic loop. This is * Return the system clock time in micrseconds for the start of the current periodic loop. This is
* in the same time base as Timer.getFPGATimestamp(), but is stable through a loop. It is updated * in the same time base as Timer.getMonotonicTimestamp(), but is stable through a loop. It is
* at the beginning of every periodic callback (including the normal periodic loop). * updated at the beginning of every periodic callback (including the normal periodic loop).
* *
* @return Robot running time in microseconds, as of the start of the current periodic function. * @return Robot running time in microseconds, as of the start of the current periodic function.
*/ */
@@ -290,8 +290,8 @@ public abstract class PeriodicOpMode implements OpMode {
break; break;
} }
long currentTime = RobotController.getFPGATime(); long currentTime = RobotController.getMonotonicTime();
m_loopStartTimeUs = RobotController.getFPGATime(); m_loopStartTimeUs = RobotController.getMonotonicTime();
callback.func.run(); callback.func.run();

View File

@@ -22,7 +22,7 @@ import org.wpilib.units.measure.Voltage;
/** Contains functions for roboRIO functionality. */ /** Contains functions for roboRIO functionality. */
public final class RobotController { public final class RobotController {
private static LongSupplier m_timeSource = RobotController::getFPGATime; private static LongSupplier m_timeSource = RobotController::getMonotonicTime;
private RobotController() { private RobotController() {
throw new UnsupportedOperationException("This is a utility class!"); throw new UnsupportedOperationException("This is a utility class!");
@@ -69,9 +69,9 @@ public final class RobotController {
} }
/** /**
* Read the microsecond timestamp. By default, the time is based on the FPGA hardware clock in * Read the microsecond timestamp. By default, the time is based on the monotonic clock. However,
* microseconds since the FPGA started. However, the return value of this method may be modified * the return value of this method may be modified to use any time base, including non-monotonic
* to use any time base, including non-monotonic and non-continuous time bases. * and non-continuous time bases.
* *
* @return The current time in microseconds. * @return The current time in microseconds.
*/ */
@@ -80,9 +80,9 @@ public final class RobotController {
} }
/** /**
* Read the microsecond timestamp. By default, the time is based on the FPGA hardware clock in * Read the microsecond timestamp. By default, the time is based on the monotonic clock. However,
* microseconds since the FPGA started. However, the return value of this method may be modified * the return value of this method may be modified to use any time base, including non-monotonic
* to use any time base, including non-monotonic and non-continuous time bases. * and non-continuous time bases.
* *
* @return The current time in a measure. * @return The current time in a measure.
*/ */
@@ -91,21 +91,21 @@ public final class RobotController {
} }
/** /**
* Read the microsecond timer from the FPGA. * Read the microsecond monotonic timer.
* *
* @return The current time in microseconds according to the FPGA. * @return The current monotonic time in microseconds.
*/ */
public static long getFPGATime() { public static long getMonotonicTime() {
return HALUtil.getFPGATime(); return HALUtil.getMonotonicTime();
} }
/** /**
* Read the microsecond timer in a measure from the FPGA. * Read the microsecond monotonic timer in a measure.
* *
* @return The current time according to the FPGA in a measure. * @return The current monotonic time in a measure.
*/ */
public static Time getMeasureFPGATime() { public static Time getMeasureMonotonicTime() {
return Microseconds.of(HALUtil.getFPGATime()); return Microseconds.of(HALUtil.getMonotonicTime());
} }
/** /**

View File

@@ -17,9 +17,9 @@ import org.wpilib.units.measure.Time;
*/ */
public class Timer { public class Timer {
/** /**
* Return the clock time in seconds. By default, the time is based on the FPGA hardware clock in * Return the clock time in seconds. By default, the time is the time returned by
* seconds since the FPGA started. However, the return value of this method may be modified to use * getMonotonicTimestamp(). However, the return value of this method may be modified to use any
* any time base, including non-monotonic time bases. * time base, including non-monotonic time bases.
* *
* @return Robot running time in seconds. * @return Robot running time in seconds.
*/ */
@@ -28,13 +28,12 @@ public class Timer {
} }
/** /**
* Return the system clock time in seconds. Return the time from the FPGA hardware clock in * Return the monotonic clock time in seconds.
* seconds since the FPGA started.
* *
* @return Robot running time in seconds. * @return Monotonic time in seconds.
*/ */
public static double getFPGATimestamp() { public static double getMonotonicTimestamp() {
return RobotController.getFPGATime() / 1000000.0; return RobotController.getMonotonicTime() / 1000000.0;
} }
/** /**

View File

@@ -38,7 +38,7 @@ public class Tracer {
/** Restarts the epoch timer. */ /** Restarts the epoch timer. */
public final void resetTimer() { public final void resetTimer() {
m_startTime = RobotController.getFPGATime(); m_startTime = RobotController.getMonotonicTime();
} }
/** /**
@@ -53,7 +53,7 @@ public class Tracer {
* @param epochName The name to associate with the epoch. * @param epochName The name to associate with the epoch.
*/ */
public void addEpoch(String epochName) { public void addEpoch(String epochName) {
long currentTime = RobotController.getFPGATime(); long currentTime = RobotController.getMonotonicTime();
m_epochs.put(epochName, currentTime - m_startTime); m_epochs.put(epochName, currentTime - m_startTime);
m_startTime = currentTime; m_startTime = currentTime;
} }
@@ -71,7 +71,7 @@ public class Tracer {
* @param output the stream that the output is sent to * @param output the stream that the output is sent to
*/ */
public void printEpochs(Consumer<String> output) { public void printEpochs(Consumer<String> output) {
long now = RobotController.getFPGATime(); long now = RobotController.getMonotonicTime();
if (now - m_lastEpochsPrintTime > kMinPrintPeriod) { if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
m_lastEpochsPrintTime = now; m_lastEpochsPrintTime = now;

View File

@@ -100,7 +100,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
* @return The time in seconds since the watchdog was last fed. * @return The time in seconds since the watchdog was last fed.
*/ */
public double getTime() { public double getTime() {
return Timer.getFPGATimestamp() - m_startTime; return Timer.getMonotonicTimestamp() - m_startTime;
} }
/** /**
@@ -109,7 +109,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
* @param timeout The watchdog's timeout in seconds with microsecond resolution. * @param timeout The watchdog's timeout in seconds with microsecond resolution.
*/ */
public void setTimeout(double timeout) { public void setTimeout(double timeout) {
m_startTime = Timer.getFPGATimestamp(); m_startTime = Timer.getMonotonicTimestamp();
m_tracer.clearEpochs(); m_tracer.clearEpochs();
m_queueMutex.lock(); m_queueMutex.lock();
@@ -184,7 +184,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
/** Enables the watchdog timer. */ /** Enables the watchdog timer. */
public void enable() { public void enable() {
m_startTime = Timer.getFPGATimestamp(); m_startTime = Timer.getMonotonicTimestamp();
m_tracer.clearEpochs(); m_tracer.clearEpochs();
m_queueMutex.lock(); m_queueMutex.lock();
@@ -247,7 +247,7 @@ public class Watchdog implements Closeable, Comparable<Watchdog> {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
break; break;
} }
long curTime = HALUtil.getFPGATime(); long curTime = HALUtil.getMonotonicTime();
m_queueMutex.lock(); m_queueMutex.lock();
try { try {

View File

@@ -69,7 +69,7 @@ class LEDPatternTest {
@AfterEach @AfterEach
void tearDown() { void tearDown() {
RobotController.setTimeSource(RobotController::getFPGATime); RobotController.setTimeSource(RobotController::getMonotonicTime);
} }
@Test @Test

View File

@@ -127,10 +127,10 @@ class TimerTest {
@Test @Test
@ResourceLock("timing") @ResourceLock("timing")
void getFPGATimestampTest() { void getMonotonicTimestampTest() {
double start = Timer.getFPGATimestamp(); double start = Timer.getMonotonicTimestamp();
SimHooks.stepTiming(0.5); SimHooks.stepTiming(0.5);
double end = Timer.getFPGATimestamp(); double end = Timer.getMonotonicTimestamp();
assertEquals(start + 0.5, end, 1e-9); assertEquals(start + 0.5, end, 1e-9);
} }
} }

View File

@@ -272,9 +272,9 @@ public class PoseEstimator<T> {
* @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use * @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use
* your own time source by calling {@link * your own time source by calling {@link
* PoseEstimator#updateWithTime(double,Rotation2d,Object)} then you must use a timestamp with * PoseEstimator#updateWithTime(double,Rotation2d,Object)} then you must use a timestamp with
* an epoch since FPGA startup (i.e., the epoch of this timestamp is the same epoch as {@link * the same epoch as {@link org.wpilib.system.Timer#getMonotonicTimestamp()}.) This means that
* org.wpilib.system.Timer#getFPGATimestamp()}.) This means that you should use {@link * you should use {@link org.wpilib.system.Timer#getMonotonicTimestamp()} as your time source
* org.wpilib.system.Timer#getFPGATimestamp()} as your time source or sync the epochs. * or sync the epochs.
*/ */
public void addVisionMeasurement(Pose2d visionRobotPose, double timestamp) { public void addVisionMeasurement(Pose2d visionRobotPose, double timestamp) {
// Step 0: If this measurement is old enough to be outside the pose buffer's timespan, skip. // Step 0: If this measurement is old enough to be outside the pose buffer's timespan, skip.
@@ -344,9 +344,9 @@ public class PoseEstimator<T> {
* @param visionRobotPose The pose of the robot as measured by the vision camera. * @param visionRobotPose The pose of the robot as measured by the vision camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use * @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use
* your own time source by calling {@link #updateWithTime}, then you must use a timestamp with * your own time source by calling {@link #updateWithTime}, then you must use a timestamp with
* an epoch since FPGA startup (i.e., the epoch of this timestamp is the same epoch as {@link * the same epoch as {@link org.wpilib.system.Timer#getMonotonicTimestamp()}). This means that
* org.wpilib.system.Timer#getFPGATimestamp()}). This means that you should use {@link * you should use {@link org.wpilib.system.Timer#getMonotonicTimestamp()} as your time source
* org.wpilib.system.Timer#getFPGATimestamp()} as your time source in this case. * in this case.
* @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
* in meters, y position in meters, and heading in radians). Increase these numbers to trust * in meters, y position in meters, and heading in radians). Increase these numbers to trust
* the vision pose measurement less. * the vision pose measurement less.

View File

@@ -283,9 +283,9 @@ public class PoseEstimator3d<T> {
* @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use * @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use
* your own time source by calling {@link * your own time source by calling {@link
* PoseEstimator3d#updateWithTime(double,Rotation3d,Object)} then you must use a timestamp * PoseEstimator3d#updateWithTime(double,Rotation3d,Object)} then you must use a timestamp
* with an epoch since FPGA startup (i.e., the epoch of this timestamp is the same epoch as * with the same epoch as {@link org.wpilib.system.Timer#getMonotonicTimestamp()}.) This means
* {@link org.wpilib.system.Timer#getFPGATimestamp()}.) This means that you should use {@link * that you should use {@link org.wpilib.system.Timer#getMonotonicTimestamp()} as your time
* org.wpilib.system.Timer#getFPGATimestamp()} as your time source or sync the epochs. * source or sync the epochs.
*/ */
public void addVisionMeasurement(Pose3d visionRobotPose, double timestamp) { public void addVisionMeasurement(Pose3d visionRobotPose, double timestamp) {
// Step 0: If this measurement is old enough to be outside the pose buffer's timespan, skip. // Step 0: If this measurement is old enough to be outside the pose buffer's timespan, skip.
@@ -359,9 +359,9 @@ public class PoseEstimator3d<T> {
* @param visionRobotPose The pose of the robot as measured by the vision camera. * @param visionRobotPose The pose of the robot as measured by the vision camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use * @param timestamp The timestamp of the vision measurement in seconds. Note that if you don't use
* your own time source by calling {@link #updateWithTime}, then you must use a timestamp with * your own time source by calling {@link #updateWithTime}, then you must use a timestamp with
* an epoch since FPGA startup (i.e., the epoch of this timestamp is the same epoch as {@link * the same epoch as {@link org.wpilib.system.Timer#getMonotonicTimestamp()}). This means that
* org.wpilib.system.Timer#getFPGATimestamp()}). This means that you should use {@link * you should use {@link org.wpilib.system.Timer#getMonotonicTimestamp()} as your time source
* org.wpilib.system.Timer#getFPGATimestamp()} as your time source in this case. * in this case.
* @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement (x position
* in meters, y position in meters, z position in meters, and angle in radians). Increase * in meters, y position in meters, z position in meters, and angle in radians). Increase
* these numbers to trust the vision pose measurement less. * these numbers to trust the vision pose measurement less.

View File

@@ -280,10 +280,9 @@ class WPILIB_DLLEXPORT PoseEstimator {
* camera. * camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note * @param timestamp The timestamp of the vision measurement in seconds. Note
* that if you don't use your own time source by calling UpdateWithTime(), * that if you don't use your own time source by calling UpdateWithTime(),
* then you must use a timestamp with an epoch since FPGA startup (i.e., * then you must use a timestamp with the same epoch as
* the epoch of this timestamp is the same epoch as * wpi::Timer::GetMonotonicTimestamp(). This means that you should use
* wpi::math::Timer::GetTimestamp(). This means that you should use * wpi::Timer::GetMonotonicTimestamp() as your time source in this case.
* wpi::math::Timer::GetTimestamp() as your time source in this case.
*/ */
void AddVisionMeasurement(const Pose2d& visionRobotPose, void AddVisionMeasurement(const Pose2d& visionRobotPose,
wpi::units::second_t timestamp) { wpi::units::second_t timestamp) {
@@ -365,10 +364,9 @@ class WPILIB_DLLEXPORT PoseEstimator {
* camera. * camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note * @param timestamp The timestamp of the vision measurement in seconds. Note
* that if you don't use your own time source by calling UpdateWithTime(), * that if you don't use your own time source by calling UpdateWithTime(),
* then you must use a timestamp with an epoch since FPGA startup (i.e., * then you must use a timestamp with the same epoch as
* the epoch of this timestamp is the same epoch as * wpi::Timer::GetMonotonicTimestamp(). This means that you should use
* wpi::math::Timer::GetTimestamp(). This means that you should use * wpi::Timer::GetMonotonicTimestamp() as your time source in this case.
* wpi::math::Timer::GetTimestamp() as your time source in this case.
* @param visionMeasurementStdDevs Standard deviations of the vision pose * @param visionMeasurementStdDevs Standard deviations of the vision pose
* measurement (x position in meters, y position in meters, and heading in * measurement (x position in meters, y position in meters, and heading in
* radians). Increase these numbers to trust the vision pose measurement * radians). Increase these numbers to trust the vision pose measurement

View File

@@ -288,10 +288,9 @@ class WPILIB_DLLEXPORT PoseEstimator3d {
* camera. * camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note * @param timestamp The timestamp of the vision measurement in seconds. Note
* that if you don't use your own time source by calling UpdateWithTime(), * that if you don't use your own time source by calling UpdateWithTime(),
* then you must use a timestamp with an epoch since FPGA startup (i.e., * then you must use a timestamp with the same epoch as
* the epoch of this timestamp is the same epoch as * wpi::Timer::GetMonotonicTimestamp(). This means that you should use
* wpi::math::Timer::GetFPGATimestamp(). This means that you should use * wpi::Timer::GetMonotonicTimestamp() as your time source in this case.
* wpi::math::Timer::GetFPGATimestamp() as your time source in this case.
*/ */
void AddVisionMeasurement(const Pose3d& visionRobotPose, void AddVisionMeasurement(const Pose3d& visionRobotPose,
wpi::units::second_t timestamp) { wpi::units::second_t timestamp) {
@@ -379,10 +378,9 @@ class WPILIB_DLLEXPORT PoseEstimator3d {
* camera. * camera.
* @param timestamp The timestamp of the vision measurement in seconds. Note * @param timestamp The timestamp of the vision measurement in seconds. Note
* that if you don't use your own time source by calling UpdateWithTime(), * that if you don't use your own time source by calling UpdateWithTime(),
* then you must use a timestamp with an epoch since FPGA startup (i.e., * then you must use a timestamp with the same epoch as
* the epoch of this timestamp is the same epoch as * wpi::Timer::GetMonotonicTimestamp(). This means that you should use
* wpi::math::Timer::GetFPGATimestamp(). This means that you should use * wpi::Timer::GetMonotonicTimestamp() as your time source in this case.
* wpi::math::Timer::GetFPGATimestamp() as your time source in this case.
* @param visionMeasurementStdDevs Standard deviations of the vision pose * @param visionMeasurementStdDevs Standard deviations of the vision pose
* measurement (x position in meters, y position in meters, and heading in * measurement (x position in meters, y position in meters, and heading in
* radians). Increase these numbers to trust the vision pose measurement * radians). Increase these numbers to trust the vision pose measurement