[wpilib] Add timestamp getters with configurable time base (#7378)

This commit is contained in:
Jonah Bonner
2024-11-16 10:43:38 -05:00
committed by GitHub
parent 91142ba5fe
commit ca51197486
27 changed files with 180 additions and 53 deletions

View File

@@ -82,7 +82,7 @@ public class OnBoardIO {
return m_buttonB.get();
}
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
DriverStation.reportError("Button B was not configured", true);
m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
@@ -100,7 +100,7 @@ public class OnBoardIO {
return m_buttonC.get();
}
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
DriverStation.reportError("Button C was not configured", true);
m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
@@ -117,7 +117,7 @@ public class OnBoardIO {
if (m_greenLed != null) {
m_greenLed.set(value);
} else {
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
DriverStation.reportError("Green LED was not configured", true);
m_nextMessageTime = currentTime + MESSAGE_INTERVAL;
@@ -134,7 +134,7 @@ public class OnBoardIO {
if (m_redLed != null) {
m_redLed.set(value);
} else {
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
DriverStation.reportError("Red LED was not configured", true);
m_nextMessageTime = currentTime + MESSAGE_INTERVAL;

View File

@@ -34,7 +34,7 @@ bool OnBoardIO::GetButtonBPressed() {
return m_buttonB->Get();
}
auto currentTime = frc::Timer::GetFPGATimestamp();
auto currentTime = frc::Timer::GetTimestamp();
if (currentTime > m_nextMessageTime) {
FRC_ReportError(frc::err::Error, "{}", "Button B was not configured");
m_nextMessageTime = currentTime + kMessageInterval;
@@ -47,7 +47,7 @@ bool OnBoardIO::GetButtonCPressed() {
return m_buttonC->Get();
}
auto currentTime = frc::Timer::GetFPGATimestamp();
auto currentTime = frc::Timer::GetTimestamp();
if (currentTime > m_nextMessageTime) {
FRC_ReportError(frc::err::Error, "{}", "Button C was not configured");
m_nextMessageTime = currentTime + kMessageInterval;
@@ -59,7 +59,7 @@ void OnBoardIO::SetGreenLed(bool value) {
if (m_greenLed) {
m_greenLed->Set(value);
} else {
auto currentTime = frc::Timer::GetFPGATimestamp();
auto currentTime = frc::Timer::GetTimestamp();
if (currentTime > m_nextMessageTime) {
FRC_ReportError(frc::err::Error, "{}", "Green LED was not configured");
m_nextMessageTime = currentTime + kMessageInterval;
@@ -71,7 +71,7 @@ void OnBoardIO::SetRedLed(bool value) {
if (m_redLed) {
m_redLed->Set(value);
} else {
auto currentTime = frc::Timer::GetFPGATimestamp();
auto currentTime = frc::Timer::GetTimestamp();
if (currentTime > m_nextMessageTime) {
FRC_ReportError(frc::err::Error, "{}", "Red LED was not configured");
m_nextMessageTime = currentTime + kMessageInterval;

View File

@@ -149,7 +149,7 @@ void Alert::Set(bool active) {
}
if (active) {
m_activeStartTime = frc::RobotController::GetFPGATime();
m_activeStartTime = frc::RobotController::GetTime();
m_activeAlerts->emplace(m_activeStartTime, m_text);
} else {
m_activeAlerts->erase({m_activeStartTime, m_text});

View File

@@ -678,7 +678,7 @@ void DriverStation::StartDataLog(wpi::log::DataLog& log, bool logJoysticks) {
void ReportJoystickUnpluggedErrorV(fmt::string_view format,
fmt::format_args args) {
auto& inst = GetInstance();
auto currentTime = Timer::GetFPGATimestamp();
auto currentTime = Timer::GetTimestamp();
if (currentTime > inst.nextMessageTime) {
ReportErrorV(err::Error, "", 0, "", format, args);
inst.nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;
@@ -689,7 +689,7 @@ void ReportJoystickUnpluggedWarningV(fmt::string_view format,
fmt::format_args args) {
auto& inst = GetInstance();
if (DriverStation::IsFMSAttached() || !inst.silenceJoystickWarning) {
auto currentTime = Timer::GetFPGATimestamp();
auto currentTime = Timer::GetTimestamp();
if (currentTime > inst.nextMessageTime) {
ReportErrorV(warn::Warning, "", 0, "", format, args);
inst.nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;

View File

@@ -4,6 +4,7 @@
#include "frc/RobotController.h"
#include <functional>
#include <string>
#include <hal/CAN.h>
@@ -15,6 +16,10 @@
using namespace frc;
std::function<uint64_t()> RobotController::m_timeSource = [] {
return RobotController::GetFPGATime();
};
int RobotController::GetFPGAVersion() {
int32_t status = 0;
int version = HAL_GetFPGAVersion(&status);
@@ -49,6 +54,14 @@ int32_t RobotController::GetTeamNumber() {
return HAL_GetTeamNumber();
}
void RobotController::SetTimeSource(std::function<uint64_t()> supplier) {
m_timeSource = supplier;
}
uint64_t RobotController::GetTime() {
return m_timeSource();
}
uint64_t RobotController::GetFPGATime() {
int32_t status = 0;
uint64_t time = HAL_GetFPGATime(&status);

View File

@@ -46,6 +46,8 @@ void TimedRobot::StartCompetition() {
break;
}
m_loopStartTimeUs = RobotController::GetFPGATime();
callback.func();
// Increment the expiration time by the number of full periods it's behind
@@ -97,6 +99,10 @@ TimedRobot::~TimedRobot() {
}
}
uint64_t TimedRobot::GetLoopStartTime() {
return m_loopStartTimeUs;
}
void TimedRobot::AddPeriodic(std::function<void()> callback,
units::second_t period, units::second_t offset) {
m_callbacks.emplace(

View File

@@ -36,7 +36,7 @@ Timer::Timer() {
units::second_t Timer::Get() const {
if (m_running) {
return (GetFPGATimestamp() - m_startTime) + m_accumulatedTime;
return (GetTimestamp() - m_startTime) + m_accumulatedTime;
} else {
return m_accumulatedTime;
}
@@ -44,12 +44,12 @@ units::second_t Timer::Get() const {
void Timer::Reset() {
m_accumulatedTime = 0_s;
m_startTime = GetFPGATimestamp();
m_startTime = GetTimestamp();
}
void Timer::Start() {
if (!m_running) {
m_startTime = GetFPGATimestamp();
m_startTime = GetTimestamp();
m_running = true;
}
}
@@ -88,6 +88,10 @@ bool Timer::IsRunning() const {
return m_running;
}
units::second_t Timer::GetTimestamp() {
return units::second_t{frc::RobotController::GetTime() * 1.0e-6};
}
units::second_t Timer::GetFPGATimestamp() {
// FPGA returns the timestamp in microseconds
return units::second_t{frc::RobotController::GetFPGATime() * 1.0e-6};

View File

@@ -6,6 +6,7 @@
#include <stdint.h>
#include <functional>
#include <string>
#include <units/temperature.h>
@@ -78,6 +79,24 @@ class RobotController {
*/
static int32_t GetTeamNumber();
/**
* Sets a new source to provide the clock time in microseconds. Changing this
* affects the return value of {@code GetTime}.
*
* @param supplier Function to return the time in microseconds.
*/
static void SetTimeSource(std::function<uint64_t()> supplier);
/**
* Read the microsecond timestamp. By default, the time is based on the FPGA
* hardware clock in microseconds since the FPGA started. However, the return
* value of this method may be modified to use any time base, including
* non-monotonic and non-continuous time bases.
*
* @return The current time in microseconds.
*/
static uint64_t GetTime();
/**
* Read the microsecond-resolution timer on the FPGA.
*
@@ -320,6 +339,9 @@ class RobotController {
* @return The status of the CAN bus
*/
static CANStatus GetCANStatus();
private:
static std::function<uint64_t()> m_timeSource;
};
} // namespace frc

View File

@@ -56,6 +56,17 @@ class TimedRobot : public IterativeRobotBase {
~TimedRobot() override;
/**
* 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 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.
*/
uint64_t GetLoopStartTime();
/**
* Add a callback to run at a specific period with a starting time offset.
*
@@ -103,6 +114,7 @@ class TimedRobot : public IterativeRobotBase {
hal::Handle<HAL_NotifierHandle, HAL_CleanNotifier> m_notifier;
std::chrono::microseconds m_startTime;
uint64_t m_loopStartTimeUs = 0;
wpi::priority_queue<Callback, std::vector<Callback>, std::greater<Callback>>
m_callbacks;

View File

@@ -118,6 +118,16 @@ class Timer {
*/
bool IsRunning() const;
/**
* Return the clock time in seconds. By default, the time is based on the FPGA
* hardware clock in seconds since the FPGA started. However, the return value
* of this method may be modified to use any time base, including
* non-monotonic time bases.
*
* @returns Robot running time in seconds.
*/
static units::second_t GetTimestamp();
/**
* Return the FPGA system clock time in seconds.
*

View File

@@ -106,7 +106,7 @@ void Drivetrain::UpdateOdometry() {
// latency delay -- on a real robot, this must be calculated based either on
// known latency or timestamps.
m_poseEstimator.AddVisionMeasurement(visionMeasurement2d,
frc::Timer::GetFPGATimestamp());
frc::Timer::GetTimestamp());
}
void Drivetrain::SimulationPeriodic() {

View File

@@ -67,5 +67,5 @@ void Drivetrain::UpdateOdometry() {
m_poseEstimator.AddVisionMeasurement(
ExampleGlobalMeasurementSensor::GetEstimatedGlobalPose(
m_poseEstimator.GetEstimatedPosition()),
frc::Timer::GetFPGATimestamp() - 0.3_s);
frc::Timer::GetTimestamp() - 0.3_s);
}

View File

@@ -41,5 +41,5 @@ void Drivetrain::UpdateOdometry() {
m_poseEstimator.AddVisionMeasurement(
ExampleGlobalMeasurementSensor::GetEstimatedGlobalPose(
m_poseEstimator.GetEstimatedPosition()),
frc::Timer::GetFPGATimestamp() - 0.3_s);
frc::Timer::GetTimestamp() - 0.3_s);
}

View File

@@ -113,7 +113,7 @@ public class Alert implements AutoCloseable {
}
if (active) {
m_activeStartTime = RobotController.getFPGATime();
m_activeStartTime = RobotController.getTime();
m_activeAlerts.add(new PublishedAlert(m_activeStartTime, m_text));
} else {
m_activeAlerts.remove(new PublishedAlert(m_activeStartTime, m_text));

View File

@@ -1328,7 +1328,7 @@ public final class DriverStation {
* the DS.
*/
private static void reportJoystickUnpluggedError(String message) {
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
reportError(message, false);
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
@@ -1341,7 +1341,7 @@ public final class DriverStation {
*/
private static void reportJoystickUnpluggedWarning(String message) {
if (isFMSAttached() || !m_silenceJoystickWarning) {
double currentTime = Timer.getFPGATimestamp();
double currentTime = Timer.getTimestamp();
if (currentTime > m_nextMessageTime) {
reportWarning(message, false);
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;

View File

@@ -191,7 +191,7 @@ public interface LEDPattern {
return (reader, writer) -> {
int bufLen = reader.getLength();
long now = WPIUtilJNI.now();
long now = RobotController.getTime();
// index should move by (buf.length) / (period)
double t = (now % (long) periodMicros) / periodMicros;
@@ -242,7 +242,7 @@ public interface LEDPattern {
return (reader, writer) -> {
int bufLen = reader.getLength();
long now = WPIUtilJNI.now();
long now = RobotController.getTime();
// every step in time that's a multiple of microsPerLED will increment the offset by 1
var offset = now / microsPerLED;
@@ -271,7 +271,7 @@ public interface LEDPattern {
final long onTimeMicros = (long) onTime.in(Microseconds);
return (reader, writer) -> {
if (WPIUtilJNI.now() % totalTimeMicros < onTimeMicros) {
if (RobotController.getTime() % totalTimeMicros < onTimeMicros) {
applyTo(reader, writer);
} else {
kOff.applyTo(reader, writer);
@@ -323,7 +323,7 @@ public interface LEDPattern {
reader,
(i, r, g, b) -> {
// How far we are in the cycle, in the range [0, 1)
double t = (WPIUtilJNI.now() % periodMicros) / (double) periodMicros;
double t = (RobotController.getTime() % periodMicros) / (double) periodMicros;
double phase = t * 2 * Math.PI;
// Apply the cosine function and shift its output from [-1, 1] to [0, 1]

View File

@@ -118,7 +118,7 @@ public abstract class RobotBase implements AutoCloseable {
@Override
public double getTimestamp() {
return WPIUtilJNI.now() * 1.0e-6;
return Timer.getTimestamp();
}
});
}

View File

@@ -19,9 +19,12 @@ import edu.wpi.first.units.measure.Current;
import edu.wpi.first.units.measure.Temperature;
import edu.wpi.first.units.measure.Time;
import edu.wpi.first.units.measure.Voltage;
import java.util.function.LongSupplier;
/** Contains functions for roboRIO functionality. */
public final class RobotController {
private static LongSupplier m_timeSource = RobotController::getFPGATime;
private RobotController() {
throw new UnsupportedOperationException("This is a utility class!");
}
@@ -76,6 +79,38 @@ public final class RobotController {
return HALUtil.getTeamNumber();
}
/**
* Sets a new source to provide the clock time in microseconds. Changing this affects the return
* value of {@code getTime} in Java.
*
* @param supplier Function to return the time in microseconds.
*/
public static void setTimeSource(LongSupplier supplier) {
m_timeSource = supplier;
}
/**
* Read the microsecond timestamp. By default, the time is based on the FPGA hardware clock in
* microseconds since the FPGA started. However, the return value of this method may be modified
* to use any time base, including non-monotonic and non-continuous time bases.
*
* @return The current time in microseconds.
*/
public static long getTime() {
return m_timeSource.getAsLong();
}
/**
* Read the microsecond timestamp. By default, the time is based on the FPGA hardware clock in
* microseconds since the FPGA started. However, the return value of this method may be modified
* to use any time base, including non-monotonic and non-continuous time bases.
*
* @return The current time in a measure.
*/
public static Time getMeasureTime() {
return Microseconds.of(m_timeSource.getAsLong());
}
/**
* Read the microsecond timer from the FPGA.
*

View File

@@ -73,6 +73,7 @@ public class TimedRobot extends IterativeRobotBase {
private final int m_notifier = NotifierJNI.initializeNotifier();
private long m_startTimeUs;
private long m_loopStartTimeUs;
private final PriorityQueue<Callback> m_callbacks = new PriorityQueue<>();
@@ -128,6 +129,8 @@ public class TimedRobot extends IterativeRobotBase {
break;
}
m_loopStartTimeUs = RobotController.getFPGATime();
callback.func.run();
// Increment the expiration time by the number of full periods it's behind
@@ -159,6 +162,17 @@ public class TimedRobot extends IterativeRobotBase {
NotifierJNI.stopNotifier(m_notifier);
}
/**
* 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
* 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.
*/
public long getLoopStartTime() {
return m_loopStartTimeUs;
}
/**
* Add a callback to run at a specific period.
*

View File

@@ -11,6 +11,17 @@ package edu.wpi.first.wpilibj;
* get() won't return a negative duration.
*/
public class Timer {
/**
* Return the clock time in seconds. By default, the time is based on the FPGA hardware clock in
* seconds since the FPGA started. However, the return value of this method may be modified to use
* any time base, including non-monotonic time bases.
*
* @return Robot running time in seconds.
*/
public static double getTimestamp() {
return RobotController.getTime() / 1000000.0;
}
/**
* Return the system clock time in seconds. Return the time from the FPGA hardware clock in
* seconds since the FPGA started.
@@ -60,7 +71,7 @@ public class Timer {
}
private double getMsClock() {
return RobotController.getFPGATime() / 1000.0;
return RobotController.getTime() / 1000.0;
}
/**

View File

@@ -43,10 +43,10 @@ import edu.wpi.first.networktables.StringSubscriber;
import edu.wpi.first.networktables.StringTopic;
import edu.wpi.first.networktables.Subscriber;
import edu.wpi.first.networktables.Topic;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.util.function.BooleanConsumer;
import edu.wpi.first.util.function.FloatConsumer;
import edu.wpi.first.util.function.FloatSupplier;
import edu.wpi.first.wpilibj.RobotController;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
@@ -180,7 +180,7 @@ public class SendableBuilderImpl implements NTSendableBuilder {
/** Update the network table values by calling the getters for all properties. */
@Override
public void update() {
long time = WPIUtilJNI.now();
long time = RobotController.getTime();
for (Property<?, ?> property : m_properties) {
property.update(m_controllable, time);
}

View File

@@ -25,7 +25,6 @@ import static edu.wpi.first.wpilibj.util.Color.kYellow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.wpilibj.util.Color;
import edu.wpi.first.wpilibj.util.Color8Bit;
import java.util.Map;
@@ -36,6 +35,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class LEDPatternTest {
long m_mockTime;
// Applies a pattern of White, Yellow, Purple to LED triplets
LEDPattern m_whiteYellowPurple =
(reader, writer) -> {
@@ -59,14 +60,13 @@ class LEDPatternTest {
@BeforeEach
void setUp() {
WPIUtilJNI.enableMockTime();
WPIUtilJNI.setMockTime(0L);
m_mockTime = 0;
RobotController.setTimeSource(() -> m_mockTime);
}
@AfterEach
void tearDown() {
WPIUtilJNI.setMockTime(0L);
WPIUtilJNI.disableMockTime();
RobotController.setTimeSource(RobotController::getFPGATime);
}
@Test
@@ -220,7 +220,7 @@ class LEDPatternTest {
var scroll = base.scrollAtRelativeSpeed(Value.per(Microsecond).of(1 / 256.0));
for (int time = 0; time < 500; time++) {
WPIUtilJNI.setMockTime(time);
m_mockTime = time;
scroll.applyTo(buffer);
for (int led = 0; led < buffer.getLength(); led++) {
@@ -254,7 +254,7 @@ class LEDPatternTest {
var scroll = base.scrollAtRelativeSpeed(Value.per(Microsecond).of(-1 / 256.0));
for (int time = 0; time < 500; time++) {
WPIUtilJNI.setMockTime(time);
m_mockTime = time;
scroll.applyTo(buffer);
for (int led = 0; led < buffer.getLength(); led++) {
@@ -290,7 +290,7 @@ class LEDPatternTest {
var scroll = base.scrollAtAbsoluteSpeed(MetersPerSecond.of(16), Centimeters.of(2));
for (int time = 0; time < 500; time++) {
WPIUtilJNI.setMockTime(time * 1_250); // 1.25ms per LED
m_mockTime = time * 1_250; // 1.25ms per LED
scroll.applyTo(buffer);
for (int led = 0; led < buffer.getLength(); led++) {
@@ -326,7 +326,7 @@ class LEDPatternTest {
var scroll = base.scrollAtAbsoluteSpeed(MetersPerSecond.of(-16), Centimeters.of(2));
for (int time = 0; time < 500; time++) {
WPIUtilJNI.setMockTime(time * 1_250); // 1.25ms per LED
m_mockTime = time * 1_250; // 1.25ms per LED
scroll.applyTo(buffer);
for (int led = 0; led < buffer.getLength(); led++) {
@@ -539,7 +539,7 @@ class LEDPatternTest {
var buffer = new AddressableLEDBuffer(1);
for (int t = 0; t < 8; t++) {
WPIUtilJNI.setMockTime(t * 1_000_000L); // time travel 1 second
m_mockTime = t * 1_000_000L; // time travel 1 second
pattern.applyTo(buffer);
Color color = buffer.getLED(0);
@@ -571,7 +571,7 @@ class LEDPatternTest {
var buffer = new AddressableLEDBuffer(1);
for (int t = 0; t < 8; t++) {
WPIUtilJNI.setMockTime(t * 1_000_000L); // time travel 1 second
m_mockTime = t * 1_000_000L; // time travel 1 second
pattern.applyTo(buffer);
Color color = buffer.getLED(0);
@@ -623,31 +623,31 @@ class LEDPatternTest {
var buffer = new AddressableLEDBuffer(1);
{
WPIUtilJNI.setMockTime(0); // start
m_mockTime = 0; // start
pattern.applyTo(buffer);
assertColorEquals(kWhite, buffer.getLED(0));
}
{
WPIUtilJNI.setMockTime(1); // midway (down)
m_mockTime = 1; // midway (down)
pattern.applyTo(buffer);
assertColorEquals(midGray, buffer.getLED(0));
}
{
WPIUtilJNI.setMockTime(2); // bottom
m_mockTime = 2; // bottom
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
}
{
WPIUtilJNI.setMockTime(3); // midway (up)
m_mockTime = 3; // midway (up)
pattern.applyTo(buffer);
assertColorEquals(midGray, buffer.getLED(0));
}
{
WPIUtilJNI.setMockTime(4); // back to start
m_mockTime = 4; // back to start
pattern.applyTo(buffer);
assertColorEquals(kWhite, buffer.getLED(0));
}

View File

@@ -245,7 +245,7 @@ public class Drivetrain {
// Apply vision measurements. For simulation purposes only, we don't input a latency delay -- on
// a real robot, this must be calculated based either on known latency or timestamps.
m_poseEstimator.addVisionMeasurement(visionMeasurement2d, Timer.getFPGATimestamp());
m_poseEstimator.addVisionMeasurement(visionMeasurement2d, Timer.getTimestamp());
}
/** This function is called periodically during simulation. */

View File

@@ -167,6 +167,6 @@ public class Drivetrain {
m_poseEstimator.addVisionMeasurement(
ExampleGlobalMeasurementSensor.getEstimatedGlobalPose(
m_poseEstimator.getEstimatedPosition()),
Timer.getFPGATimestamp() - 0.3);
Timer.getTimestamp() - 0.3);
}
}

View File

@@ -99,6 +99,6 @@ public class Drivetrain {
m_poseEstimator.addVisionMeasurement(
ExampleGlobalMeasurementSensor.getEstimatedGlobalPose(
m_poseEstimator.getEstimatedPosition()),
Timer.getFPGATimestamp() - 0.3);
Timer.getTimestamp() - 0.3);
}
}

View File

@@ -23,11 +23,11 @@ public class TimerTest extends AbstractComsSetup {
@Test
public void delayTest() {
// Given
long startTime = RobotController.getFPGATime();
long startTime = RobotController.getTime();
// When
Timer.delay(TIMER_RUNTIME / 1000000);
long endTime = RobotController.getFPGATime();
long endTime = RobotController.getTime();
long difference = endTime - startTime;
// Then

View File

@@ -228,8 +228,8 @@ class WPILIB_DLLEXPORT PoseEstimator {
* 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.,
* the epoch of this timestamp is the same epoch as
* frc::Timer::GetFPGATimestamp(). This means that you should use
* frc::Timer::GetFPGATimestamp() as your time source in this case.
* frc::Timer::GetTimestamp(). This means that you should use
* frc::Timer::GetTimestamp() as your time source in this case.
*/
void AddVisionMeasurement(const Pose2d& visionRobotPose,
units::second_t timestamp) {
@@ -311,8 +311,8 @@ class WPILIB_DLLEXPORT PoseEstimator {
* 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.,
* the epoch of this timestamp is the same epoch as
* frc::Timer::GetFPGATimestamp(). This means that you should use
* frc::Timer::GetFPGATimestamp() as your time source in this case.
* frc::Timer::GetTimestamp(). This means that you should use
* frc::Timer::GetTimestamp() as your time source in this case.
* @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 the vision pose measurement