Squashed 'third_party/allwpilib/' changes from 83f1860047..f1a82828fe

f1a82828fe [wpiutil] Add DataLog and DataLogManager Stop() (#5860)
2a04e12c6f [apriltag] AprilTagFieldLayout: Add accessors for origin and field dimensions (#5869)
33e0089afb Cleanup usages of std::function<void(void)> (#5864)
d06fa633d5 [build] Fix protobuf generation when building with make (#5867)
049732afb8 [cscore] Make camera connection logging clearer (#5866)
87f7c19f90 [wpimath] Make InterpolatingDoubleTreeMap constructor public (#5865)
6b53ef47cf [wpimath] Don't recreate TrapezoidProfile in ProfiledPIDController calculate() (#5863)
8a3a268ae6 [commands] Add finallyDo with zero-arg lambda (#5862)
1c35d42cd0 [wpilib] Pop diagnostic for deprecated function use (#5859)
ddc8db6c26 [wpimath] Add feedforward constant constructor to ElevatorSim (#5823)
c6aff2c431 [upstream_utils] Update to LLVM 17.0.4 (#5855)
a9c5b18a39 [build] Update OpenCV to 2024-4.8.0-2 (#5854)
9540b6922d [hal] Add CAN IDs for AndyMark and Vivid Hosting (#5852)
83a7d33c47 [glass] Improve display of protobuf/struct type strings (#5850)
a4a8ad9c75 [commands] Make Java SelectCommand generic (#5849)
9eecf2a456 [build] Add CMake option to build Java sources jars (#5768)
9536a311cb [wpilib] Add support for the PS5 DualSense controller (#5257)
8d5e6737fc [wpilibc] SolenoidSim: Add virtual destructor (#5848)
07e13d60a2 [ntcore] Fix write_impl (#5847)
1713386869 [wpiutil] ProtobufMessageDatabase: Fix out-of-order Add() rebuild (#5845)
35472f5fc9 [ntcore] Fix a use-after-free in client close (#5844)
ed168b522c [ntcore] Disable buf pool when asan is enabled (#5843)
3e7ba2cc6f [wpinet] WebSocket: Fix write behavior (#5841)
80c47da237 [sim] Disable the robot program when DS disconnects (#5818)
abe1cec90c [wpilib] Update Usage Reporting ResourceType from NI Libraries (#5842)
cdf981abba [glass] Fix position of data type in NT view (#5840)
04dcd80adb [build] Publish unit tests for examples (#5838)
49920234ac [build] Fix checkstyle rules to allow Windows paths (#5839)
366b715942 [wpilib] Fix SendableChooser test (#5835)
3ba501f947 [commands] Java: Fix CommandXboxController.leftTrigger() parameter order (#5831)
ec569a58ef [wpimath] Make KalmanTypeFilter interface public (#5830)
b91317fd36 [wpiutil] DataLog.addSchema(): Don't add into a set view (#5829)
2ab4fcbc24 [wpiutil] ProtobufMessageDatabase: Clear messages first (#5827)
98c14f1692 [wpimath] Add EKF/UKF u-y-R correct overload (#5832)
60bcdeded9 [ci] Disable java in sanitizer builds (#5833)
c87f8fd538 [commands] Add DeferredCommand (#5566)
ad80eb3a0b [ci] Update actions for comment-command (#5824)
c7d6ad5a0b [ntcore] WebSocketConnection: Use weak capture (#5822)
8a8e220792 [simgui] Add 'Invalid' option for AllianceStation (#5820)
cfc6a47f76 [sim] DS plugin: Fix off-by-one error when setting alliance station (#5819)
8efa586ace [ntcore] Don't check type string on publishing an entry (#5816)
23ea188e60 [glass] Add protobuf decode error log message (#5812)
928e87b4f4 [build] Add combined test meta-task (#5813)
63ef585d4b [wpiutil] Fix compilation of MathExtras.h on Windows with /sdl (#5809)
b03a7668f9 [build] Windows CMake/vcpkg fixes (#5807)
3f08bcde54 [hal] Fix HAL AllianceStation on rio (#5811)
196d963dc4 [ntcore] Fix off-by-one error in stream write (#5810)
f4cbcbc984 Fix typos (NFC) (#5804)
ec0f7fefb0 [myrobot] Update the myRobot JRE (#5805)
3d618bdbfd [wpiutil] Fix Java struct array unpacking (#5801)
1fa7445667 [ntcore] Check for valid client in incoming text and binary (#5799)
269b9647da [ci] Update JDK for combine step (#5794)
bee32f080e [docs] Add wpiunits to JavaDocs (#5793)
25dad5a531 [wpinet] TCPConnector_parallel: Don't use thread_local (#5791)
4a93581f1a [build] cmake: use default library type for libglassnt, libglass, wpigui, and imgui (#5797)
abb2857e03 [wpilib] Counter: Fix default distance per pulse, add distance and rate to C++ (#5796)
b14a61e1c0 [readme] Add link to QuickBuffers release page (#5795)
cf54d9ccb7 [wpiutil, ntcore] Add structured data support (#5391)
ecb7cfa9ef [wpimath] Add Exponential motion profile (#5720)
7c6fe56cf2 [ntcore] Fix crash on disconnect (#5788)
85147bf69e [wpinet] WebSocketSerializer: Fix UB (#5787)
244163acad [wpinet] uv::Stream::TryWrite(): Return 0 on EAGAIN (#5784)
820728503d [hal] Remove extra semicolon in RoboRioData (#5786)
45f307d87e [upstream_utils] Upgrade to LLVM 17.0.3 (#5785)
4ce4d63efc [wpilibj] Fix RobotBase.isSimulation() (#5783)
579007ceb3 [commands] Add requirements parameter to Commands.idle() (#5774)
3f3a169149 [wpilib] Make physics sim setState() functions public (#5779)
7501e4ac88 [wpilib] Close sim device in ADIS IMUs (#5776)
99630d2e78 [wpimath] Upgrade to EJML 0.43.1 (#5778)
02cbbc997d [wpimath] Make Vector-Vector binary operators return Vector (#5772)
ed93889e17 [examples] Fix typo in TimesliceRobot example name (#5773)
da70e4c262 [docs] Add jinja2 to CMake prerequisites (#5771)
e814595ea7 [wpimath] Add ChassisSpeeds.fromRobotRelativeSpeeds() (#5744)
f98c943445 [wpimath] LinearSystemId: Add DCMotorSystem overload (#5770)
b3eb64b0f7 [wpiutil] ct_string: Use inline namespace for literals (#5767)
7d9ba256c2 Revert "[build] Add CMake option to build Java source jars (#5756)" (#5766)
1f6492e3d8 [sysid] Update JSON library usage (#5765)
638f04f626 [wpiutil] Add protobuf to thirdparty sources (#5746)
210255bfff [wpiutil] Update json to 3.11.2 (#5680)
896772c750 [wpimath] Add DCMotor functions for Kraken X60 and Neo Vortex (#5759)
fd427f6c82 [wpimath] Fix hardcoded module count in SwerveDriveKinematics.resetHeading() (#5762)
c0b4c6cce6 [wpimath] Add overloads for Transform2d and Transform3d (#5757)
9a0aafd8ab [examples] Make swerve examples multiply desired module speeds by cosine of heading error (#5758)
1c724884ca [build] Add CMake option to build Java source jars (#5756)
5b0db6b93e [ci] Forward CI as well (#5755)
f8cbbbac12 [ci] Take 2 on passing GITHUB_REF (#5754)
b9944be09c [ci] Pass GITHUB_REF to docker container (#5753)
de5e4eda6c [build] Update apriltag, libssh, googletest for 2024 (#5752)
227e660e20 [upstream_utils] Upgrade to LLVM 17.0.2 (#5750)
36f94c9f21 [commands,romi,xrp] Add frcYear to vendordep (#5747)
741d166457 [glass] NT view: enhance array support (#5732)
1d23513945 [ntcore] Fix string array value comparison (#5745)
ff1849052e [commands] Make command scheduling order consistent (#5470)
58e8474368 [build] Disable armsimulation unit test (#5739)
fb07b0da49 [examples] Add XRP C++ Examples and Templates (#5743)
81893ad73d Run wpiformat with clang-format 17 (#5740)
faa1e665ba [wpimath] Add ElevatorFeedforward.calculate(currentV, nextV) overload (#5715)
a789632052 [build] Update to native utils 2024.3.1 (#5738)
8f60ab5182 [build] Update OpenCV to 2024-4.8.0-1 (#5737)
33243f982b [wpimath] Expand Quaternion class with additional operators (#5600)
420f2f7c80 [ntcore] Add RTT-only subprotocol (#5731)
2b63e35ded [ntcore] Fix moving outgoing queue to new period (#5735)
be939cb636 [ntcore] Fix notification of SetDefaultEntryValue (#5733)
69a54de202 [build] Update enterprise plugin (#5730)
fef03a3ff5 [commands] Clean up C++ includes after Requirements was added (#5719)
8b7c6852cf [ntcore] Networking improvements (#5659)
1d19e09ca9 [wpiutil] Set WPI_{UN}IGNORE_DEPRECATED to empty when all else fails (#5728)
58141d6eb5 [wpilib] Make BooleanEvent more consistent (#5436)
6576d9b474 [wpilib] SendableChooser: implement Sendable instead of NTSendable (#5718)
a4030c670f [build] Update to gradle 8.4, enable win arm builds (#5727)
0960f11eba [wpinet] Revert removal of uv_clock_gettime() (#5723)
cb1bd0a3be [wpiutil] Get more precise system time on Windows (#5722)
4831277ffe [wpigui] Fix loading a maximized window on second monitor (#5721)
3eb372c25a [wpiutil] SendableBuilder: Add PublishConst methods (#5158)
1fec8596a4 [ci] Fix -dirty version (#5716)
f7e47d03f3 [build] Remove unnecessary CMake config installs (#5714)
a331ed2374 [sysid] Add SysId (#5672)
8d2cbfce16 [wpiutil] DataLog: Stop logging if insufficient free space (#5699)
48facb9cef [ntcoreffi] Add DataLogManager (#5702)
aecbcb08fc [ntcore] Correctly start DataLog for existing publishers (#5703)
5e295dfbda [wpiutil] DataLog: Limit total buffer allocation (#5700)
c7c7e05d9d [ci] Unbreak combiner (#5698)
c92bad52cb [wpilib] DataLogManager: Use system time valid function (#5697)
d404af5f24 [wpilib] RobotController: Add isSystemTimeValid() (#5696)
e56f1a3632 [ci] Run combine but skip all steps (#5695)
8f5bcad244 [ci] Use sccache for cmake builds (#5692)
703dedc4a6 [ci] Upgrade get-cmake action to fix node12 deprecation warning (#5694)
c69a0d7504 [ci] Don't run example unit test that segfaults (#5693)
66358d103e Add menu items for online docs to GUI tools (#5689)
4be8384a76 [ci] Disable combine on PR builds (#5691)
90288f06a6 [ci] Fix Gradle disk space issues (#5688)
9e9583412e [wpigui] Make wpi::gui::OpenURL() fork the process first (#5687)
d4fcd80b7b [ci] Gradle: Use container only for build step (#5684)
7b70e66772 [outlineviewer] Fix thirdparty library include sorting (#5683)
5f651df5d5 [build] Clean up Gradle configs (#5685)
65b26738d5 Add CMakeSettings.json to gitignore (#5682)
d0305951ad Fix GitHub inline warnings (#5681)
e8d4a20331 [build][cmake] Fix windows tests and re-enable CI tests (#5674)
2b58bbde0b [xrp] Add Reflectance sensor and rangefinder classes (#5673)
dd5612fbee [json] Add forward definition header (#5676)
eab44534c3 [wpimath] Remove unused SmallString include (#5677)
5ab54ff760 Replace wpi::raw_istream with wpi::MemoryBuffer (#5675)
1b6ec5a95d [wpiutil] Upgrade to LLVM 17.0.1 (#5482)
07a0d22fe6 [build] Build examples in CMake CI (#5667)
97021f074a [build] Upgrade imgui and implot (#5668)
87ce1e3761 [build] Fix wpilibNewCommands CMake install (#5671)
6ef94de9b5 [wpimath] Add tests for ArmFeedforward and ElevatorFeedforward (#5663)
c395b29fb4 [wpinet] Add WebSocket::TrySendFrames() (#5607)
c4643ba047 [romi/xrp] Fix version typo in vendordep json (#5664)
51dcb8b55a [examples] Make Romi/XRP Examples use appropriate vendordeps (#5665)
daf7702007 [build] Test each example in a new environment (#5662)
e67df8c180 [wpilib] Const-qualify EncoderSim getters (#5660)
7be290147c [wpiutil] Refactor SpanMatcher and TestPrinters from ntcore (#5658)
9fe258427a [commands] Add proxy factory to Commands (#5603)
633c5a8a22 [commands] Add C++ Requirements struct (#5504)
b265a68eea [commands] Add interruptor parameter to onCommandInterrupt callbacks (#5461)
e93c233d60 [ntcore] Compute Value memory size when creating value (#5657)
5383589f99 [wpinet] uv::Request: Return shared_ptr from Release() (#5656)
40b552be4a [wpinet] uv::Stream: Return error from TryWrite() (#5655)
202a75fe08 [wpinet] RequestImpl: Avoid infinite loop in shared_from_this() (#5654)
8896515eb7 [wpinet] uv::Buffer: Add bytes() accessor (#5653)
ae59a2fba2 [wpinet] uv::Error: Change default error to 0 (#5652)
3b51ecc35b [wpiutil] SpanExtras: Add take_back and take_front (#5651)
17f1062885 Replace std::snprintf() with wpi::format_to_n_c_str() (#5645)
bb39900353 [romi/xrp] Add Romi and XRP Vendordeps (#5644)
cb99517838 [build] cmake: Use default install location on windows for dlls (#5580)
25b0622d4c [build] Add Windows CMake CI (#5516)
34e7849605 Add warning to development builds instructions (NFC) (#5646)
e9e611c9d8 [cameraserver] Remove CameraServer.SetSize() (#5650)
94f58cc536 [wpilib] Remove Compressor.Enabled() (#5649)
4da5aee88a [wpimath] Remove SlewRateLimiter 2 argument constructor (#5648)
2e3ddf5502 Update versions in development builds instructions to 2024 (#5647)
19a8850fb1 [examples] Add TimesliceRobot templates (#3683)
9047682202 [sim] Add XRP-specific plugin (#5631)
575348b81c [wpilib] Use IsSimulation() consistently (#3534)
12e2043b77 [wpilib] Clean up Notifier (#5630)
4bac4dd0f4 [wpimath] Move PIDController from frc2 to frc namespace (#5640)
494cfd78c1 [wpiutil] Fix deprecation warning in LLVM for C++23 (#5642)
43a727e868 [apriltag] Make loadAprilTagFieldLayout throw an unchecked exception instead (#5629)
ad4b017321 [ci] Use Ninja for faster builds (#5626)
4f2114d6f5 Fix warnings from GCC 13 release build (#5637)
e7e927fe26 [build] Also compress debug info for CMake RelWithDebInfo build type (#5638)
205a40c895 [build] Specify zlib for debug info compression (#5636)
707444f000 [apriltag] Suppress -Wtype-limits warning in asserts from GCC 13 (#5635)
3b79cb6ed3 [commands] Revert SubsystemBase deprecation/removal (#5634)
bc7f23a632 [build] Compress Linux debug info (#5633)
57b2d6f254 [build] Update to image 2024 v1.0 (#5625)
339ef1ea39 [wpilib] DataLogManager: Warn user if logging to RoboRIO 1 internal storage (#5617)
7a9a901a73 [build] Fix cmake config files (#5624)
298f8a6e33 [wpilib] Add Mechanism2d tests and make Java impl match C++ (#5527)
d7ef817bae [apriltag] Update apriltag library (#5619)
c3fb31fd0e [docs] Switch to Java 17 api docs (#5613)
bd64f81cf9 [build] Run Google tests in release mode in CI (#5615)
66e6bd81ea [wpimath] Cleanup wpimath/algorithms.md (NFC) (#5621)
4fa56fd884 [build] Add missing find_dependency call (#5623)
f63d958995 [build] Update to native utils 2024.2.0 (#5601)
a9ab08f48b [wpimath] Rename ChassisSpeeds.fromDiscreteSpeeds() to discretize() (#5616)
8e05983a4a [wpimath] Add math docs to plant inversion feedforward internals (NFC) (#5618)
3a33ce918b [ntcore] Add missing StringMap include (#5620)
a6157f184d [wpiutil] timestamp: Add ShutdownNowRio (#5610)
e9f612f581 [build] Guard policy setting for CMake versions below 3.24 (#5612)
1a6df6fec6 [wpimath] Fix DARE Q decomposition (#5611)
9b3f7fb548 [build] Exclude IntelliJ folders from spotless XML (#5602)
814f18c7f5 [wpimath] Fix computation of C for DARE (A, C) detectability check (#5609)
ac23f92451 [hal] Add GetTeamNumber (#5596)
a750bee54d [wpimath] Use std::norm() in IsStabilizable() (#5599)
8e2465f8a0 [wpimath] Add arithmetic functions to wheel speeds classes (#5465)
10d4f5b5df [wpimath] Clean up notation in DARE precondition docs (#5595)
b2dd59450b [hal] Fix unfinished/incorrect GetCPUTemp functions (#5598)
99f66b1e24 [wpimath] Replace frc/EigenCore.h typedefs with Eigen's where possible (#5597)
383289bc4b [build] Make custom CMake macros use lowercase (#5594)
45e7720ec1 [build] Add error message when downloading files in CMake (#5593)
4e0d785356 [wpimath] ChassisSpeeds: document that values aren't relative to the robot (NFC) (#5551)
3c04580a57 [commands] ProxyCommand: Use inner command name in unique_ptr constructor (#5570)
cf19102c4a [commands] SelectCommand: Fix leakage and multiple composition bug (#5571)
171375f440 [ntcoreffi] Link to NI libraries (#5589)
89add5d05b Disable flaky tests (#5591)
a8d4b162ab [ntcore] Remove RPC manual tests (#5590)
39a73b5b58 [commands] C++: Add CommandPtr supplier constructor to ProxyCommand (#5572)
36d514eae7 [commands] Refactor C++ ScheduleCommand to use SmallSet (#5568)
52297ffe29 [commands] Add idle command (#5555)
67043a8eeb [wpimath] Add angular jerk unit (#5582)
51b0fb1492 [wpimath] Fix incorrect header inclusion in angular_acceleration.h (#5587)
b7657a8e28 [wpimath] Split WPIMathJNI into logical chunks (#5552)
ea17f90f87 [build] Fix tool builds with multiple arm platforms installed (#5586)
f1d7b05723 [wpimath] Clean up unit formatter (#5584)
d7264ff597 Replace wpi::errs() usage with fmtlib (#5560)
ab3bf39e0e [wpiutil] Upgrade to fmt 10.1.1 (#5585)
165ebe4c79 Upgrade to fmt 10.1.0 (#5326)
8e2a7fd306 Include thirdparty libraries with angle brackets (#5578)
e322ab8e46 [wpimath] Fix docs for DARE ABQRN stabilizability check (NFC) (#5579)
360fb835f4 [upstream_utils] Handle edge case in filename matches (#5576)
9d86624c00 [build] Fix CMake configure warnings (#5577)
969979d6c7 [wpiutil] Update to foonathan memory 0.7-3 (#5573)
0d2d989e84 [wpimath] Update to gcem 1.17.0 (#5575)
cf86af7166 [wpiutil] Update to mpack 1.1.1 (#5574)
a0c029a35b [commands] Fix dangling SelectCommand documentation (NFC) (#5567)
349141b91b [upstream_utils] Document adding a patch (NFC) (#5432)
7889b35b67 [wpimath] Add RamseteController comparison to LTV controller docs (NFC) (#5559)
b3ef536677 [build] Ignore nt/sim json files in spotless (#5565)
ed895815b5 [build] Compile Java with UTF-8 encoding (#5564)
2e4ad35e36 [wpiutil] jni_util: Add JSpan and CriticalJSpan (#5554)
8f3d6a1d4b [wpimath] Remove discretizeAQTaylor() (#5562)
7c20fa1b18 [wpimath] Refactor DARE tests to reduce RAM usage at compile time (#5557)
89e738262c [ntcore] Limit buffer pool size to 64KB per connection (#5485)
96f7fa662e Upgrade Maven dependencies (#5553)
7a2d336d52 [wpinet] Leak multicast handles during windows shutdown (#5550)
f9e2757d8f [wpimath] Use JDoubleArrayRef in all JNI functions (#5546)
0cf6e37dc1 [wpimath] Make LTV controller constructors use faster DARE solver (#5543)
6953a303b3 [build] Fix the windows build with fmt (#5544)
7a37e3a496 [wpimath] Correct Rotation3d::RotateBy doc comment (NFC) (#5541)
186b409e16 [wpimath] Remove internal Eigen header include (#5539)
03764dfe93 [wpimath] Add static matrix support to DARE solver (#5536)
394cfeadbd [wpimath] Use SDA algorithm instead of SSCA for DARE solver (#5526)
a4b7fde767 [wpilib] Add mechanism specific SetState overloads to physics sims (#5534)
8121566258 [wpimath] Fix CoordinateSystem.convert() Transform3d overload (#5532)
b542e01a0b [glass] Fix array crash when clearing existing workspace (#5535)
e2e1b763b2 [wpigui] Fix PFD file dialogs not closing after window closing (#5530)
86d7bbc4e4 [examples] Add Java Examples and Templates for the XRP (#5529)
e8b5d44752 [wpimath] Make Java Quaternion use doubles instead of Vector (#5525)
38c198fa64 [myRobot] Add apriltags to myRobot build (#5528)
00450c3548 [wpimath] Upgrade to EJML 0.42 (#5531)
faf3cecd83 [wpimath] Don't copy Matrix and underlying storage in VecBuilder (#5524)
6b896a38dc [build] Don't enforce WITH_FLAT_INSTALL with MSVC (part 2) (#5517)
c01814b80e [wpiutil] Add C API for DataLog (#5509)
b5bd0771eb [wpimath] Document extrinsic vs intrinsic rotations (NFC) (#5508)
84ed8aec05 [build] Don't enforce WITH_FLAT_INSTALL with MSVC (#5515)
999f677d8c [ntcoreffi] Add WPI_Impl_SetupNowRio to exported symbols (#5510)
338f37d302 Fix header sorting of libssh (#5507)
75cbd9d6d0 [glass] Add background color selector to glass plots (#5506)
e2c190487b [examples] Add flywheel bang-bang controller example (#4071)
c52dad609e [wpinet] WebSocket: Send pong in response to ping (#5498)
e2d17a24a6 [hal] Expose power rail disable and cpu temp functionality (#5477)
3ad5d2e42d [hal,wpiutil] Use HMB for FPGA Timestamps (#5499)
b46a872494 [ntcore] Remove pImpl from implementation (#5480)
d8c59ccc71 [wpimath] Add tests for MathUtil clamp() and interpolate() (#5501)
0552c8621d [glass,ov] Improve Glass and OutlineViewer title bar message (#5502)
90e37a129f [wpiutil,wpimath] Add generic InterpolatingTreeMap (#5372)
d83a6edc20 [wpilib] Update GetMatchTime docs and units (#5232)
6db2c42966 [wpimath] Trajectory: Throw on empty lists of States (#5497)
21439b606c [wpimath] Disallow LTV controller max velocities above 15 m/s (#5495)
7496e0d208 [ntcore] Value: More efficiently store arrays (#5484)
0c93aded8a [wpimath] Change kinematics.ToTwist2d(end - start) to kinematics.ToTwist2d(start, end) (#5493)
815a8403e5 [wpimath] Give infeasible trajectory constraints a better exception message (#5492)
35a8b129d9 [wpimath] Add RotateBy() function to pose classes (#5491)
26d6e68c8f [upstream_utils] Add GCEM to CI (#5483)
6aa469ae45 [wpilib] Document how to create LinearSystem object for physics sim classes (NFC) (#5488)
a01b6467d3 [wpimath] Link to docs on LQR and KF tolerances (#5486)
d814f1d123 [wpimath] Fix copy-paste error from Pose2d docs (NFC) (#5490)
98f074b072 [wpimath] Add folder prefix to geometry includes (#5489)
e9858c10e9 [glass] Add tooltips for NT settings (#5476)
12dda24f06 [examples] Fix C robot template not correctly looping (#5474)
fc75d31755 [apriltag] Update apriltaglib (#5475)
a95994fff6 [wpiutil] timestamp: Call FPGA functions directly (#5235)
2ba8fbb6f4 [wpimath] Improve documentation for SwerveModulePosition::operator- (#5468)
b8cdf97621 [build] Prepare for Windows arm64 builds (#5390)
552f4b76b5 [wpimath] Add FOC-enabled Falcon constants to the DCMotor class (#5469)
1938251436 [examples] Add Feedforward to ElevatorProfiledPid (#5300)
873c2a6c10 [examples] Update ElevatorTrapezoidProfile example (#5466)
99b88be4f3 [wpilib] Reduce usage of NTSendable (#5434)
d125711023 [hal] Fix Java REVPH faults bitfield (take 2) (#5464)
c3fab7f1f2 [ntcore] Don't update timestamp when value is unchanged (#5356)
5ec7f18bdc [wpilib] EventLoop docs: Remove BooleanEvent references (NFC) (#5463)
c065ae1fcf [wpiunits] Add subproject for a Java typesafe unit system (#5371)
44acca7c00 [wpiutil] Add ClassPreloader (#5365)
88b11832ec [hal] Fix Java REVPH faults bitfield (#5148)
fb57d82e52 [ntcore] Enhance Java raw value support
3a6e40a44b [wpiutil] Enhance DataLog Java raw value support
8dae5af271 [wpiutil] Add compile-time string utilities (ct_string) (#5462)
fc56f8049a [wpilib] DriverStation: Change alliance station to use optional (#5229)
ef155438bd [build] Consume libuv via cmake config instead of via pkg-config (#5438)
86e91e6724 [wpimath] Refactor TrapezoidProfile API (#5457)
72a4543493 [wpilib] DutyCycleEncoderSim: Expand API (#5443)
657338715d [wpimath] Add ChassisSpeeds method to fix drifting during compound swerve drive maneuvers (#5425)
1af224c21b Add missing <functional> includes (#5459)
0b91ca6d5a [wpilib] SendableChooser: Add onChange listener (#5458)
6f7cdd460e [wpimath] Pose3d: Switch to JNI for exp and log (#5444)
c69e34c80c [wpimath] ChassisSpeeds: Add arithmetic functions (#5293)
335e7dd89d [wpilib] Simulation: Add ctor parameter to set starting state of mechanism sims (#5288)
14f30752ab [wpilib] Deprecate Accelerometer and Gyro interfaces (#5445)
70b60e3a74 [commands] Trigger: Fix method names in requireNonNullParam (#5454)
593767c8c7 [wpimath] Improve Euler angle calculations in gimbal lock (#5437)
daf022d3da [build] Make devImplementation inherit from implementation (#5450)
9b8d90b852 [examples] Convert the unitless joystick inputs to actual physical units (#5451)
1f6428ab63 [ntcore] Fix undefined comparison behavior when array is empty (#5448)
17eb9161cd Update code owners for removal of old commands (#5447)
3c4b58ae1e [wpinet] Upgrade to libuv 1.46.0 (#5446)
aaea85ff16 [commands] Merge CommandBase into Command and SubsystemBase into Subsystem (#5392)
7ac932996a [ci] Use PAT for workflow dispatch (#5442)
efe1987e8b [ci] Trigger pages repo workflow (#5441)
828bc5276f [wpiutil] Upgrade to LLVM 16.0.6 (#5435)
701df9eb87 [ci] Change documentation publish to single-commit (#5440)
e5452e3f69 [wpiutil] Add WPICleaner and an example how to use it (#4850)
7a099cb02a [commands] Remove deprecated classes and functions (#5409)
b250a03944 [wpilib] Add function to wait for DS Connection (#5230)
a6463ed761 [wpiutil] Fix unused variable warning in release build (#5430)
f031513470 [ntcore] NetworkTable::GetSubTables(): Remove duplicates (#5076)
f8e74e2f7c [hal] Unify PWM simulation Speed, Position, and Raw (#5277)
fd5699b240 Remove references to Drake (#5427)
e2d385d80a [build] cmake: Respect USE_SYSTEM_FMTLIB (#5429)
d37f990ce3 [hal] Fix HAL Relay/Main doc module (NFC) (#5422)
a7a8b874ac [docs] Expand HAL_ENUM in doxygen docs (#5421)
3a61deedde [wpimath] Rotation2d: Only use gcem::hypot when constexpr evaluated (#5419)
96145de7db [examples] Fix formatting (NFC) (#5420)
fffe6a7b9a [examples] Improve Pneumatics example coverage in Solenoid and RapidReactCmdBot examples (#4998)
6b5817836d [wpimath] Add tolerance for some tests (#5416)
3233883f3e [cscore] Fix warnings on macos arm (#5415)
c4fc21838f [commands] Add ConditionalCommand getInterruptionBehavior (#5161)
89fc51f0d4 Add tests for SendableChooser and Command Sendable functionality (#5179)
663bf25aaf [docs] Generate docs for symbols in __cplusplus (#5412)
fe32127ea8 [command] Clean up Command doc comments (NFC) (#5321)
c1a01569b4 [wpilib][hal] PWM Raw using microseconds (#5283)
1fca519fb4 [wpiutil] Remove remnants of ghc fs and tcb_span libraries (#5411)
90602cc135 [github] Update issue template to collect more project info (#5090)
34412ac57e [build] Exclude files in bin from Spotless (#5410)
61aa60f0e3 [wpilib] Add robot callback that is called when the DS is initially connected (#5231)
ebae341a91 [commands] Add test for subsystem registration and periodic (#5408)
5d3a133f9f Remove spaces in NOLINT comments (#5407)
3a0e484691 [wpimath] Fix clang-tidy warnings (#5403)
eb3810c765 [wpiutil] Fix clang-tidy warnings (#5406)
c4dc697192 [hal] WS Simulation: Add message filtering capability (#5395)
0eccc3f247 [ntcore] Fix clang-tidy warnings (#5405)
f4dda4bac0 [hal] Add javadocs for JNI (NFC) (#5298)
1c20c69793 [cscore] Fix clang-tidy warnings (#5404)
1501607e48 [commands] Fix clang-tidy warnings (#5402)
991f4b0f62 [wpimath] PIDController: Add IZone (#5315)
f5b0d1484b [wpimath] Add isNear method to MathUtil (#5353)
2ce248f66c [hal] Fix clang-tidy warnings (#5401)
5fc4aee2d2 [wpimath] SwerveDriveKinematics: Rename currentChassisSpeed to desiredChassisSpeed (#5393)
50b90ceb54 [wpimath] SwerveDriveKinematics: Add reset method (#5398)
316cd2a453 [commands] Notify DriverStationSim in CommandTestBaseWithParam (#5400)
d4ea5fa902 [cscore] VideoMode: Add equals override (Java) (#5397)
d6bd72d738 [wpimath] ProfiledPIDController: Add getConstraints (#5399)
25ad5017a9 [wpimath] Refactor kinematics, odometry, and pose estimator (#5355)
5c2addda0f [doc] Add missing pneumatics docs (NFC) (#5389)
c3e04a6ea2 Fix loading tests on macos 12 (#5388)
d5ed9fb859 [wpimath] Create separate archive with just units headers (#5383)
901ab693d4 [wpimath] Use UtilityClassTest for more utility classes (#5384)
9d53231b01 [wpilib] DataLogManager: Add warning for low storage space (#5364)
d466933963 [wpiutil] Group doxygen into MPack module (#5380)
652d1c44e3 [wpiutil] Upgrade to macOS 12 to remove concept shims (#5379)
6414be0e5d [wpimath] Group units doxygen modules (#5382)
7ab5800487 [wpiutil] Fix docs typo in SmallVector (#5381)
59905ea721 Replace WPI_DEPRECATED() macro with [[deprecated]] attribute (#5373)
753cb49a5e [ntcore] Fix doxygen module in generated C types (NFC) (#5374)
1c00a52b67 [hal] Expose CAN timestamp base clock (#5357)
91cbcea841 Replace SFINAE with concepts (#5361)
d57d1a4598 [wpimath] Remove unnecessary template argument from unit formatter (#5367)
5acc5e22aa [wpimath] Only compute eigenvalues with EigenSolvers (#5369)
d3c9316a97 extend shuffleboard test timeout (#5377)
1ea868081a [ci] Fix /format command (#5376)
5fac18ff4a Update formatting to clang-format 16 (#5370)
a94a998002 [wpimath] Generalize Eigen formatter (#5360)
125f6ea101 [wpimath] Make SwerveDriveKinematics::ToChassisSpeeds() take const-ref argument (#5363)
51066a5a8a [wpimath] Move unit formatters into units library (#5358)
282c032b60 [wpilibc] Add unit-aware Joystick.GetDirection() (#5319)
073d19cb69 [build] Fix CMake warning (#5359)
01490fc77b [wpiutil] DataLog: Add documentation for append methods (NFC) (#5348)
c9b612c986 [wpilibcExamples] Make C++ state-space elevator KF and LQR match Java (#5346)
eed1e6e3cb [wpimath] Replace DiscretizeAQTaylor() with DiscretizeAQ() (#5344)
c976f40364 [readme] Document how to run examples in simulation (#5340)
4d28bdc19e [ci] Update Github Pages deploy action parameters (#5343)
e0f851871f [ci] Fix github pages deploy version (#5342)
063c8cbedc Run wpiformat (NFC) (#5341)
96e41c0447 [ci] Update deploy and sshagent actions (#5338)
fd294bdd71 [build] Fix compilation with GCC 13 (#5322)
d223e4040b [dlt] Add delete without download functionality (#5329)
abc19bcb43 [upstream_utils] Zero out commit hashes and show 40 digits in index hashes (#5336)
e909f2e687 [build] Update gradle cache repo name (#5334)
52bd5b972d [wpimath] Rewrite DARE solver (#5328)
3876a2523a [wpimath] Remove unused MatrixImpl() function (#5330)
c82fcb1975 [wpiutil] Add reflection based cleanup helper (#4919)
15ba95df7e [wpiutil] Use std::filesystem (#4941)
77c2124fc5 [wpimath] Remove Eigen's custom STL types (#4945)
27fb47ab10 [glass] Field2D: Embed standard field images (#5159)
102e4f2566 [wpilib] Remove deprecated and broken SPI methods (#5249)
463a90f1df [wpilib, hal] Add function to read the RSL state (#5312)
7a90475eec [wpilib] Update RobotBase documentation (NFC) (#5320)
218cfea16b [wpilib] DutyCycleEncoder: Fix reset behavior (#5287)
91392823ff [build] Update to gradle 8.1 (#5303)
258b7cc48b [wpilibj] Filesystem.getDeployDirectory(): Strip JNI path from user.dir (#5317)
26cc43bee1 [wpilib] Add documentation to SPI mode enum (NFC) (#5324)
ac4da9b1cb [hal] Add HAL docs for Addressable LED (NFC) (#5304)
21d4244cf7 [wpimath] Fix DCMotor docs (NFC) (#5309)
1dff81bea7 [hal] Miscellaneous HAL doc fixes (NFC) (#5306)
7ce75574bf [wpimath] Upgrade to Drake v1.15.0 (#5310)
576bd646ae [hal] Add CANManufacturer for Redux Robotics (#5305)
ee3b4621e5 [commands] Add onlyWhile and onlyIf (#5291)
40ca094686 [commands] Fix RepeatCommand calling end() twice (#5261)
9cbeb841f5 [rtns] Match imaging tool capitalization (#5265)
a63d06ff77 [examples] Add constants to java gearsbot example (#5248)
b6c43322a3 [wpilibc] XboxController: Add return tag to docs (NFC) (#5246)
5162d0001c [hal] Fix and document addressable LED timings (#5272)
90fabe9651 [wpilibj] Use method references in drive class initSendable() (#5251)
24828afd11 [wpimath] Fix desaturateWheelSpeeds to account for negative speeds (#5269)
e099948a77 [wpimath] Clean up rank notation in docs (NFC) (#5274)
fd2d8cb9c1 [hal] Use std::log2() for base-2 logarithm (#5278)
ba8c64bcff [wpimath] Fix misspelled Javadoc parameters in pose estimators (NFC) (#5292)
f53c6813d5 [wpimath] Patch Eigen warnings (#5290)
663703d370 [gitattributes] Mark json files as lf text files (#5256)
aa34aacf6e [wpilib] Shuffleboard: Keep duplicates on SelectTab() (#5198)
63512bbbb8 [wpimath] Fix potential divide-by-zero in RKDP (#5242)
9227b2166e [wpilibj] DriverStation: Fix joystick data logs (#5240)
fbf92e9190 [wpinet] ParallelTcpConnector: don't connect to duplicate addresses (#5169)
2108a61362 [ntcore] NT4 client: close timed-out connections (#5175)
0a66479693 [ntcore] Optimize scan of outgoing messages (#5227)
b510c17ef6 [hal] Fix RobotController.getComments() mishandling quotes inside the comments string (#5197)
e7a7eb2e93 [commands] WaitCommand: Remove subclass doc note (NFC) (#5200)
a465f2d8f0 [examples] Shuffleboard: Correct parameter order (#5204)
a3364422fa LICENSE.md: Bump year to 2023 (#5195)
df3242a40a [wpimath] Fix NaN in C++ MakeCostMatrix() that takes an array (#5194)
00abb8c1e0 [commands] RamseteCommand: default-initialize m_prevSpeeds (#5188)
c886273fd7 [wpilibj] DutyCycleEncoder.setDistancePerRotation(): fix simulation (#5147)
53b5fd2ace [ntcore] Use int64 for datalog type string (#5186)
56b758320f [wpilib] DataLogManager: increase time for datetime to be valid (#5185)
08f298e4cd [wpimath] Fix Pose3d log returning Twist3d NaN for theta between 1E-8 and 1E-7 (#5168)
6d0c5b19db [commands] CommandScheduler.isComposed: Remove incorrect throws clause (NFC) (#5183)
0d22cf5ff7 [wpilib] Fix enableLiveWindowInTest crashing in disabled (#5173)
32ec5b3f75 [wpilib] Add isTestEnabled and minor docs cleanup (#5172)
e5c4c6b1a7 [wpimath] Fix invalid iterator access in TimeInterpolatableBuffer (#5138)
099d048d9e [wpimath] Fix Pose3d log returning Twist3d NaN for theta between 1E-9 and 1E-8 (#5143)
4af84a1c12 Fix Typos (NFC) (#5137)
ce3686b80d [wpimath] Check LTV controller max velocity precondition (#5142)
4b0eecaee0 [commands] Subsystem: Add default command removal method (#5064)
edf4ded412 [wpilib] PH: Revert to 5V rail being fixed 5V (#5122)
4c46b6aff9 [wpilibc] Fix DataLogManager crash on exit in sim (#5125)
490ca4a68a [wpilibc] Fix XboxController::GetBackButton doc (NFC) (#5131)
cbb5b0b802 [hal] Simulation: Fix REV PH solenoids 8+ (#5132)
bb7053d9ee [hal] Fix HAL_GetRuntimeType being slow on the roboRIO (#5130)
9efed9a533 Update .clang-format to c++20 (#5121)
dbbfe1aed2 [wpilib] Use PH voltage to calc Analog pressure switch threshold (#5115)
de65a135c3 [wpilib] DutyCycleEncoderSim: Add channel number constructor (#5118)
3e9788cdff [docs] Strip path from generated NT docs (#5119)
ecb072724d [ntcore] Client::Disconnect(): actually close connection (#5113)
0d462a4561 [glass] NT view: Change string/string array to quoted (#5111)
ba37986561 [ntcore] NetworkClient::Disconnect: Add null check (#5112)
25ab9cda92 [glass,ov] Provide menu item to create topic from root (#5110)
2f6251d4a6 [glass] Set default value when publishing new topic (#5109)
e9a7bed988 [wpimath] Add timestamp getter to MathShared (#5091)
9cc14bbb43 [ntcore] Add stress test to dev executable (#5107)
8068369542 [wpinet] uv: Stop creating handles when closing loop (#5102)
805c837a42 [ntcore] Fix use-after-free in server (#5101)
fd18577ba0 [commands] Improve documentation of addRequirements (NFC) (#5103)
74dea9f05e [wpimath] Fix exception for empty pose buffer in pose estimators (#5106)
9eef79d638 [wpilib] PneumaticHub: Document range of enableCompressorAnalog (NFC) (#5099)
843574a810 [ntcore] Use wpi::Now instead of loop time for transmit time
226ef35212 [wpinet] WebSocket: Reduce server send frame overhead
b30664d630 [ntcore] Reduce initial connection overhead
804e5ce236 [examples] MecanumDrive: Fix axis comment in C++ example (NFC) (#5096)
49af88f2bb [examples] ArmSimulation: Fix flaky test (#5093)
d56314f866 [wpiutil] Disable mock time on the Rio (#5092)
43975ac7cc [examples] ArmSimulation, ElevatorSimulation: Extract mechanism to class (#5052)
5483464158 [examples, templates] Improve descriptions (NFC) (#5051)
785e7dd85c [wpilibc] SendableChooser: static_assert copy- and default-constructibility (#5078)
e57ded8c39 [ntcore] Improve disconnect error reporting (#5085)
01f0394419 [wpinet] Revert WebSocket: When Close() is called, call closed immediately (#5084)
59be120982 [wpimath] Fix Pose3d exp()/log() and add rotation vector constructor to Rotation3d (#5072)
37f065032f [wpilib] Refactor TimedRobot tests (#5068)
22a170bee7 [wpilib] Add Notifier test (#5070)
2f310a748c [wpimath] Fix DCMotor.getSpeed() (#5061)
b43ec87f57 [wpilib] ElevatorSim: Fix WouldHitLimit methods (#5057)
19267bef0c [ntcore] Output warning on property set on unpublished topic (#5059)
84cbd48d84 [ntcore] Handle excludeSelf on SetDefault (#5058)
1f35750865 [cameraserver] Add GetInstance() to all functions (#5054)
8230fc631d [wpilib] Revert throw on nonexistent SimDevice name in SimDeviceSim (#5053)
b879a6f8c6 [wpinet] WebSocket: When Close() is called, call closed immediately (#5047)
49459d3e45 [ntcore] Change wire timeout to fixed 1 second (#5048)
4079eabe9b [wpimath] Discard stale pose estimates (#5045)
fe5d226a19 [glass] Fix option for debug-level NT logging (#5049)
b7535252c2 [ntcore] Don't leak buffers in rare WS shutdown case (#5046)
b61ac6db33 [ntcore] Add client disconnect function (#5022)
7b828ce84f [wpimath] Add nearest to Pose2d and Translation2d (#4882)
08a536291b [examples] Improvements to Elevator Simulation Example (#4937)
193a10d020 [wpigui] Limit frame rate to 120 fps by default (#5030)
7867bbde0e [wpilib] Clarify DS functions provided by FMS (NFC) (#5043)
fa7c01b598 [glass] Add option for debug-level NT logging (#5007)
2b81610248 [wpiutil] Add msgpack to datalog Python example (#5032)
a4a369b8da CONTRIBUTING.md: Add unicodeit CLI to math docs guidelines (#5031)
d991f6e435 [wpilib] Throw on nonexistent SimDevice name in SimDeviceSim constructor (#5041)
a27a047ae8 [hal] Check for null in getSimDeviceName JNI (#5038)
2f96cae31a [examples] Hatchbots: Add telemetry (#5011)
83ef8f9658 [simulation] GUI: Fix buffer overflow in joystick axes copy (#5036)
4054893669 [commands] Fix C++ Select() factory (#5024)
f75acd11ce [commands] Use Timer.restart() (#5023)
8bf67b1b33 [wpimath] PIDController::Calculate(double, double): update setpoint flag (#5021)
49bb1358d8 [wpiutil] MemoryBuffer: Fix GetMemoryBufferForStream (#5017)
9c4c07c0f9 [wpiutil] Remove NDEBUG check for debug-level logging (#5018)
1a47cc2e86 [ntcore] Use full handle when subscribing (#5013)
7cd30cffbc Ignore networktables.json (#5006)
92aecab2ef [commands] Command controllers are not subclasses (NFC) (#5000)
8785bba080 [ntcore] Special-case default timestamps (#5003)
9e5b7b8040 [ntcore] Handle topicsonly followed by value subscribe (#4991)
917906530a [wpilib] Add Timer::Restart() (#4963)
00aa66e4fd [wpimath] Remove extraneous assignments from DiscretizeAB() (#4967)
893320544a [examples] C++ RamseteCommand: Fix units (#4954)
b95d0e060d [wpilib] XboxController: Fix docs discrepancy (NFC) (#4993)
008232b43c [ntcore] Write empty persistent file if none found (#4996)
522be348f4 [examples] Rewrite tags (NFC) (#4961)
d48a83dee2 [wpimath] Update Wikipedia links for quaternion to Euler angle conversion (NFC) (#4995)
504fa22143 [wpimath] Workaround intellisense Eigen issue (#4992)
b2b25bf09f [commands] Fix docs inconsistency for toggleOnFalse(Command) (NFC) (#4978)
ce3dc4eb3b [hal] Properly use control word that is in sync with DS data (#4989)
1ea48caa7d [wpilib] Fix C++ ADXRS450 and Java SPI gyro defs (#4988)
fb101925a7 [build] Include wpimathjni in commands binaries (#4981)
657951f6dd [starter] Add a process starter for use by the installer for launching tools (#4931)
a60ca9d71c [examples] Update AprilTag field load API usage (#4975)
f8a45f1558 [wpimath] Remove print statements from tests (#4977)
ecba8b99a8 [examples] Fix swapped arguments in MecanumControllerCommand example (#4976)
e95e88fdf9 [examples] Add comment to drivedistanceoffboard example (#4877)
371d15dec3 [examples] Add Computer Vision Pose Estimation and Latency Compensation Example (#4901)
cb9b8938af [sim] Enable docking in the GUI (#4960)
3b084ecbe0 [apriltag] AprilTagFieldLayout: Improve API shape for loading builtin JSONs (#4949)
27ba096ea1 [wpilib] Fix MOI calculation error in SingleJointedArmSim (#4968)
42c997a3c4 [wpimath] Fix Pose3d exponential and clean up Pose3d logarithm (#4970)
5f1a025f27 [wpilibj] Fix typo in MecanumDrive docs (NFC) (#4969)
0ebf79b54c [wpimath] Fix typo in Pose3d::Exp() docs (NFC) (#4966)
a8c465f3fb [wpimath] HolonomicDriveController: Add getters for the controllers (#4948)
a7b1ab683d [wpilibc] Add unit test for fast deconstruction of GenericHID (#4953)
bd6479dc29 [build] Add Spotless for JSON (#4956)
5cb0340a8c [hal, wpilib] Load joystick values upon code initialization (#4950)
ab0e8c37a7 [readme] Update build requirements (NFC) (#4947)
b74ac1c645 [build] Add apriltag to C++ cmake example builds (#4944)
cf1a411acf [examples] Add example programs for AprilTags detection (#4932)
1e05b21ab5 [wpimath] Fix PID atSetpoint to not return true prematurely (#4906)
e5a6197633 [wpimath] Fix SwerveDriveKinematics not initializing a new array each time (#4942)
039edcc23f [ntcore] Queue current value on subscriber creation (#4938)
f7f19207e0 [wpimath] Allow multiple vision measurements from same timestamp (#4917)
befd12911c [commands] Delete UB-causing rvalue variants of CommandPtr methods (#4923)
34519de60a [commands] Fix spacing in command composition exception (#4924)
dc4355c031 [hal] Add handle constructor and name getters for sim devices (#4925)
53d8d33bca [hal, wpilibj] Add missing distance per pulse functions to EncoderSim (#4928)
530ae40614 [apriltag] Explain what April tag poses represent (NFC) (#4930)
79f565191e [examples] DigitalCommunication, I2CCommunication: Add tests (#4865)
2cd9be413f [wpilib, examples] Cleanup PotentiometerPID, Ultrasonic, UltrasonicPID examples (#4893)
babb0c1fcf [apriltag] Add 2023 field layout JSON (#4912)
330ba45f9c [wpimath] Fix swerve kinematics util classes equals function (#4907)
51272ef6b3 [fieldImages] Add 2023 field (#4915)
0d105ab771 [commands] Deduplicate command test utils (#4897)
cf4235ea36 [wpiutil] Guard MSVC pragma in SymbolExports.h (#4911)
2d4b7b9147 [build] Update opencv version in opencv.gradle (#4909)
aec6f3d506 [ntcore] Fix client flush behavior (#4903)
bfe346c76a [build] Fix cmake java resources (#4898)

Change-Id: Ia1dd90fe42c6cd5df281b8a5b710e136f54355f4
git-subtree-dir: third_party/allwpilib
git-subtree-split: f1a82828fed8950f9a3f1586c44327027627a0c8
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp b/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
index 7048720..14fd37f 100644
--- a/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
+++ b/wpilibc/src/main/native/cpp/ADIS16448_IMU.cpp
@@ -26,7 +26,7 @@
 #include <string>
 
 #include <hal/HAL.h>
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 #include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/Errors.h"
@@ -285,7 +285,7 @@
   m_spi->SetAutoTransmitData({{GLOB_CMD}}, 27);
   // Configure auto stall time
   m_spi->ConfigureAutoStall(HAL_SPI_kMXP, 100, 1000, 255);
-  // Kick off DMA SPI (Note: Device configration impossible after SPI DMA is
+  // Kick off DMA SPI (Note: Device configuration impossible after SPI DMA is
   // activated)
   m_spi->StartAutoTrigger(*m_auto_interrupt, true, false);
   // Check to see if the acquire thread is running. If not, kick one off.
@@ -882,7 +882,7 @@
  * This function pushes the most recent angle estimates for all axes to the
  *driver station.
  **/
-void ADIS16448_IMU::InitSendable(nt::NTSendableBuilder& builder) {
+void ADIS16448_IMU::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("ADIS16448 IMU");
   builder.AddDoubleProperty(
       "Yaw Angle", [=, this] { return GetAngle().value(); }, nullptr);
diff --git a/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp b/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
index e1cd986..05dac78 100644
--- a/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
+++ b/wpilibc/src/main/native/cpp/ADIS16470_IMU.cpp
@@ -23,7 +23,7 @@
 #include <string>
 
 #include <hal/HAL.h>
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 #include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/Errors.h"
@@ -279,7 +279,7 @@
   }
   // Configure auto stall time
   m_spi->ConfigureAutoStall(HAL_SPI_kOnboardCS0, 5, 1000, 1);
-  // Kick off DMA SPI (Note: Device configration impossible after SPI DMA is
+  // Kick off DMA SPI (Note: Device configuration impossible after SPI DMA is
   // activated) DR High = Data good (data capture should be triggered on the
   // rising edge)
   m_spi->StartAutoTrigger(*m_auto_interrupt, true, false);
@@ -816,7 +816,7 @@
  * This function pushes the most recent angle estimates for all axes to the
  *driver station.
  **/
-void ADIS16470_IMU::InitSendable(nt::NTSendableBuilder& builder) {
+void ADIS16470_IMU::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("ADIS16470 IMU");
   builder.AddDoubleProperty(
       "Yaw Angle", [=, this] { return GetAngle().value(); }, nullptr);
diff --git a/wpilibc/src/main/native/cpp/ADXL362.cpp b/wpilibc/src/main/native/cpp/ADXL362.cpp
index fa0a8a3..425815e 100644
--- a/wpilibc/src/main/native/cpp/ADXL362.cpp
+++ b/wpilibc/src/main/native/cpp/ADXL362.cpp
@@ -93,7 +93,6 @@
       m_gsPerLSB = 0.002;
       break;
     case kRange_8G:
-    case kRange_16G:  // 16G not supported; treat as 8G
       m_gsPerLSB = 0.004;
       break;
   }
diff --git a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
index 4d4b22d..c134b5c 100644
--- a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
+++ b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
@@ -69,7 +69,7 @@
   return m_connected;
 }
 
-static bool CalcParity(int v) {
+static bool CalcParity(uint32_t v) {
   bool parity = false;
   while (v != 0) {
     parity = !parity;
@@ -87,7 +87,7 @@
 }
 
 uint16_t ADXRS450_Gyro::ReadRegister(int reg) {
-  int cmd = 0x80000000 | static_cast<int>(reg) << 17;
+  uint32_t cmd = 0x80000000 | static_cast<int>(reg) << 17;
   if (!CalcParity(cmd)) {
     cmd |= 1u;
   }
@@ -140,6 +140,10 @@
   m_spi.ResetAccumulator();
 }
 
+Rotation2d ADXRS450_Gyro::GetRotation2d() const {
+  return units::degree_t{-GetAngle()};
+}
+
 int ADXRS450_Gyro::GetPort() const {
   return m_port;
 }
diff --git a/wpilibc/src/main/native/cpp/AddressableLED.cpp b/wpilibc/src/main/native/cpp/AddressableLED.cpp
index 538dc10..0baae3c 100644
--- a/wpilibc/src/main/native/cpp/AddressableLED.cpp
+++ b/wpilibc/src/main/native/cpp/AddressableLED.cpp
@@ -65,14 +65,14 @@
   FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
-void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0,
-                                  units::nanosecond_t highTime0,
-                                  units::nanosecond_t lowTime1,
-                                  units::nanosecond_t highTime1) {
+void AddressableLED::SetBitTiming(units::nanosecond_t highTime0,
+                                  units::nanosecond_t lowTime0,
+                                  units::nanosecond_t highTime1,
+                                  units::nanosecond_t lowTime1) {
   int32_t status = 0;
   HAL_SetAddressableLEDBitTiming(
-      m_handle, lowTime0.to<int32_t>(), highTime0.to<int32_t>(),
-      lowTime1.to<int32_t>(), highTime1.to<int32_t>(), &status);
+      m_handle, highTime0.to<int32_t>(), lowTime0.to<int32_t>(),
+      highTime1.to<int32_t>(), lowTime1.to<int32_t>(), &status);
   FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp
index cab9f94..df6d33f 100644
--- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp
@@ -133,6 +133,10 @@
   FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
+Rotation2d AnalogGyro::GetRotation2d() const {
+  return units::degree_t{-GetAngle()};
+}
+
 std::shared_ptr<AnalogInput> AnalogGyro::GetAnalogInput() const {
   return m_analog;
 }
diff --git a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
index 4284e89..857643b 100644
--- a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
+++ b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
@@ -22,11 +22,6 @@
 }
 
 void BuiltInAccelerometer::SetRange(Range range) {
-  if (range == kRange_16G) {
-    throw FRC_MakeError(err::ParameterOutOfRange,
-                        "16G range not supported (use k2G, k4G, or k8G)");
-  }
-
   HAL_SetAccelerometerActive(false);
   HAL_SetAccelerometerRange(static_cast<HAL_AccelerometerRange>(range));
   HAL_SetAccelerometerActive(true);
diff --git a/wpilibc/src/main/native/cpp/CAN.cpp b/wpilibc/src/main/native/cpp/CAN.cpp
index fc4a821..aa0370f 100644
--- a/wpilibc/src/main/native/cpp/CAN.cpp
+++ b/wpilibc/src/main/native/cpp/CAN.cpp
@@ -131,3 +131,7 @@
     return true;
   }
 }
+
+uint64_t CAN::GetTimestampBaseTime() {
+  return HAL_GetCANPacketBaseTime();
+}
diff --git a/wpilibc/src/main/native/cpp/Compressor.cpp b/wpilibc/src/main/native/cpp/Compressor.cpp
index 2479ffc..1f6c906 100644
--- a/wpilibc/src/main/native/cpp/Compressor.cpp
+++ b/wpilibc/src/main/native/cpp/Compressor.cpp
@@ -34,10 +34,6 @@
   }
 }
 
-bool Compressor::Enabled() const {
-  return IsEnabled();
-}
-
 bool Compressor::IsEnabled() const {
   return m_module->GetCompressor();
 }
diff --git a/wpilibc/src/main/native/cpp/Counter.cpp b/wpilibc/src/main/native/cpp/Counter.cpp
index 2e9c916..c33b69b 100644
--- a/wpilibc/src/main/native/cpp/Counter.cpp
+++ b/wpilibc/src/main/native/cpp/Counter.cpp
@@ -260,6 +260,18 @@
   return m_index;
 }
 
+void Counter::SetDistancePerPulse(double distancePerPulse) {
+  m_distancePerPulse = distancePerPulse;
+}
+
+double Counter::GetDistance() const {
+  return Get() * m_distancePerPulse;
+}
+
+double Counter::GetRate() const {
+  return m_distancePerPulse / GetPeriod().value();
+}
+
 int Counter::Get() const {
   int32_t status = 0;
   int value = HAL_GetCounter(m_counter, &status);
diff --git a/wpilibc/src/main/native/cpp/DataLogManager.cpp b/wpilibc/src/main/native/cpp/DataLogManager.cpp
index b92faa3..afe9330 100644
--- a/wpilibc/src/main/native/cpp/DataLogManager.cpp
+++ b/wpilibc/src/main/native/cpp/DataLogManager.cpp
@@ -4,6 +4,8 @@
 
 #include "frc/DataLogManager.h"
 
+#include <frc/Errors.h>
+
 #include <algorithm>
 #include <ctime>
 #include <random>
@@ -19,6 +21,8 @@
 
 #include "frc/DriverStation.h"
 #include "frc/Filesystem.h"
+#include "frc/RobotBase.h"
+#include "frc/RobotController.h"
 
 using namespace frc;
 
@@ -26,6 +30,7 @@
 
 struct Thread final : public wpi::SafeThread {
   Thread(std::string_view dir, std::string_view filename, double period);
+  ~Thread() override;
 
   void Main() final;
 
@@ -66,8 +71,13 @@
       (s.permissions() & fs::perms::others_write) != fs::perms::none) {
     return std::string{usbDir};
   }
+  if (RobotBase::GetRuntimeType() == kRoboRIO) {
+    FRC_ReportError(warn::Warning,
+                    "DataLogManager: Logging to RoboRIO 1 internal storage is "
+                    "not recommended! Plug in a FAT32 formatted flash drive!");
+  }
 #endif
-  return frc::filesystem::GetOperatingDirectory();
+  return filesystem::GetOperatingDirectory();
 }
 
 static std::string MakeLogFilename(std::string_view filenameOverride) {
@@ -94,15 +104,25 @@
   StartNTLog();
 }
 
+Thread::~Thread() {
+  StopNTLog();
+}
+
 void Thread::Main() {
   // based on free disk space, scan for "old" FRC_*.wpilog files and remove
   {
-    uintmax_t freeSpace = fs::space(m_logDir).free;
+    std::error_code ec;
+    uintmax_t freeSpace;
+    auto freeSpaceInfo = fs::space(m_logDir, ec);
+    if (!ec) {
+      freeSpace = freeSpaceInfo.available;
+    } else {
+      freeSpace = UINTMAX_MAX;
+    }
     if (freeSpace < kFreeSpaceThreshold) {
       // Delete oldest FRC_*.wpilog files (ignore FRC_TBD_*.wpilog as we just
       // created one)
       std::vector<fs::directory_entry> entries;
-      std::error_code ec;
       for (auto&& entry : fs::directory_iterator{m_logDir, ec}) {
         auto stem = entry.path().stem().string();
         if (wpi::starts_with(stem, "FRC_") &&
@@ -124,6 +144,8 @@
         }
         auto size = entry.file_size();
         if (fs::remove(entry.path(), ec)) {
+          FRC_ReportError(warn::Warning, "DataLogManager: Deleted {}",
+                          entry.path().string());
           freeSpace += size;
           if (freeSpace >= kFreeSpaceThreshold) {
             break;
@@ -133,6 +155,13 @@
                      entry.path().string());
         }
       }
+    } else if (freeSpace < 2 * kFreeSpaceThreshold) {
+      FRC_ReportError(
+          warn::Warning,
+          "DataLogManager: Log storage device has {} MB of free space "
+          "remaining! Logs will get deleted below {} MB of free space. "
+          "Consider deleting logs off the storage device.",
+          freeSpace / 1000000, kFreeSpaceThreshold / 1000000);
     }
   }
 
@@ -182,11 +211,9 @@
         dsAttachCount = 0;
       }
       if (dsAttachCount > 50) {  // 1 second
-        std::time_t now = std::time(nullptr);
-        auto tm = std::gmtime(&now);
-        if (tm->tm_year > 100) {
-          // assume local clock is now synchronized to DS, so rename based on
-          // local time
+        if (RobotController::IsSystemTimeValid()) {
+          std::time_t now = std::time(nullptr);
+          auto tm = std::gmtime(&now);
           m_log.SetFilename(fmt::format("FRC_{:%Y%m%d_%H%M%S}.wpilog", *tm));
           dsRenamed = true;
         } else {
@@ -202,7 +229,7 @@
       } else {
         fmsAttachCount = 0;
       }
-      if (fmsAttachCount > 100) {  // 2 seconds
+      if (fmsAttachCount > 250) {  // 5 seconds
         // match info comes through TCP, so we need to double-check we've
         // actually received it
         auto matchType = DriverStation::GetMatchType();
@@ -238,7 +265,9 @@
     ++sysTimeCount;
     if (sysTimeCount >= 250) {
       sysTimeCount = 0;
-      sysTimeEntry.Append(wpi::GetSystemTime(), wpi::Now());
+      if (RobotController::IsSystemTimeValid()) {
+        sysTimeEntry.Append(wpi::GetSystemTime(), wpi::Now());
+      }
     }
   }
   DriverStation::RemoveRefreshedDataEventHandle(newDataEvent.GetHandle());
@@ -285,6 +314,9 @@
                              std::string_view filename = "",
                              double period = 0.25) {
   static Instance instance(dir, filename, period);
+  if (!instance.owner) {
+    instance.owner.Start(MakeLogDir(dir), filename, period);
+  }
   return instance;
 }
 
@@ -293,6 +325,12 @@
   GetInstance(dir, filename, period);
 }
 
+void DataLogManager::Stop() {
+  auto& inst = GetInstance();
+  inst.owner.GetThread()->m_log.Stop();
+  inst.owner.Stop();
+}
+
 void DataLogManager::Log(std::string_view message) {
   GetInstance().owner.GetThread()->m_messageLog.Append(message);
   fmt::print("{}\n", message);
diff --git a/wpilibc/src/main/native/cpp/DriverStation.cpp b/wpilibc/src/main/native/cpp/DriverStation.cpp
index aead343..8f80427 100644
--- a/wpilibc/src/main/native/cpp/DriverStation.cpp
+++ b/wpilibc/src/main/native/cpp/DriverStation.cpp
@@ -484,6 +484,12 @@
   return controlWord.test;
 }
 
+bool DriverStation::IsTestEnabled() {
+  HAL_ControlWord controlWord;
+  HAL_GetControlWord(&controlWord);
+  return controlWord.test && controlWord.enabled;
+}
+
 bool DriverStation::IsDSAttached() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
@@ -527,7 +533,7 @@
   return info.replayNumber;
 }
 
-DriverStation::Alliance DriverStation::GetAlliance() {
+std::optional<DriverStation::Alliance> DriverStation::GetAlliance() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -540,11 +546,11 @@
     case HAL_AllianceStationID_kBlue3:
       return kBlue;
     default:
-      return kInvalid;
+      return {};
   }
 }
 
-int DriverStation::GetLocation() {
+std::optional<int> DriverStation::GetLocation() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -558,13 +564,27 @@
     case HAL_AllianceStationID_kBlue3:
       return 3;
     default:
-      return 0;
+      return {};
   }
 }
 
-double DriverStation::GetMatchTime() {
+bool DriverStation::WaitForDsConnection(units::second_t timeout) {
+  wpi::Event event{true, false};
+  HAL_ProvideNewDataEventHandle(event.GetHandle());
+  bool result = false;
+  if (timeout == 0_s) {
+    result = wpi::WaitForObject(event.GetHandle());
+  } else {
+    result = wpi::WaitForObject(event.GetHandle(), timeout.value(), nullptr);
+  }
+
+  HAL_RemoveNewDataEventHandle(event.GetHandle());
+  return result;
+}
+
+units::second_t DriverStation::GetMatchTime() {
   int32_t status = 0;
-  return HAL_GetMatchTime(&status);
+  return units::second_t{HAL_GetMatchTime(&status)};
 }
 
 double DriverStation::GetBatteryVoltage() {
diff --git a/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp b/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
index b1c943e..f2ac77f 100644
--- a/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
+++ b/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
@@ -164,7 +164,10 @@
   if (m_counter) {
     m_counter->Reset();
   }
-  m_positionOffset = m_dutyCycle->GetOutput();
+  if (m_simPosition) {
+    m_simPosition.Set(0);
+  }
+  m_positionOffset = GetAbsolutePosition();
 }
 
 bool DutyCycleEncoder::IsConnected() const {
diff --git a/wpilibc/src/main/native/cpp/Filesystem.cpp b/wpilibc/src/main/native/cpp/Filesystem.cpp
index d497779..7947d75 100644
--- a/wpilibc/src/main/native/cpp/Filesystem.cpp
+++ b/wpilibc/src/main/native/cpp/Filesystem.cpp
@@ -13,7 +13,7 @@
 }
 
 std::string frc::filesystem::GetOperatingDirectory() {
-  if constexpr (RobotBase::IsReal()) {
+  if constexpr (!RobotBase::IsSimulation()) {
     return "/home/lvuser";
   } else {
     return frc::filesystem::GetLaunchDirectory();
@@ -21,7 +21,7 @@
 }
 
 std::string frc::filesystem::GetDeployDirectory() {
-  if constexpr (RobotBase::IsReal()) {
+  if constexpr (!RobotBase::IsSimulation()) {
     return "/home/lvuser/deploy";
   } else {
     return (fs::current_path() / "src" / "main" / "deploy").string();
diff --git a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
index 0bccb0b..ee941b6 100644
--- a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
+++ b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
@@ -24,6 +24,8 @@
 
 void IterativeRobotBase::RobotInit() {}
 
+void IterativeRobotBase::DriverStationConnected() {}
+
 void IterativeRobotBase::SimulationInit() {}
 
 void IterativeRobotBase::DisabledInit() {}
@@ -95,7 +97,7 @@
 }
 
 void IterativeRobotBase::EnableLiveWindowInTest(bool testLW) {
-  if (IsTest()) {
+  if (IsTestEnabled()) {
     throw FRC_MakeError(err::IncompatibleMode,
                         "Can't configure test mode while in test mode!");
   }
@@ -127,6 +129,11 @@
     mode = Mode::kTest;
   }
 
+  if (!m_calledDsConnected && word.IsDSAttached()) {
+    m_calledDsConnected = true;
+    DriverStationConnected();
+  }
+
   // If mode changed, call mode exit and entry functions
   if (m_lastMode != mode) {
     // Call last mode's exit function
diff --git a/wpilibc/src/main/native/cpp/Joystick.cpp b/wpilibc/src/main/native/cpp/Joystick.cpp
index 0eff226..8a378c5 100644
--- a/wpilibc/src/main/native/cpp/Joystick.cpp
+++ b/wpilibc/src/main/native/cpp/Joystick.cpp
@@ -8,6 +8,9 @@
 #include <numbers>
 
 #include <hal/FRCUsageReporting.h>
+#include <units/dimensionless.h>
+#include <units/math.h>
+#include <wpi/deprecated.h>
 
 #include "frc/event/BooleanEvent.h"
 
@@ -124,5 +127,12 @@
 }
 
 double Joystick::GetDirectionDegrees() const {
+  WPI_IGNORE_DEPRECATED
   return (180 / std::numbers::pi) * GetDirectionRadians();
+  WPI_UNIGNORE_DEPRECATED
+}
+
+units::radian_t Joystick::GetDirection() const {
+  return units::math::atan2(units::dimensionless::scalar_t{GetX()},
+                            units::dimensionless::scalar_t{-GetY()});
 }
diff --git a/wpilibc/src/main/native/cpp/Notifier.cpp b/wpilibc/src/main/native/cpp/Notifier.cpp
index d837752..147beb4 100644
--- a/wpilibc/src/main/native/cpp/Notifier.cpp
+++ b/wpilibc/src/main/native/cpp/Notifier.cpp
@@ -17,11 +17,11 @@
 
 using namespace frc;
 
-Notifier::Notifier(std::function<void()> handler) {
-  if (!handler) {
-    throw FRC_MakeError(err::NullParameter, "handler");
+Notifier::Notifier(std::function<void()> callback) {
+  if (!callback) {
+    throw FRC_MakeError(err::NullParameter, "callback");
   }
-  m_handler = handler;
+  m_callback = callback;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
   FRC_CheckErrorStatus(status, "InitializeNotifier");
@@ -38,32 +38,32 @@
         break;
       }
 
-      std::function<void()> handler;
+      std::function<void()> callback;
       {
         std::scoped_lock lock(m_processMutex);
-        handler = m_handler;
+        callback = m_callback;
         if (m_periodic) {
           m_expirationTime += m_period;
           UpdateAlarm();
         } else {
-          // need to update the alarm to cause it to wait again
+          // Need to update the alarm to cause it to wait again
           UpdateAlarm(UINT64_MAX);
         }
       }
 
-      // call callback
-      if (handler) {
-        handler();
+      // Call callback
+      if (callback) {
+        callback();
       }
     }
   });
 }
 
-Notifier::Notifier(int priority, std::function<void()> handler) {
-  if (!handler) {
-    throw FRC_MakeError(err::NullParameter, "handler");
+Notifier::Notifier(int priority, std::function<void()> callback) {
+  if (!callback) {
+    throw FRC_MakeError(err::NullParameter, "callback");
   }
-  m_handler = handler;
+  m_callback = callback;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
   FRC_CheckErrorStatus(status, "InitializeNotifier");
@@ -81,10 +81,10 @@
         break;
       }
 
-      std::function<void()> handler;
+      std::function<void()> callback;
       {
         std::scoped_lock lock(m_processMutex);
-        handler = m_handler;
+        callback = m_callback;
         if (m_periodic) {
           m_expirationTime += m_period;
           UpdateAlarm();
@@ -95,9 +95,9 @@
       }
 
       // call callback
-      if (handler) {
+      if (callback) {
         try {
-          handler();
+          callback();
         } catch (const frc::RuntimeError& e) {
           e.Report();
           FRC_ReportError(
@@ -123,7 +123,7 @@
   HAL_StopNotifier(handle, &status);
   FRC_ReportError(status, "StopNotifier");
 
-  // Join the thread to ensure the handler has exited.
+  // Join the thread to ensure the callback has exited.
   if (m_thread.joinable()) {
     m_thread.join();
   }
@@ -134,7 +134,7 @@
 Notifier::Notifier(Notifier&& rhs)
     : m_thread(std::move(rhs.m_thread)),
       m_notifier(rhs.m_notifier.load()),
-      m_handler(std::move(rhs.m_handler)),
+      m_callback(std::move(rhs.m_callback)),
       m_expirationTime(std::move(rhs.m_expirationTime)),
       m_period(std::move(rhs.m_period)),
       m_periodic(std::move(rhs.m_periodic)) {
@@ -145,7 +145,7 @@
   m_thread = std::move(rhs.m_thread);
   m_notifier = rhs.m_notifier.load();
   rhs.m_notifier = HAL_kInvalidHandle;
-  m_handler = std::move(rhs.m_handler);
+  m_callback = std::move(rhs.m_callback);
   m_expirationTime = std::move(rhs.m_expirationTime);
   m_period = std::move(rhs.m_period);
   m_periodic = std::move(rhs.m_periodic);
@@ -161,9 +161,14 @@
   HAL_SetNotifierName(m_notifier, buf.data(), &status);
 }
 
-void Notifier::SetHandler(std::function<void()> handler) {
+void Notifier::SetHandler(std::function<void()> callback) {
   std::scoped_lock lock(m_processMutex);
-  m_handler = handler;
+  m_callback = callback;
+}
+
+void Notifier::SetCallback(std::function<void()> callback) {
+  std::scoped_lock lock(m_processMutex);
+  m_callback = callback;
 }
 
 void Notifier::StartSingle(units::second_t delay) {
diff --git a/wpilibc/src/main/native/cpp/PS4Controller.cpp b/wpilibc/src/main/native/cpp/PS4Controller.cpp
index e59e18c..5ac3420 100644
--- a/wpilibc/src/main/native/cpp/PS4Controller.cpp
+++ b/wpilibc/src/main/native/cpp/PS4Controller.cpp
@@ -11,7 +11,8 @@
 using namespace frc;
 
 PS4Controller::PS4Controller(int port) : GenericHID(port) {
-  HAL_Report(HALUsageReporting::kResourceType_PS4Controller, port + 1);
+  // re-enable when PS4Controller is added to Usage Reporting
+  // HAL_Report(HALUsageReporting::kResourceType_PS4Controller, port + 1);
 }
 
 double PS4Controller::GetLeftX() const {
diff --git a/wpilibc/src/main/native/cpp/PS5Controller.cpp b/wpilibc/src/main/native/cpp/PS5Controller.cpp
new file mode 100644
index 0000000..95685d6
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PS5Controller.cpp
@@ -0,0 +1,263 @@
+// 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 "frc/PS5Controller.h"
+
+#include <hal/FRCUsageReporting.h>
+
+#include "frc/event/BooleanEvent.h"
+
+using namespace frc;
+
+PS5Controller::PS5Controller(int port) : GenericHID(port) {
+  // HAL_Report(HALUsageReporting::kResourceType_PS5Controller, port + 1);
+}
+
+double PS5Controller::GetLeftX() const {
+  return GetRawAxis(Axis::kLeftX);
+}
+
+double PS5Controller::GetRightX() const {
+  return GetRawAxis(Axis::kRightX);
+}
+
+double PS5Controller::GetLeftY() const {
+  return GetRawAxis(Axis::kLeftY);
+}
+
+double PS5Controller::GetRightY() const {
+  return GetRawAxis(Axis::kRightY);
+}
+
+double PS5Controller::GetL2Axis() const {
+  return GetRawAxis(Axis::kL2);
+}
+
+double PS5Controller::GetR2Axis() const {
+  return GetRawAxis(Axis::kR2);
+}
+
+bool PS5Controller::GetSquareButton() const {
+  return GetRawButton(Button::kSquare);
+}
+
+bool PS5Controller::GetSquareButtonPressed() {
+  return GetRawButtonPressed(Button::kSquare);
+}
+
+bool PS5Controller::GetSquareButtonReleased() {
+  return GetRawButtonReleased(Button::kSquare);
+}
+
+BooleanEvent PS5Controller::Square(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetSquareButton(); });
+}
+
+bool PS5Controller::GetCrossButton() const {
+  return GetRawButton(Button::kCross);
+}
+
+bool PS5Controller::GetCrossButtonPressed() {
+  return GetRawButtonPressed(Button::kCross);
+}
+
+bool PS5Controller::GetCrossButtonReleased() {
+  return GetRawButtonReleased(Button::kCross);
+}
+
+BooleanEvent PS5Controller::Cross(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCrossButton(); });
+}
+
+bool PS5Controller::GetCircleButton() const {
+  return GetRawButton(Button::kCircle);
+}
+
+bool PS5Controller::GetCircleButtonPressed() {
+  return GetRawButtonPressed(Button::kCircle);
+}
+
+bool PS5Controller::GetCircleButtonReleased() {
+  return GetRawButtonReleased(Button::kCircle);
+}
+
+BooleanEvent PS5Controller::Circle(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCircleButton(); });
+}
+
+bool PS5Controller::GetTriangleButton() const {
+  return GetRawButton(Button::kTriangle);
+}
+
+bool PS5Controller::GetTriangleButtonPressed() {
+  return GetRawButtonPressed(Button::kTriangle);
+}
+
+bool PS5Controller::GetTriangleButtonReleased() {
+  return GetRawButtonReleased(Button::kTriangle);
+}
+
+BooleanEvent PS5Controller::Triangle(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetTriangleButton(); });
+}
+
+bool PS5Controller::GetL1Button() const {
+  return GetRawButton(Button::kL1);
+}
+
+bool PS5Controller::GetL1ButtonPressed() {
+  return GetRawButtonPressed(Button::kL1);
+}
+
+bool PS5Controller::GetL1ButtonReleased() {
+  return GetRawButtonReleased(Button::kL1);
+}
+
+BooleanEvent PS5Controller::L1(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL1Button(); });
+}
+
+bool PS5Controller::GetR1Button() const {
+  return GetRawButton(Button::kR1);
+}
+
+bool PS5Controller::GetR1ButtonPressed() {
+  return GetRawButtonPressed(Button::kR1);
+}
+
+bool PS5Controller::GetR1ButtonReleased() {
+  return GetRawButtonReleased(Button::kR1);
+}
+
+BooleanEvent PS5Controller::R1(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR1Button(); });
+}
+
+bool PS5Controller::GetL2Button() const {
+  return GetRawButton(Button::kL2);
+}
+
+bool PS5Controller::GetL2ButtonPressed() {
+  return GetRawButtonPressed(Button::kL2);
+}
+
+bool PS5Controller::GetL2ButtonReleased() {
+  return GetRawButtonReleased(Button::kL2);
+}
+
+BooleanEvent PS5Controller::L2(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL2Button(); });
+}
+
+bool PS5Controller::GetR2Button() const {
+  return GetRawButton(Button::kR2);
+}
+
+bool PS5Controller::GetR2ButtonPressed() {
+  return GetRawButtonPressed(Button::kR2);
+}
+
+bool PS5Controller::GetR2ButtonReleased() {
+  return GetRawButtonReleased(Button::kR2);
+}
+
+BooleanEvent PS5Controller::R2(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR2Button(); });
+}
+
+bool PS5Controller::GetCreateButton() const {
+  return GetRawButton(Button::kCreate);
+}
+
+bool PS5Controller::GetCreateButtonPressed() {
+  return GetRawButtonPressed(Button::kCreate);
+}
+
+bool PS5Controller::GetCreateButtonReleased() {
+  return GetRawButtonReleased(Button::kCreate);
+}
+
+BooleanEvent PS5Controller::Create(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetCreateButton(); });
+}
+
+bool PS5Controller::GetOptionsButton() const {
+  return GetRawButton(Button::kOptions);
+}
+
+bool PS5Controller::GetOptionsButtonPressed() {
+  return GetRawButtonPressed(Button::kOptions);
+}
+
+bool PS5Controller::GetOptionsButtonReleased() {
+  return GetRawButtonReleased(Button::kOptions);
+}
+
+BooleanEvent PS5Controller::Options(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetOptionsButton(); });
+}
+
+bool PS5Controller::GetL3Button() const {
+  return GetRawButton(Button::kL3);
+}
+
+bool PS5Controller::GetL3ButtonPressed() {
+  return GetRawButtonPressed(Button::kL3);
+}
+
+bool PS5Controller::GetL3ButtonReleased() {
+  return GetRawButtonReleased(Button::kL3);
+}
+
+BooleanEvent PS5Controller::L3(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetL3Button(); });
+}
+
+bool PS5Controller::GetR3Button() const {
+  return GetRawButton(Button::kR3);
+}
+
+bool PS5Controller::GetR3ButtonPressed() {
+  return GetRawButtonPressed(Button::kR3);
+}
+
+bool PS5Controller::GetR3ButtonReleased() {
+  return GetRawButtonReleased(Button::kR3);
+}
+
+BooleanEvent PS5Controller::R3(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetR3Button(); });
+}
+
+bool PS5Controller::GetPSButton() const {
+  return GetRawButton(Button::kPS);
+}
+
+bool PS5Controller::GetPSButtonPressed() {
+  return GetRawButtonPressed(Button::kPS);
+}
+
+bool PS5Controller::GetPSButtonReleased() {
+  return GetRawButtonReleased(Button::kPS);
+}
+
+BooleanEvent PS5Controller::PS(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetPSButton(); });
+}
+
+bool PS5Controller::GetTouchpad() const {
+  return GetRawButton(Button::kTouchpad);
+}
+
+bool PS5Controller::GetTouchpadPressed() {
+  return GetRawButtonPressed(Button::kTouchpad);
+}
+
+bool PS5Controller::GetTouchpadReleased() {
+  return GetRawButtonReleased(Button::kTouchpad);
+}
+
+BooleanEvent PS5Controller::Touchpad(EventLoop* loop) const {
+  return BooleanEvent(loop, [this]() { return this->GetTouchpad(); });
+}
diff --git a/wpilibc/src/main/native/cpp/PWM.cpp b/wpilibc/src/main/native/cpp/PWM.cpp
index 5c5e6f5..6a4c0f5 100644
--- a/wpilibc/src/main/native/cpp/PWM.cpp
+++ b/wpilibc/src/main/native/cpp/PWM.cpp
@@ -54,18 +54,18 @@
   FRC_ReportError(status, "Channel {}", m_channel);
 }
 
-void PWM::SetRaw(uint16_t value) {
+void PWM::SetPulseTime(units::microsecond_t time) {
   int32_t status = 0;
-  HAL_SetPWMRaw(m_handle, value, &status);
+  HAL_SetPWMPulseTimeMicroseconds(m_handle, time.value(), &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-uint16_t PWM::GetRaw() const {
+units::microsecond_t PWM::GetPulseTime() const {
   int32_t status = 0;
-  uint16_t value = HAL_GetPWMRaw(m_handle, &status);
+  double value = HAL_GetPWMPulseTimeMicroseconds(m_handle, &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
-  return value;
+  return units::microsecond_t{value};
 }
 
 void PWM::SetPosition(double pos) {
@@ -135,27 +135,37 @@
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::SetBounds(double max, double deadbandMax, double center,
-                    double deadbandMin, double min) {
+void PWM::SetBounds(units::microsecond_t max, units::microsecond_t deadbandMax,
+                    units::microsecond_t center,
+                    units::microsecond_t deadbandMin,
+                    units::microsecond_t min) {
   int32_t status = 0;
-  HAL_SetPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min,
-                   &status);
+  HAL_SetPWMConfigMicroseconds(m_handle, max.value(), deadbandMax.value(),
+                               center.value(), deadbandMin.value(), min.value(),
+                               &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
-                       int min) {
+void PWM::GetBounds(units::microsecond_t* max,
+                    units::microsecond_t* deadbandMax,
+                    units::microsecond_t* center,
+                    units::microsecond_t* deadbandMin,
+                    units::microsecond_t* min) {
   int32_t status = 0;
-  HAL_SetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  int32_t rawMax, rawDeadbandMax, rawCenter, rawDeadbandMin, rawMin;
+  HAL_GetPWMConfigMicroseconds(m_handle, &rawMax, &rawDeadbandMax, &rawCenter,
+                               &rawDeadbandMin, &rawMin, &status);
+  *max = units::microsecond_t{static_cast<double>(rawMax)};
+  *deadbandMax = units::microsecond_t{static_cast<double>(rawDeadbandMax)};
+  *center = units::microsecond_t{static_cast<double>(rawCenter)};
+  *deadbandMin = units::microsecond_t{static_cast<double>(rawDeadbandMin)};
+  *min = units::microsecond_t{static_cast<double>(rawMin)};
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-void PWM::GetRawBounds(int* max, int* deadbandMax, int* center,
-                       int* deadbandMin, int* min) {
+void PWM::SetAlwaysHighMode() {
   int32_t status = 0;
-  HAL_GetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
-                      &status);
+  HAL_SetPWMAlwaysHighMode(m_handle, &status);
   FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
@@ -168,6 +178,12 @@
   builder.SetActuator(true);
   builder.SetSafeState([=, this] { SetDisabled(); });
   builder.AddDoubleProperty(
-      "Value", [=, this] { return GetRaw(); },
-      [=, this](double value) { SetRaw(value); });
+      "Value", [=, this] { return GetPulseTime().value(); },
+      [=, this](double value) { SetPulseTime(units::millisecond_t{value}); });
+  builder.AddDoubleProperty(
+      "Speed", [=, this] { return GetSpeed(); },
+      [=, this](double value) { SetSpeed(value); });
+  builder.AddDoubleProperty(
+      "Position", [=, this] { return GetPosition(); },
+      [=, this](double value) { SetPosition(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/PneumaticHub.cpp b/wpilibc/src/main/native/cpp/PneumaticHub.cpp
index ec008cf..bcbbd55 100644
--- a/wpilibc/src/main/native/cpp/PneumaticHub.cpp
+++ b/wpilibc/src/main/native/cpp/PneumaticHub.cpp
@@ -17,7 +17,6 @@
 #include "frc/RobotBase.h"
 #include "frc/SensorUtil.h"
 #include "frc/Solenoid.h"
-#include "frc/fmt/Units.h"
 
 using namespace frc;
 
@@ -161,9 +160,14 @@
                         "maxPressure must be between 0 and 120 PSI, got {}",
                         maxPressure);
   }
-  int32_t status = 0;
+
+  // Send the voltage as it would be if the 5V rail was at exactly 5V.
+  // The firmware will compensate for the real 5V rail voltage, which
+  // can fluctuate somewhat over time.
   units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
   units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
+
+  int32_t status = 0;
   HAL_SetREVPHClosedLoopControlAnalog(m_handle, minAnalogVoltage.value(),
                                       maxAnalogVoltage.value(), &status);
   FRC_ReportError(status, "Module {}", m_module);
@@ -186,9 +190,14 @@
                         "maxPressure must be between 0 and 120 PSI, got {}",
                         maxPressure);
   }
-  int32_t status = 0;
+
+  // Send the voltage as it would be if the 5V rail was at exactly 5V.
+  // The firmware will compensate for the real 5V rail voltage, which
+  // can fluctuate somewhat over time.
   units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
   units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
+
+  int32_t status = 0;
   HAL_SetREVPHClosedLoopControlHybrid(m_handle, minAnalogVoltage.value(),
                                       maxAnalogVoltage.value(), &status);
   FRC_ReportError(status, "Module {}", m_module);
diff --git a/wpilibc/src/main/native/cpp/RobotController.cpp b/wpilibc/src/main/native/cpp/RobotController.cpp
index da6e364..8b2b496 100644
--- a/wpilibc/src/main/native/cpp/RobotController.cpp
+++ b/wpilibc/src/main/native/cpp/RobotController.cpp
@@ -41,6 +41,10 @@
   return std::string(comments, len);
 }
 
+int32_t RobotController::GetTeamNumber() {
+  return HAL_GetTeamNumber();
+}
+
 uint64_t RobotController::GetFPGATime() {
   int32_t status = 0;
   uint64_t time = HAL_GetFPGATime(&status);
@@ -76,6 +80,20 @@
   return retVal;
 }
 
+bool RobotController::GetRSLState() {
+  int32_t status = 0;
+  bool retVal = HAL_GetRSLState(&status);
+  FRC_CheckErrorStatus(status, "GetRSLState");
+  return retVal;
+}
+
+bool RobotController::IsSystemTimeValid() {
+  int32_t status = 0;
+  bool retVal = HAL_GetSystemTimeValid(&status);
+  FRC_CheckErrorStatus(status, "IsSystemTimeValid");
+  return retVal;
+}
+
 double RobotController::GetInputVoltage() {
   int32_t status = 0;
   double retVal = HAL_GetVinVoltage(&status);
@@ -104,6 +122,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled3V3(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled3V3(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled3V3");
+}
+
 bool RobotController::GetEnabled3V3() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive3V3(&status);
@@ -132,6 +156,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled5V(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled5V(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled5V");
+}
+
 bool RobotController::GetEnabled5V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive5V(&status);
@@ -160,6 +190,12 @@
   return retVal;
 }
 
+void RobotController::SetEnabled6V(bool enabled) {
+  int32_t status = 0;
+  HAL_SetUserRailEnabled6V(enabled, &status);
+  FRC_CheckErrorStatus(status, "SetEnabled6V");
+}
+
 bool RobotController::GetEnabled6V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive6V(&status);
@@ -187,6 +223,13 @@
   FRC_CheckErrorStatus(status, "SetBrownoutVoltage");
 }
 
+units::celsius_t RobotController::GetCPUTemp() {
+  int32_t status = 0;
+  double retVal = HAL_GetCPUTemp(&status);
+  FRC_CheckErrorStatus(status, "GetCPUTemp");
+  return units::celsius_t{retVal};
+}
+
 CANStatus RobotController::GetCANStatus() {
   int32_t status = 0;
   float percentBusUtilization = 0;
diff --git a/wpilibc/src/main/native/cpp/SPI.cpp b/wpilibc/src/main/native/cpp/SPI.cpp
index 29cd006..26e26b7 100644
--- a/wpilibc/src/main/native/cpp/SPI.cpp
+++ b/wpilibc/src/main/native/cpp/SPI.cpp
@@ -177,44 +177,6 @@
   HAL_SetSPISpeed(m_port, hz);
 }
 
-void SPI::SetMSBFirst() {
-  FRC_ReportError(1, "SetMSBFirst not supported by roboRIO {}",
-                  static_cast<int>(m_port));
-}
-
-void SPI::SetLSBFirst() {
-  FRC_ReportError(1, "SetLSBFirst not supported by roboRIO {}",
-                  static_cast<int>(m_port));
-}
-
-void SPI::SetSampleDataOnLeadingEdge() {
-  int mode = m_mode;
-  mode &= 2;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetSampleDataOnTrailingEdge() {
-  int mode = m_mode;
-  mode |= 2;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetClockActiveLow() {
-  int mode = m_mode;
-  mode |= 1;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
-void SPI::SetClockActiveHigh() {
-  int mode = m_mode;
-  mode &= 1;
-  m_mode = static_cast<HAL_SPIMode>(mode);
-  HAL_SetSPIMode(m_port, m_mode);
-}
-
 void SPI::SetMode(Mode mode) {
   m_mode = static_cast<HAL_SPIMode>(mode & 0x3);
   HAL_SetSPIMode(m_port, m_mode);
diff --git a/wpilibc/src/main/native/cpp/Servo.cpp b/wpilibc/src/main/native/cpp/Servo.cpp
index 4a292b9..79f7f54 100644
--- a/wpilibc/src/main/native/cpp/Servo.cpp
+++ b/wpilibc/src/main/native/cpp/Servo.cpp
@@ -13,12 +13,12 @@
 constexpr double Servo::kMaxServoAngle;
 constexpr double Servo::kMinServoAngle;
 
-constexpr double Servo::kDefaultMaxServoPWM;
-constexpr double Servo::kDefaultMinServoPWM;
+constexpr units::millisecond_t Servo::kDefaultMaxServoPWM;
+constexpr units::millisecond_t Servo::kDefaultMinServoPWM;
 
 Servo::Servo(int channel) : PWM(channel) {
   // Set minimum and maximum PWM values supported by the servo
-  SetBounds(kDefaultMaxServoPWM, 0.0, 0.0, 0.0, kDefaultMinServoPWM);
+  SetBounds(kDefaultMaxServoPWM, 0.0_ms, 0.0_ms, 0.0_ms, kDefaultMinServoPWM);
 
   // Assign defaults for period multiplier for the servo PWM control signal
   SetPeriodMultiplier(kPeriodMultiplier_4X);
@@ -32,7 +32,7 @@
 }
 
 void Servo::SetOffline() {
-  SetRaw(0);
+  SetDisabled();
 }
 
 double Servo::Get() const {
diff --git a/wpilibc/src/main/native/cpp/Timer.cpp b/wpilibc/src/main/native/cpp/Timer.cpp
index 3863de4..dfad620 100644
--- a/wpilibc/src/main/native/cpp/Timer.cpp
+++ b/wpilibc/src/main/native/cpp/Timer.cpp
@@ -54,6 +54,14 @@
   }
 }
 
+void Timer::Restart() {
+  if (m_running) {
+    Stop();
+  }
+  Reset();
+  Start();
+}
+
 void Timer::Stop() {
   if (m_running) {
     m_accumulatedTime = Get();
@@ -82,5 +90,5 @@
 }
 
 units::second_t Timer::GetMatchTime() {
-  return units::second_t{frc::DriverStation::GetMatchTime()};
+  return frc::DriverStation::GetMatchTime();
 }
diff --git a/wpilibc/src/main/native/cpp/TimesliceRobot.cpp b/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
index d212c10..b817aa9 100644
--- a/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
+++ b/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
@@ -5,7 +5,6 @@
 #include "frc/TimesliceRobot.h"
 
 #include "frc/Errors.h"
-#include "frc/fmt/Units.h"
 
 using namespace frc;
 
diff --git a/wpilibc/src/main/native/cpp/Ultrasonic.cpp b/wpilibc/src/main/native/cpp/Ultrasonic.cpp
index a034fc3..ec4c9c3 100644
--- a/wpilibc/src/main/native/cpp/Ultrasonic.cpp
+++ b/wpilibc/src/main/native/cpp/Ultrasonic.cpp
@@ -85,10 +85,7 @@
 }
 
 void Ultrasonic::Ping() {
-  if (m_automaticEnabled) {
-    throw FRC_MakeError(err::IncompatibleMode,
-                        "cannot call Ping() in automatic mode");
-  }
+  SetAutomaticMode(false);  // turn off automatic round-robin if pinging
 
   // Reset the counter to zero (invalid data now)
   m_counter.Reset();
@@ -138,7 +135,7 @@
 units::meter_t Ultrasonic::GetRange() const {
   if (IsRangeValid()) {
     if (m_simRange) {
-      return units::meter_t{m_simRange.Get()};
+      return units::inch_t{m_simRange.Get()};
     }
     return m_counter.GetPeriod() * kSpeedOfSound / 2.0;
   } else {
diff --git a/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp b/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
index 5b8ce63..fd9c831 100644
--- a/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
+++ b/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp
@@ -7,56 +7,61 @@
 using namespace frc;
 
 BooleanEvent::BooleanEvent(EventLoop* loop, std::function<bool()> condition)
-    : m_loop(loop), m_condition(std::move(condition)) {}
+    : m_loop(loop), m_condition(std::move(condition)) {
+  m_state = std::make_shared<bool>(m_condition());
+  m_loop->Bind(
+      // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+      [condition = m_condition, state = m_state] { *state = condition(); });
+}
 
 BooleanEvent::operator std::function<bool()>() {
-  return m_condition;
+  return [state = m_state] { return *state; };
 }
 
 bool BooleanEvent::GetAsBoolean() const {
-  return m_condition();
+  return *m_state;
 }
 
 void BooleanEvent::IfHigh(std::function<void()> action) {
-  m_loop->Bind([condition = m_condition, action = std::move(action)] {
-    if (condition()) {
+  m_loop->Bind([state = m_state, action = std::move(action)] {
+    if (*state) {
       action();
     }
   });
 }
 
 BooleanEvent BooleanEvent::operator!() {
-  return BooleanEvent(this->m_loop, [lhs = m_condition] { return !lhs(); });
+  return BooleanEvent(this->m_loop, [state = m_state] { return !*state; });
 }
 
 BooleanEvent BooleanEvent::operator&&(std::function<bool()> rhs) {
   return BooleanEvent(this->m_loop,
-                      [lhs = m_condition, rhs] { return lhs() && rhs(); });
+                      [state = m_state, rhs] { return *state && rhs(); });
 }
 
 BooleanEvent BooleanEvent::operator||(std::function<bool()> rhs) {
   return BooleanEvent(this->m_loop,
-                      [lhs = m_condition, rhs] { return lhs() || rhs(); });
+                      [state = m_state, rhs] { return *state || rhs(); });
 }
 
 BooleanEvent BooleanEvent::Rising() {
-  return BooleanEvent(
-      this->m_loop, [lhs = m_condition, m_previous = m_condition()]() mutable {
-        bool present = lhs();
-        bool past = m_previous;
-        m_previous = present;
-        return !past && present;
-      });
+  return BooleanEvent(this->m_loop,
+                      [state = m_state, m_previous = *m_state]() mutable {
+                        bool present = *state;
+                        bool past = m_previous;
+                        m_previous = present;
+                        return !past && present;
+                      });
 }
 
 BooleanEvent BooleanEvent::Falling() {
-  return BooleanEvent(
-      this->m_loop, [lhs = m_condition, m_previous = m_condition()]() mutable {
-        bool present = lhs();
-        bool past = m_previous;
-        m_previous = present;
-        return past && !present;
-      });
+  return BooleanEvent(this->m_loop,
+                      [state = m_state, m_previous = *m_state]() mutable {
+                        bool present = *state;
+                        bool past = m_previous;
+                        m_previous = present;
+                        return past && !present;
+                      });
 }
 
 BooleanEvent BooleanEvent::Debounce(units::second_t debounceTime,
@@ -64,5 +69,5 @@
   return BooleanEvent(
       this->m_loop,
       [debouncer = frc::Debouncer(debounceTime, type),
-       lhs = m_condition]() mutable { return debouncer.Calculate(lhs()); });
+       state = m_state]() mutable { return debouncer.Calculate(*state); });
 }
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
index 81fa868..ec13698 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 DMC60::DMC60(int channel) : PWMMotorController("DMC60", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
index c68ae0c..a152566 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Jaguar::Jaguar(int channel) : PWMMotorController("Jaguar", channel) {
-  m_pwm.SetBounds(2.31, 1.55, 1.507, 1.454, 0.697);
+  m_pwm.SetBounds(2.31_ms, 1.55_ms, 1.507_ms, 1.454_ms, 0.697_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
index 01c70d0..f25aa91 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
@@ -30,7 +30,7 @@
   if (!m_disabled) {
     m_speed = speed;
     m_dio.UpdateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
-    m_pwm.SetRaw(0xffff);
+    m_pwm.SetAlwaysHighMode();
   }
   Feed();
 }
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
index 608d452..56c3a45 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
@@ -10,7 +10,7 @@
 
 PWMSparkMax::PWMSparkMax(int channel)
     : PWMMotorController("PWMSparkMax", channel) {
-  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetBounds(2.003_ms, 1.55_ms, 1.50_ms, 1.46_ms, 0.999_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
index 2c6982b..51327b0 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
@@ -10,7 +10,7 @@
 
 PWMTalonFX::PWMTalonFX(int channel)
     : PWMMotorController("PWMTalonFX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
index b253412..ee579ed 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
@@ -10,7 +10,7 @@
 
 PWMTalonSRX::PWMTalonSRX(int channel)
     : PWMMotorController("PWMTalonSRX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
index e558028..24041c8 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 PWMVenom::PWMVenom(int channel) : PWMMotorController("PWMVenom", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
index 10ce992..4aeb399 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
@@ -10,7 +10,7 @@
 
 PWMVictorSPX::PWMVictorSPX(int channel)
     : PWMMotorController("PWMVictorSPX", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
index 3d5738f..7b6b838 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 SD540::SD540(int channel) : PWMMotorController("SD540", channel) {
-  m_pwm.SetBounds(2.05, 1.55, 1.50, 1.44, 0.94);
+  m_pwm.SetBounds(2.05_ms, 1.55_ms, 1.50_ms, 1.44_ms, 0.94_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
index 45394df..05ecb89 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Spark::Spark(int channel) : PWMMotorController("Spark", channel) {
-  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetBounds(2.003_ms, 1.55_ms, 1.50_ms, 1.46_ms, 0.999_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
index f4b3b69..4994baf 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Talon::Talon(int channel) : PWMMotorController("Talon", channel) {
-  m_pwm.SetBounds(2.037, 1.539, 1.513, 1.487, 0.989);
+  m_pwm.SetBounds(2.037_ms, 1.539_ms, 1.513_ms, 1.487_ms, 0.989_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
index 3ad29f7..a05900f 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 Victor::Victor(int channel) : PWMMotorController("Victor", channel) {
-  m_pwm.SetBounds(2.027, 1.525, 1.507, 1.49, 1.026);
+  m_pwm.SetBounds(2.027_ms, 1.525_ms, 1.507_ms, 1.49_ms, 1.026_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_2X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
index 6dc888e..27419fb 100644
--- a/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
+++ b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
@@ -9,7 +9,7 @@
 using namespace frc;
 
 VictorSP::VictorSP(int channel) : PWMMotorController("VictorSP", channel) {
-  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetBounds(2.004_ms, 1.52_ms, 1.50_ms, 1.48_ms, 0.997_ms);
   m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
   m_pwm.SetSpeed(0.0);
   m_pwm.SetZeroLatch();
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
index a315b90..a19cc6a 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
@@ -20,12 +20,16 @@
   bool tabsChanged = false;
   std::shared_ptr<nt::NetworkTable> rootTable;
   std::shared_ptr<nt::NetworkTable> rootMetaTable;
+  nt::StringPublisher selectedTabPub;
 };
 
 ShuffleboardInstance::ShuffleboardInstance(nt::NetworkTableInstance ntInstance)
     : m_impl(new Impl) {
   m_impl->rootTable = ntInstance.GetTable(Shuffleboard::kBaseTableName);
   m_impl->rootMetaTable = m_impl->rootTable->GetSubTable(".metadata");
+  m_impl->selectedTabPub =
+      m_impl->rootMetaTable->GetStringTopic("Selected")
+          .Publish(nt::PubSubOptions{.keepDuplicates = true});
   HAL_Report(HALUsageReporting::kResourceType_Shuffleboard, 0);
 }
 
@@ -75,9 +79,9 @@
 }
 
 void ShuffleboardInstance::SelectTab(int index) {
-  m_impl->rootMetaTable->GetEntry("Selected").SetDouble(index);
+  m_impl->selectedTabPub.Set(std::to_string(index));
 }
 
 void ShuffleboardInstance::SelectTab(std::string_view title) {
-  m_impl->rootMetaTable->GetEntry("Selected").SetString(title);
+  m_impl->selectedTabPub.Set(title);
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp b/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
index bda2020..c0448ea 100644
--- a/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DCMotorSim.cpp
@@ -24,6 +24,11 @@
     : DCMotorSim(LinearSystemId::DCMotorSystem(gearbox, moi, gearing), gearbox,
                  gearing, measurementStdDevs) {}
 
+void DCMotorSim::SetState(units::radian_t angularPosition,
+                          units::radians_per_second_t angularVelocity) {
+  SetState(Vectord<2>{angularPosition, angularVelocity});
+}
+
 units::radian_t DCMotorSim::GetAngularPosition() const {
   return units::radian_t{GetOutput(0)};
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp b/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
index b6c95dc..8c27cdf 100644
--- a/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
@@ -41,7 +41,8 @@
               driveMotor, mass, wheelRadius, trackWidth / 2.0, J, gearing),
           trackWidth, driveMotor, gearing, wheelRadius, measurementStdDevs) {}
 
-Vectord<2> DifferentialDrivetrainSim::ClampInput(const Vectord<2>& u) {
+Eigen::Vector2d DifferentialDrivetrainSim::ClampInput(
+    const Eigen::Vector2d& u) {
   return frc::DesaturateInputVector<2>(u,
                                        frc::RobotController::GetInputVoltage());
 }
@@ -128,7 +129,7 @@
 }
 
 Vectord<7> DifferentialDrivetrainSim::Dynamics(const Vectord<7>& x,
-                                               const Vectord<2>& u) {
+                                               const Eigen::Vector2d& u) {
   // Because G² can be factored out of A, we can divide by the old ratio
   // squared and multiply by the new ratio squared to get a new drivetrain
   // model.
diff --git a/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp b/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
index cb83ccb..3df775d 100644
--- a/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
@@ -9,17 +9,49 @@
 
 using namespace frc::sim;
 
-DutyCycleEncoderSim::DutyCycleEncoderSim(const frc::DutyCycleEncoder& encoder) {
-  frc::sim::SimDeviceSim deviceSim{"DutyCycle:DutyCycleEncoder",
-                                   encoder.GetSourceChannel()};
+DutyCycleEncoderSim::DutyCycleEncoderSim(const frc::DutyCycleEncoder& encoder)
+    : DutyCycleEncoderSim{encoder.GetSourceChannel()} {}
+
+DutyCycleEncoderSim::DutyCycleEncoderSim(int channel) {
+  frc::sim::SimDeviceSim deviceSim{"DutyCycle:DutyCycleEncoder", channel};
   m_simPosition = deviceSim.GetDouble("position");
   m_simDistancePerRotation = deviceSim.GetDouble("distance_per_rot");
+  m_simAbsolutePosition = deviceSim.GetDouble("absPosition");
+  m_simIsConnected = deviceSim.GetBoolean("connected");
+}
+
+double DutyCycleEncoderSim::Get() {
+  return m_simPosition.Get();
 }
 
 void DutyCycleEncoderSim::Set(units::turn_t turns) {
   m_simPosition.Set(turns.value());
 }
 
+double DutyCycleEncoderSim::GetDistance() {
+  return m_simPosition.Get() * m_simDistancePerRotation.Get();
+}
+
 void DutyCycleEncoderSim::SetDistance(double distance) {
   m_simPosition.Set(distance / m_simDistancePerRotation.Get());
 }
+
+double DutyCycleEncoderSim::GetAbsolutePosition() {
+  return m_simAbsolutePosition.Get();
+}
+
+void DutyCycleEncoderSim::SetAbsolutePosition(double position) {
+  m_simAbsolutePosition.Set(position);
+}
+
+double DutyCycleEncoderSim::GetDistancePerRotation() {
+  return m_simDistancePerRotation.Get();
+}
+
+bool DutyCycleEncoderSim::IsConnected() {
+  return m_simIsConnected.Get();
+}
+
+void DutyCycleEncoderSim::SetConnected(bool isConnected) {
+  m_simIsConnected.Set(isConnected);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp b/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
index 529fb1a..0281730 100644
--- a/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
@@ -13,39 +13,54 @@
 using namespace frc::sim;
 
 ElevatorSim::ElevatorSim(const LinearSystem<2, 1, 1>& plant,
-                         const DCMotor& gearbox, double gearing,
-                         units::meter_t drumRadius, units::meter_t minHeight,
+                         const DCMotor& gearbox, units::meter_t minHeight,
                          units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
                          const std::array<double, 1>& measurementStdDevs)
     : LinearSystemSim(plant, measurementStdDevs),
       m_gearbox(gearbox),
-      m_drumRadius(drumRadius),
       m_minHeight(minHeight),
       m_maxHeight(maxHeight),
-      m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+      m_simulateGravity(simulateGravity) {
+  SetState(startingHeight, 0_mps);
+}
 
 ElevatorSim::ElevatorSim(const DCMotor& gearbox, double gearing,
                          units::kilogram_t carriageMass,
                          units::meter_t drumRadius, units::meter_t minHeight,
                          units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
                          const std::array<double, 1>& measurementStdDevs)
-    : LinearSystemSim(LinearSystemId::ElevatorSystem(gearbox, carriageMass,
-                                                     drumRadius, gearing),
-                      measurementStdDevs),
-      m_gearbox(gearbox),
-      m_drumRadius(drumRadius),
-      m_minHeight(minHeight),
-      m_maxHeight(maxHeight),
-      m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+    : ElevatorSim(LinearSystemId::ElevatorSystem(gearbox, carriageMass,
+                                                 drumRadius, gearing),
+                  gearbox, minHeight, maxHeight, simulateGravity,
+                  startingHeight, measurementStdDevs) {}
+
+template <typename Distance>
+  requires std::same_as<units::meter, Distance> ||
+           std::same_as<units::radian, Distance>
+ElevatorSim::ElevatorSim(decltype(1_V / Velocity_t<Distance>(1)) kV,
+                         decltype(1_V / Acceleration_t<Distance>(1)) kA,
+                         const DCMotor& gearbox, units::meter_t minHeight,
+                         units::meter_t maxHeight, bool simulateGravity,
+                         units::meter_t startingHeight,
+                         const std::array<double, 1>& measurementStdDevs)
+    : ElevatorSim(LinearSystemId::IdentifyPositionSystem(kV, kA), gearbox,
+                  minHeight, maxHeight, simulateGravity, startingHeight,
+                  measurementStdDevs) {}
+
+void ElevatorSim::SetState(units::meter_t position,
+                           units::meters_per_second_t velocity) {
+  SetState(
+      Vectord<2>{std::clamp(position, m_minHeight, m_maxHeight), velocity});
+}
 
 bool ElevatorSim::WouldHitLowerLimit(units::meter_t elevatorHeight) const {
-  return elevatorHeight < m_minHeight;
+  return elevatorHeight <= m_minHeight;
 }
 
 bool ElevatorSim::WouldHitUpperLimit(units::meter_t elevatorHeight) const {
-  return elevatorHeight > m_maxHeight;
+  return elevatorHeight >= m_maxHeight;
 }
 
 bool ElevatorSim::HasHitLowerLimit() const {
@@ -69,10 +84,12 @@
   // Reductions are greater than 1, so a reduction of 10:1 would mean the motor
   // is spinning 10x faster than the output.
 
-  // v = r w, so w = v / r
+  double kA = 1.0 / m_plant.B(1, 0);
+  using Kv_t = units::unit_t<units::compound_unit<
+      units::volt, units::inverse<units::meters_per_second>>>;
+  Kv_t Kv = Kv_t{kA * m_plant.A(1, 1)};
   units::meters_per_second_t velocity{m_x(1)};
-  units::radians_per_second_t motorVelocity =
-      velocity / m_drumRadius * m_gearing * 1_rad;
+  units::radians_per_second_t motorVelocity = velocity * Kv * m_gearbox.Kv;
 
   // Perform calculation and return.
   return m_gearbox.Current(motorVelocity, units::volt_t{m_u(0)}) *
diff --git a/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp b/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
index 146328d..9709b4c 100644
--- a/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
@@ -191,7 +191,7 @@
   HALSIM_SetEncoderDistance(m_index, distance);
 }
 
-double EncoderSim::GetDistance() {
+double EncoderSim::GetDistance() const {
   return HALSIM_GetEncoderDistance(m_index);
 }
 
@@ -199,6 +199,6 @@
   HALSIM_SetEncoderRate(m_index, rate);
 }
 
-double EncoderSim::GetRate() {
+double EncoderSim::GetRate() const {
   return HALSIM_GetEncoderRate(m_index);
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp b/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
index 95dcb9e..f4b9a81 100644
--- a/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
@@ -24,6 +24,10 @@
     : FlywheelSim(LinearSystemId::FlywheelSystem(gearbox, moi, gearing),
                   gearbox, gearing, measurementStdDevs) {}
 
+void FlywheelSim::SetState(units::radians_per_second_t velocity) {
+  LinearSystemSim::SetState(Vectord<1>{velocity.value()});
+}
+
 units::radians_per_second_t FlywheelSim::GetAngularVelocity() const {
   return units::radians_per_second_t{GetOutput(0)};
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp b/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp
new file mode 100644
index 0000000..e20105c
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/PS5ControllerSim.cpp
@@ -0,0 +1,103 @@
+// 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 "frc/simulation/PS5ControllerSim.h"
+
+#include "frc/PS5Controller.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+PS5ControllerSim::PS5ControllerSim(const PS5Controller& joystick)
+    : GenericHIDSim{joystick} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+  SetPOVCount(1);
+}
+
+PS5ControllerSim::PS5ControllerSim(int port) : GenericHIDSim{port} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+  SetPOVCount(1);
+}
+
+void PS5ControllerSim::SetLeftX(double value) {
+  SetRawAxis(PS5Controller::Axis::kLeftX, value);
+}
+
+void PS5ControllerSim::SetRightX(double value) {
+  SetRawAxis(PS5Controller::Axis::kRightX, value);
+}
+
+void PS5ControllerSim::SetLeftY(double value) {
+  SetRawAxis(PS5Controller::Axis::kLeftY, value);
+}
+
+void PS5ControllerSim::SetRightY(double value) {
+  SetRawAxis(PS5Controller::Axis::kRightY, value);
+}
+
+void PS5ControllerSim::SetL2Axis(double value) {
+  SetRawAxis(PS5Controller::Axis::kL2, value);
+}
+
+void PS5ControllerSim::SetR2Axis(double value) {
+  SetRawAxis(PS5Controller::Axis::kR2, value);
+}
+
+void PS5ControllerSim::SetSquareButton(bool value) {
+  SetRawButton(PS5Controller::Button::kSquare, value);
+}
+
+void PS5ControllerSim::SetCrossButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCross, value);
+}
+
+void PS5ControllerSim::SetCircleButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCircle, value);
+}
+
+void PS5ControllerSim::SetTriangleButton(bool value) {
+  SetRawButton(PS5Controller::Button::kTriangle, value);
+}
+
+void PS5ControllerSim::SetL1Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL1, value);
+}
+
+void PS5ControllerSim::SetR1Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR1, value);
+}
+
+void PS5ControllerSim::SetL2Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL2, value);
+}
+
+void PS5ControllerSim::SetR2Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR2, value);
+}
+
+void PS5ControllerSim::SetCreateButton(bool value) {
+  SetRawButton(PS5Controller::Button::kCreate, value);
+}
+
+void PS5ControllerSim::SetOptionsButton(bool value) {
+  SetRawButton(PS5Controller::Button::kOptions, value);
+}
+
+void PS5ControllerSim::SetL3Button(bool value) {
+  SetRawButton(PS5Controller::Button::kL3, value);
+}
+
+void PS5ControllerSim::SetR3Button(bool value) {
+  SetRawButton(PS5Controller::Button::kR3, value);
+}
+
+void PS5ControllerSim::SetPSButton(bool value) {
+  SetRawButton(PS5Controller::Button::kPS, value);
+}
+
+void PS5ControllerSim::SetTouchpad(bool value) {
+  SetRawButton(PS5Controller::Button::kTouchpad, value);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp b/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
index 3fafa8e..9332c78 100644
--- a/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
@@ -39,21 +39,21 @@
   HALSIM_SetPWMInitialized(m_index, initialized);
 }
 
-std::unique_ptr<CallbackStore> PWMSim::RegisterRawValueCallback(
+std::unique_ptr<CallbackStore> PWMSim::RegisterPulseMicrosecondCallback(
     NotifyCallback callback, bool initialNotify) {
   auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPWMRawValueCallback);
-  store->SetUid(HALSIM_RegisterPWMRawValueCallback(m_index, &CallbackStoreThunk,
-                                                   store.get(), initialNotify));
+      m_index, -1, callback, &HALSIM_CancelPWMPulseMicrosecondCallback);
+  store->SetUid(HALSIM_RegisterPWMPulseMicrosecondCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
   return store;
 }
 
-int PWMSim::GetRawValue() const {
-  return HALSIM_GetPWMRawValue(m_index);
+int32_t PWMSim::GetPulseMicrosecond() const {
+  return HALSIM_GetPWMPulseMicrosecond(m_index);
 }
 
-void PWMSim::SetRawValue(int rawValue) {
-  HALSIM_SetPWMRawValue(m_index, rawValue);
+void PWMSim::SetPulseMicrosecond(int32_t microsecondPulseTime) {
+  HALSIM_SetPWMPulseMicrosecond(m_index, microsecondPulseTime);
 }
 
 std::unique_ptr<CallbackStore> PWMSim::RegisterSpeedCallback(
diff --git a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
index 6d0f809..90d9651 100644
--- a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
@@ -284,6 +284,40 @@
   HALSIM_SetRoboRioBrownoutVoltage(vInVoltage.value());
 }
 
+std::unique_ptr<CallbackStore> RoboRioSim::RegisterCPUTempCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      -1, callback, &HALSIM_CancelRoboRioCPUTempCallback);
+  store->SetUid(HALSIM_RegisterRoboRioCPUTempCallback(
+      &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+units::celsius_t RoboRioSim::GetCPUTemp() {
+  return units::celsius_t{HALSIM_GetRoboRioCPUTemp()};
+}
+
+void RoboRioSim::SetCPUTemp(units::celsius_t cpuTemp) {
+  HALSIM_SetRoboRioCPUTemp(cpuTemp.value());
+}
+
+std::unique_ptr<CallbackStore> RoboRioSim::RegisterTeamNumberCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      -1, callback, &HALSIM_CancelRoboRioTeamNumberCallback);
+  store->SetUid(HALSIM_RegisterRoboRioTeamNumberCallback(
+      &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+int32_t RoboRioSim::GetTeamNumber() {
+  return HALSIM_GetRoboRioTeamNumber();
+}
+
+void RoboRioSim::SetTeamNumber(int32_t teamNumber) {
+  HALSIM_SetRoboRioTeamNumber(teamNumber);
+}
+
 std::string RoboRioSim::GetSerialNumber() {
   char serialNum[9];
   size_t len = HALSIM_GetRoboRioSerialNumber(serialNum, sizeof(serialNum));
diff --git a/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp b/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
index 34fd1e3..70e43e7 100644
--- a/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
@@ -27,6 +27,12 @@
       fmt::format("{}[{},{}]", name, index, channel).c_str());
 }
 
+SimDeviceSim::SimDeviceSim(HAL_SimDeviceHandle handle) : m_handle(handle) {}
+
+std::string SimDeviceSim::GetName() const {
+  return std::string(HALSIM_GetSimDeviceName(m_handle));
+}
+
 hal::SimValue SimDeviceSim::GetValue(const char* name) const {
   return HALSIM_GetSimValueHandle(m_handle, name);
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp b/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
index 88e45c6..d9969ca 100644
--- a/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
@@ -18,26 +18,34 @@
 SingleJointedArmSim::SingleJointedArmSim(
     const LinearSystem<2, 1, 1>& system, const DCMotor& gearbox, double gearing,
     units::meter_t armLength, units::radian_t minAngle,
-    units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
+    units::radian_t maxAngle, bool simulateGravity,
+    units::radian_t startingAngle,
     const std::array<double, 1>& measurementStdDevs)
     : LinearSystemSim<2, 1, 1>(system, measurementStdDevs),
-      m_r(armLength),
+      m_armLen(armLength),
       m_minAngle(minAngle),
       m_maxAngle(maxAngle),
-      m_armMass(armMass),
       m_gearbox(gearbox),
       m_gearing(gearing),
-      m_simulateGravity(simulateGravity) {}
+      m_simulateGravity(simulateGravity) {
+  SetState(startingAngle, 0_rad_per_s);
+}
 
 SingleJointedArmSim::SingleJointedArmSim(
     const DCMotor& gearbox, double gearing, units::kilogram_square_meter_t moi,
     units::meter_t armLength, units::radian_t minAngle,
-    units::radian_t maxAngle, units::kilogram_t armMass, bool simulateGravity,
+    units::radian_t maxAngle, bool simulateGravity,
+    units::radian_t startingAngle,
     const std::array<double, 1>& measurementStdDevs)
     : SingleJointedArmSim(
           LinearSystemId::SingleJointedArmSystem(gearbox, moi, gearing),
-          gearbox, gearing, armLength, minAngle, maxAngle, armMass,
-          simulateGravity, measurementStdDevs) {}
+          gearbox, gearing, armLength, minAngle, maxAngle, simulateGravity,
+          startingAngle, measurementStdDevs) {}
+
+void SingleJointedArmSim::SetState(units::radian_t angle,
+                                   units::radians_per_second_t velocity) {
+  SetState(Vectord<2>{std::clamp(angle, m_minAngle, m_maxAngle), velocity});
+}
 
 bool SingleJointedArmSim::WouldHitLowerLimit(units::radian_t armAngle) const {
   return armAngle <= m_minAngle;
@@ -78,24 +86,37 @@
 Vectord<2> SingleJointedArmSim::UpdateX(const Vectord<2>& currentXhat,
                                         const Vectord<1>& u,
                                         units::second_t dt) {
-  // Horizontal case:
-  // Torque = F * r = I * alpha
-  // alpha = F * r / I
-  // Since F = mg,
-  // alpha = m * g * r / I
-  // Finally, multiply RHS by cos(theta) to account for the arm angle
-  // This acceleration is added to the linear system dynamics x-dot = Ax + Bu
-  // We therefore find that f(x, u) = Ax + Bu + [[0] [m * g * r / I *
-  // std::cos(theta)]]
+  // The torque on the arm is given by τ = F⋅r, where F is the force applied by
+  // gravity and r the distance from pivot to center of mass. Recall from
+  // dynamics that the sum of torques for a rigid body is τ = J⋅α, were τ is
+  // torque on the arm, J is the mass-moment of inertia about the pivot axis,
+  // and α is the angular acceleration in rad/s². Rearranging yields: α = F⋅r/J
+  //
+  // We substitute in F = m⋅g⋅cos(θ), where θ is the angle from horizontal:
+  //
+  //   α = (m⋅g⋅cos(θ))⋅r/J
+  //
+  // Multiply RHS by cos(θ) to account for the arm angle. Further, we know the
+  // arm mass-moment of inertia J of our arm is given by J=1/3 mL², modeled as a
+  // rod rotating about it's end, where L is the overall rod length. The mass
+  // distribution is assumed to be uniform. Substitute r=L/2 to find:
+  //
+  //   α = (m⋅g⋅cos(θ))⋅r/(1/3 mL²)
+  //   α = (m⋅g⋅cos(θ))⋅(L/2)/(1/3 mL²)
+  //   α = 3/2⋅g⋅cos(θ)/L
+  //
+  // This acceleration is next added to the linear system dynamics ẋ=Ax+Bu
+  //
+  //   f(x, u) = Ax + Bu + [0  α]áµ€
+  //   f(x, u) = Ax + Bu + [0  3/2⋅g⋅cos(θ)/L]áµ€
 
   Vectord<2> updatedXhat = RKDP(
       [&](const auto& x, const auto& u) -> Vectord<2> {
         Vectord<2> xdot = m_plant.A() * x + m_plant.B() * u;
 
         if (m_simulateGravity) {
-          xdot += Vectord<2>{0.0, (m_armMass * m_r * -9.8 * 3.0 /
-                                   (m_armMass * m_r * m_r) * std::cos(x(0)))
-                                      .value()};
+          xdot += Vectord<2>{
+              0.0, (3.0 / 2.0 * -9.8 / m_armLen * std::cos(x(0))).value()};
         }
         return xdot;
       },
diff --git a/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
index d2d1687..30a5650 100644
--- a/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
@@ -9,8 +9,11 @@
 
 using namespace frc::sim;
 
-UltrasonicSim::UltrasonicSim(const frc::Ultrasonic& ultrasonic) {
-  frc::sim::SimDeviceSim deviceSim{"Ultrasonic", ultrasonic.GetEchoChannel()};
+UltrasonicSim::UltrasonicSim(const frc::Ultrasonic& ultrasonic)
+    : UltrasonicSim(0, ultrasonic.GetEchoChannel()) {}
+
+UltrasonicSim::UltrasonicSim(int ping, int echo) {
+  frc::sim::SimDeviceSim deviceSim{"Ultrasonic", echo};
   m_simRangeValid = deviceSim.GetBoolean("Range Valid");
   m_simRange = deviceSim.GetDouble("Range (in)");
 }
@@ -19,6 +22,6 @@
   m_simRangeValid.Set(isValid);
 }
 
-void UltrasonicSim::SetRange(units::meter_t range) {
+void UltrasonicSim::SetRange(units::inch_t range) {
   m_simRange.Set(range.value());
 }
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
index 355cbc6..1bee916 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
@@ -4,7 +4,6 @@
 
 #include "frc/smartdashboard/Mechanism2d.h"
 
-#include <cstdio>
 #include <string_view>
 
 #include <networktables/NTSendableBuilder.h>
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
index a43aa16..75ec44e 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
@@ -4,7 +4,7 @@
 
 #include "frc/smartdashboard/MechanismLigament2d.h"
 
-#include <cstdio>
+#include <wpi/StringExtras.h>
 
 using namespace frc;
 
@@ -36,8 +36,10 @@
 
 void MechanismLigament2d::SetColor(const Color8Bit& color) {
   std::scoped_lock lock(m_mutex);
-  std::snprintf(m_color, sizeof(m_color), "#%02X%02X%02X", color.red,
-                color.green, color.blue);
+
+  wpi::format_to_n_c_str(m_color, sizeof(m_color), "#{:02X}{:02X}{:02X}",
+                         color.red, color.green, color.blue);
+
   if (m_colorEntry) {
     m_colorEntry.Set(m_color);
   }
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
index 79b0e6e..c063850 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
@@ -141,6 +141,14 @@
   m_properties.emplace_back(std::move(prop));
 }
 
+template <typename Topic, typename Value>
+void SendableBuilderImpl::PublishConstImpl(Topic topic, Value value) {
+  auto prop = std::make_unique<PropertyImpl<Topic>>();
+  prop->pub = topic.Publish();
+  prop->pub.Set(value);
+  m_properties.emplace_back(std::move(prop));
+}
+
 void SendableBuilderImpl::AddBooleanProperty(std::string_view key,
                                              std::function<bool()> getter,
                                              std::function<void(bool)> setter) {
@@ -148,6 +156,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstBoolean(std::string_view key,
+                                              bool value) {
+  PublishConstImpl(m_table->GetBooleanTopic(key), value);
+}
+
 void SendableBuilderImpl::AddIntegerProperty(
     std::string_view key, std::function<int64_t()> getter,
     std::function<void(int64_t)> setter) {
@@ -155,6 +168,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstInteger(std::string_view key,
+                                              int64_t value) {
+  PublishConstImpl(m_table->GetIntegerTopic(key), value);
+}
+
 void SendableBuilderImpl::AddFloatProperty(std::string_view key,
                                            std::function<float()> getter,
                                            std::function<void(float)> setter) {
@@ -162,6 +180,10 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstFloat(std::string_view key, float value) {
+  PublishConstImpl(m_table->GetFloatTopic(key), value);
+}
+
 void SendableBuilderImpl::AddDoubleProperty(
     std::string_view key, std::function<double()> getter,
     std::function<void(double)> setter) {
@@ -169,6 +191,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstDouble(std::string_view key,
+                                             double value) {
+  PublishConstImpl(m_table->GetDoubleTopic(key), value);
+}
+
 void SendableBuilderImpl::AddStringProperty(
     std::string_view key, std::function<std::string()> getter,
     std::function<void(std::string_view)> setter) {
@@ -176,6 +203,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstString(std::string_view key,
+                                             std::string_view value) {
+  PublishConstImpl(m_table->GetStringTopic(key), value);
+}
+
 void SendableBuilderImpl::AddBooleanArrayProperty(
     std::string_view key, std::function<std::vector<int>()> getter,
     std::function<void(std::span<const int>)> setter) {
@@ -183,6 +215,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstBooleanArray(std::string_view key,
+                                                   std::span<const int> value) {
+  PublishConstImpl(m_table->GetBooleanArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddIntegerArrayProperty(
     std::string_view key, std::function<std::vector<int64_t>()> getter,
     std::function<void(std::span<const int64_t>)> setter) {
@@ -190,6 +227,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstIntegerArray(
+    std::string_view key, std::span<const int64_t> value) {
+  PublishConstImpl(m_table->GetIntegerArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddFloatArrayProperty(
     std::string_view key, std::function<std::vector<float>()> getter,
     std::function<void(std::span<const float>)> setter) {
@@ -197,6 +239,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstFloatArray(std::string_view key,
+                                                 std::span<const float> value) {
+  PublishConstImpl(m_table->GetFloatArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddDoubleArrayProperty(
     std::string_view key, std::function<std::vector<double>()> getter,
     std::function<void(std::span<const double>)> setter) {
@@ -204,6 +251,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstDoubleArray(
+    std::string_view key, std::span<const double> value) {
+  PublishConstImpl(m_table->GetDoubleArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddStringArrayProperty(
     std::string_view key, std::function<std::vector<std::string>()> getter,
     std::function<void(std::span<const std::string>)> setter) {
@@ -211,6 +263,11 @@
                   std::move(setter));
 }
 
+void SendableBuilderImpl::PublishConstStringArray(
+    std::string_view key, std::span<const std::string> value) {
+  PublishConstImpl(m_table->GetStringArrayTopic(key), value);
+}
+
 void SendableBuilderImpl::AddRawProperty(
     std::string_view key, std::string_view typeString,
     std::function<std::vector<uint8_t>()> getter,
@@ -235,6 +292,16 @@
   m_properties.emplace_back(std::move(prop));
 }
 
+void SendableBuilderImpl::PublishConstRaw(std::string_view key,
+                                          std::string_view typeString,
+                                          std::span<const uint8_t> value) {
+  auto topic = m_table->GetRawTopic(key);
+  auto prop = std::make_unique<PropertyImpl<nt::RawTopic>>();
+  prop->pub = topic.Publish(typeString);
+  prop->pub.Set(value);
+  m_properties.emplace_back(std::move(prop));
+}
+
 template <typename T, size_t Size, typename Topic, typename Getter,
           typename Setter>
 void SendableBuilderImpl::AddSmallPropertyImpl(Topic topic, Getter getter,
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
index 9fa5b69..f19d9e4 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
@@ -19,8 +19,6 @@
       m_defaultChoice(std::move(oth.m_defaultChoice)),
       m_selected(std::move(oth.m_selected)),
       m_haveSelected(std::move(oth.m_haveSelected)),
-      m_instancePubs(std::move(oth.m_instancePubs)),
-      m_activePubs(std::move(oth.m_activePubs)),
       m_instance(std::move(oth.m_instance)) {}
 
 SendableChooserBase& SendableChooserBase::operator=(SendableChooserBase&& oth) {
@@ -29,8 +27,6 @@
   m_defaultChoice = std::move(oth.m_defaultChoice);
   m_selected = std::move(oth.m_selected);
   m_haveSelected = std::move(oth.m_haveSelected);
-  m_instancePubs = std::move(oth.m_instancePubs);
-  m_activePubs = std::move(oth.m_activePubs);
   m_instance = std::move(oth.m_instance);
   return *this;
 }
diff --git a/wpilibc/src/main/native/cppcs/RobotBase.cpp b/wpilibc/src/main/native/cppcs/RobotBase.cpp
index 535d20d..c3616b9 100644
--- a/wpilibc/src/main/native/cppcs/RobotBase.cpp
+++ b/wpilibc/src/main/native/cppcs/RobotBase.cpp
@@ -15,6 +15,7 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <networktables/NetworkTableInstance.h>
+#include <wpi/timestamp.h>
 #include <wpimath/MathShared.h>
 
 #include "WPILibVersion.h"
@@ -41,6 +42,7 @@
     std::puts("FATAL ERROR: HAL could not be initialized");
     return -1;
   }
+  DriverStation::RefreshData();
   HAL_Report(HALUsageReporting::kResourceType_Language,
              HALUsageReporting::kLanguage_CPlusPlus, 0, GetWPILibVersion());
 
@@ -137,6 +139,10 @@
         break;
     }
   }
+
+  units::second_t GetTimestamp() override {
+    return units::second_t{wpi::Now() * 1.0e-6};
+  }
 };
 }  // namespace
 
@@ -204,6 +210,10 @@
   return DriverStation::IsTest();
 }
 
+bool RobotBase::IsTestEnabled() const {
+  return DriverStation::IsTestEnabled();
+}
+
 std::thread::id RobotBase::GetThreadId() {
   return m_threadId;
 }
@@ -219,13 +229,13 @@
   SetupMathShared();
 
   auto inst = nt::NetworkTableInstance::GetDefault();
-  // subscribe to "" to force persistent values to progagate to local
+  // subscribe to "" to force persistent values to propagate to local
   nt::SubscribeMultiple(inst.GetHandle(), {{std::string_view{}}});
-#ifdef __FRC_ROBORIO__
-  inst.StartServer("/home/lvuser/networktables.json");
-#else
-  inst.StartServer();
-#endif
+  if constexpr (!IsSimulation()) {
+    inst.StartServer("/home/lvuser/networktables.json");
+  } else {
+    inst.StartServer();
+  }
 
   // wait for the NT server to actually start
   int count = 0;
@@ -241,7 +251,7 @@
 
   SmartDashboard::init();
 
-  if (IsReal()) {
+  if constexpr (!IsSimulation()) {
     std::FILE* file = nullptr;
     file = std::fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
 
diff --git a/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h b/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
index 60d57a9..3f4a1c2 100644
--- a/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
+++ b/wpilibc/src/main/native/include/frc/ADIS16448_IMU.h
@@ -20,7 +20,6 @@
 #include <thread>
 
 #include <hal/SimDevice.h>
-#include <networktables/NTSendable.h>
 #include <units/acceleration.h>
 #include <units/angle.h>
 #include <units/angular_velocity.h>
@@ -29,6 +28,7 @@
 #include <units/temperature.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/DigitalInput.h"
@@ -53,7 +53,7 @@
  * the RoboRIO MXP port.
  */
 
-class ADIS16448_IMU : public nt::NTSendable,
+class ADIS16448_IMU : public wpi::Sendable,
                       public wpi::SendableHelper<ADIS16448_IMU> {
  public:
   /* ADIS16448 Calibration Time Enum Class */
@@ -216,7 +216,7 @@
    */
   int GetPort() const;
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
   /** @brief ADIS16448 Register Map Declaration */
diff --git a/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h b/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
index 4619819..e3b521c 100644
--- a/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
+++ b/wpilibc/src/main/native/include/frc/ADIS16470_IMU.h
@@ -20,12 +20,12 @@
 #include <thread>
 
 #include <hal/SimDevice.h>
-#include <networktables/NTSendable.h>
 #include <units/acceleration.h>
 #include <units/angle.h>
 #include <units/angular_velocity.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/DigitalInput.h"
@@ -50,7 +50,7 @@
  * available on the RoboRIO.
  */
 
-class ADIS16470_IMU : public nt::NTSendable,
+class ADIS16470_IMU : public wpi::Sendable,
                       public wpi::SendableHelper<ADIS16470_IMU> {
  public:
   /* ADIS16470 Calibration Time Enum Class */
@@ -172,7 +172,7 @@
    */
   int GetPort() const;
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  void InitSendable(wpi::SendableBuilder& builder) override;
 
  private:
   /* ADIS16470 Register Map Declaration */
diff --git a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
index 6b6b76c..2fe0da4 100644
--- a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
+++ b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/I2C.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -24,10 +23,11 @@
  * href="https://docs.wpilib.org/en/stable/docs/yearly-overview/known-issues.html#onboard-i2c-causing-system-lockups">
  * WPILib Known Issues</a> page for details.
  */
-class ADXL345_I2C : public Accelerometer,
-                    public nt::NTSendable,
+class ADXL345_I2C : public nt::NTSendable,
                     public wpi::SendableHelper<ADXL345_I2C> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
 
   struct AllAxes {
@@ -53,11 +53,34 @@
   I2C::Port GetI2CPort() const;
   int GetI2CDeviceAddress() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
index 18d48a3..3f2d8ae 100644
--- a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
+++ b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -19,10 +18,11 @@
  * This class allows access to an Analog Devices ADXL345 3-axis accelerometer
  * via SPI. This class assumes the sensor is wired in 4-wire SPI mode.
  */
-class ADXL345_SPI : public Accelerometer,
-                    public nt::NTSendable,
+class ADXL345_SPI : public nt::NTSendable,
                     public wpi::SendableHelper<ADXL345_SPI> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2, kRange_16G = 3 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
 
   struct AllAxes {
@@ -46,11 +46,34 @@
 
   SPI::Port GetSpiPort() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/wpilibc/src/main/native/include/frc/ADXL362.h b/wpilibc/src/main/native/include/frc/ADXL362.h
index 451b5fb..ddf6ebe 100644
--- a/wpilibc/src/main/native/include/frc/ADXL362.h
+++ b/wpilibc/src/main/native/include/frc/ADXL362.h
@@ -9,7 +9,6 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Accelerometer.h"
 
 namespace frc {
 
@@ -18,10 +17,10 @@
  *
  * This class allows access to an Analog Devices ADXL362 3-axis accelerometer.
  */
-class ADXL362 : public Accelerometer,
-                public nt::NTSendable,
-                public wpi::SendableHelper<ADXL362> {
+class ADXL362 : public nt::NTSendable, public wpi::SendableHelper<ADXL362> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 };
+
   enum Axes { kAxis_X = 0x00, kAxis_Y = 0x02, kAxis_Z = 0x04 };
   struct AllAxes {
     double XAxis;
@@ -52,11 +51,34 @@
 
   SPI::Port GetSpiPort() const;
 
-  // Accelerometer interface
-  void SetRange(Range range) final;
-  double GetX() override;
-  double GetY() override;
-  double GetZ() override;
+  /**
+   * Set the measuring range of the accelerometer.
+   *
+   * @param range The maximum acceleration, positive or negative, that the
+   *     accelerometer will measure.
+   */
+  void SetRange(Range range);
+
+  /**
+   * Returns the acceleration along the X axis in g-forces.
+   *
+   * @return The acceleration along the X axis in g-forces.
+   */
+  double GetX();
+
+  /**
+   * Returns the acceleration along the Y axis in g-forces.
+   *
+   * @return The acceleration along the Y axis in g-forces.
+   */
+  double GetY();
+
+  /**
+   * Returns the acceleration along the Z axis in g-forces.
+   *
+   * @return The acceleration along the Z axis in g-forces.
+   */
+  double GetZ();
 
   /**
    * Get the acceleration of one axis in Gs.
diff --git a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
index 55414d4..d923e13 100644
--- a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
+++ b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h
@@ -11,7 +11,7 @@
 #include <wpi/sendable/SendableHelper.h>
 
 #include "frc/SPI.h"
-#include "frc/interfaces/Gyro.h"
+#include "frc/geometry/Rotation2d.h"
 
 namespace frc {
 
@@ -28,8 +28,7 @@
  * This class is for the digital ADXRS450 gyro sensor that connects via SPI.
  * Only one instance of an ADXRS %Gyro is supported.
  */
-class ADXRS450_Gyro : public Gyro,
-                      public wpi::Sendable,
+class ADXRS450_Gyro : public wpi::Sendable,
                       public wpi::SendableHelper<ADXRS450_Gyro> {
  public:
   /**
@@ -61,7 +60,7 @@
    *
    * @return the current heading of the robot in degrees.
    */
-  double GetAngle() const override;
+  double GetAngle() const;
 
   /**
    * Return the rate of rotation of the gyro
@@ -70,7 +69,7 @@
    *
    * @return the current rate in degrees per second
    */
-  double GetRate() const override;
+  double GetRate() const;
 
   /**
    * Reset the gyro.
@@ -79,11 +78,9 @@
    * significant drift in the gyro and it needs to be recalibrated after it has
    * been running.
    */
-  void Reset() override;
+  void Reset();
 
   /**
-   * Initialize the gyro.
-   *
    * Calibrate the gyro by running for a number of samples and computing the
    * center value. Then use the center value as the Accumulator center value for
    * subsequent measurements.
@@ -93,7 +90,22 @@
    * robot is first turned on while it's sitting at rest before the competition
    * starts.
    */
-  void Calibrate() final;
+  void Calibrate();
+
+  /**
+   * Return the heading of the robot as a Rotation2d.
+   *
+   * The angle is continuous, that is it will continue from 360 to 361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps past from 360 to 0 on the second time around.
+   *
+   * The angle is expected to increase as the gyro turns counterclockwise when
+   * looked at from the top. It needs to follow the NWU axis convention.
+   *
+   * @return the current heading of the robot as a Rotation2d. This heading is
+   *         based on integration of the returned rate from the gyro.
+   */
+  Rotation2d GetRotation2d() const;
 
   /**
    * Get the SPI port number.
diff --git a/wpilibc/src/main/native/include/frc/AddressableLED.h b/wpilibc/src/main/native/include/frc/AddressableLED.h
index e6adfca..198eb67 100644
--- a/wpilibc/src/main/native/include/frc/AddressableLED.h
+++ b/wpilibc/src/main/native/include/frc/AddressableLED.h
@@ -17,7 +17,10 @@
 namespace frc {
 
 /**
- * A class for driving addressable LEDs, such as WS2812s and NeoPixels.
+ * A class for driving addressable LEDs, such as WS2812Bs and NeoPixels.
+ *
+ * By default, the timing supports WS2812B LEDs, but is configurable using
+ * SetBitTiming()
  *
  * <p>Only 1 LED driver is currently supported by the roboRIO.
  */
@@ -122,25 +125,25 @@
   /**
    * Sets the bit timing.
    *
-   * <p>By default, the driver is set up to drive WS2812s, so nothing needs to
+   * <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to
    * be set for those.
    *
-   * @param lowTime0 low time for 0 bit
-   * @param highTime0 high time for 0 bit
-   * @param lowTime1 low time for 1 bit
-   * @param highTime1 high time for 1 bit
+   * @param highTime0 high time for 0 bit (default 400ns)
+   * @param lowTime0 low time for 0 bit (default 900ns)
+   * @param highTime1 high time for 1 bit (default 900ns)
+   * @param lowTime1 low time for 1 bit (default 600ns)
    */
-  void SetBitTiming(units::nanosecond_t lowTime0, units::nanosecond_t highTime0,
-                    units::nanosecond_t lowTime1,
-                    units::nanosecond_t highTime1);
+  void SetBitTiming(units::nanosecond_t highTime0, units::nanosecond_t lowTime0,
+                    units::nanosecond_t highTime1,
+                    units::nanosecond_t lowTime1);
 
   /**
    * Sets the sync time.
    *
    * <p>The sync time is the time to hold output so LEDs enable. Default set for
-   * WS2812.
+   * WS2812B.
    *
-   * @param syncTime the sync time
+   * @param syncTime the sync time (default 280us)
    */
   void SetSyncTime(units::microsecond_t syncTime);
 
diff --git a/wpilibc/src/main/native/include/frc/AnalogGyro.h b/wpilibc/src/main/native/include/frc/AnalogGyro.h
index 6565c93..0c472fb 100644
--- a/wpilibc/src/main/native/include/frc/AnalogGyro.h
+++ b/wpilibc/src/main/native/include/frc/AnalogGyro.h
@@ -10,7 +10,7 @@
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
-#include "frc/interfaces/Gyro.h"
+#include "frc/geometry/Rotation2d.h"
 
 namespace frc {
 
@@ -29,8 +29,7 @@
  *
  * This class is for gyro sensors that connect to an analog input.
  */
-class AnalogGyro : public Gyro,
-                   public wpi::Sendable,
+class AnalogGyro : public wpi::Sendable,
                    public wpi::SendableHelper<AnalogGyro> {
  public:
   static constexpr int kOversampleBits = 10;
@@ -118,7 +117,7 @@
    * @return The current heading of the robot in degrees. This heading is based
    *         on integration of the returned rate from the gyro.
    */
-  double GetAngle() const override;
+  double GetAngle() const;
 
   /**
    * Return the rate of rotation of the gyro
@@ -127,7 +126,7 @@
    *
    * @return the current rate in degrees per second
    */
-  double GetRate() const override;
+  double GetRate() const;
 
   /**
    * Return the gyro center value. If run after calibration,
@@ -174,7 +173,7 @@
    * significant drift in the gyro and it needs to be recalibrated after it has
    * been running.
    */
-  void Reset() final;
+  void Reset();
 
   /**
    * Initialize the gyro.
@@ -183,7 +182,32 @@
    */
   void InitGyro();
 
-  void Calibrate() final;
+  /**
+   * Calibrate the gyro by running for a number of samples and computing the
+   * center value. Then use the center value as the Accumulator center value for
+   * subsequent measurements.
+   *
+   * It's important to make sure that the robot is not moving while the
+   * centering calculations are in progress, this is typically done when the
+   * robot is first turned on while it's sitting at rest before the competition
+   * starts.
+   */
+  void Calibrate();
+
+  /**
+   * Return the heading of the robot as a Rotation2d.
+   *
+   * The angle is continuous, that is it will continue from 360 to 361 degrees.
+   * This allows algorithms that wouldn't want to see a discontinuity in the
+   * gyro output as it sweeps past from 360 to 0 on the second time around.
+   *
+   * The angle is expected to increase as the gyro turns counterclockwise when
+   * looked at from the top. It needs to follow the NWU axis convention.
+   *
+   * @return the current heading of the robot as a Rotation2d. This heading is
+   *         based on integration of the returned rate from the gyro.
+   */
+  Rotation2d GetRotation2d() const;
 
   /**
    * Gets the analog input for the gyro.
diff --git a/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h b/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
index 6f52cab..88bd6c3 100644
--- a/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
+++ b/wpilibc/src/main/native/include/frc/AnalogTriggerOutput.h
@@ -30,7 +30,7 @@
  * range defined by the limits.
  *
  * The RisingPulse and FallingPulse outputs detect an instantaneous transition
- * from above the upper limit to below the lower limit, and vise versa. These
+ * from above the upper limit to below the lower limit, and vice versa. These
  * pulses represent a rollover condition of a sensor and can be routed to an up
  * / down counter or to interrupts. Because the outputs generate a pulse, they
  * cannot be read directly. To help ensure that a rollover condition is not
diff --git a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
index 0e5bee6..a8f0adc 100644
--- a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
+++ b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h
@@ -7,8 +7,6 @@
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
-#include "frc/interfaces/Accelerometer.h"
-
 namespace frc {
 
 /**
@@ -16,10 +14,11 @@
  *
  * This class allows access to the roboRIO's internal accelerometer.
  */
-class BuiltInAccelerometer : public Accelerometer,
-                             public wpi::Sendable,
+class BuiltInAccelerometer : public wpi::Sendable,
                              public wpi::SendableHelper<BuiltInAccelerometer> {
  public:
+  enum Range { kRange_2G = 0, kRange_4G = 1, kRange_8G = 2 };
+
   /**
    * Constructor.
    *
@@ -30,30 +29,28 @@
   BuiltInAccelerometer(BuiltInAccelerometer&&) = default;
   BuiltInAccelerometer& operator=(BuiltInAccelerometer&&) = default;
 
-  // Accelerometer interface
   /**
    * Set the measuring range of the accelerometer.
    *
    * @param range The maximum acceleration, positive or negative, that the
-   *              accelerometer will measure. Not all accelerometers support all
-   *              ranges.
+   *     accelerometer will measure.
    */
-  void SetRange(Range range) final;
+  void SetRange(Range range);
 
   /**
    * @return The acceleration of the roboRIO along the X axis in g-forces
    */
-  double GetX() override;
+  double GetX();
 
   /**
    * @return The acceleration of the roboRIO along the Y axis in g-forces
    */
-  double GetY() override;
+  double GetY();
 
   /**
    * @return The acceleration of the roboRIO along the Z axis in g-forces
    */
-  double GetZ() override;
+  double GetZ();
 
   void InitSendable(wpi::SendableBuilder& builder) override;
 };
diff --git a/wpilibc/src/main/native/include/frc/CAN.h b/wpilibc/src/main/native/include/frc/CAN.h
index 4c9b9bd..dc04988 100644
--- a/wpilibc/src/main/native/include/frc/CAN.h
+++ b/wpilibc/src/main/native/include/frc/CAN.h
@@ -10,8 +10,11 @@
 
 namespace frc {
 struct CANData {
+  /** Contents of the CAN packet. */
   uint8_t data[8];
+  /** Length of packet in bytes. */
   int32_t length;
+  /** CAN frame timestamp in milliseconds. */
   uint64_t timestamp;
 };
 
@@ -158,6 +161,15 @@
    */
   bool ReadPacketTimeout(int apiId, int timeoutMs, CANData* data);
 
+  /**
+   * Reads the current value of the millisecond-resolution timer that CANData
+   * timestamps are based on
+   *
+   * @return Current value of timer used as a base time for CANData timestamps
+   * in milliseconds
+   */
+  static uint64_t GetTimestampBaseTime();
+
   static constexpr HAL_CANManufacturer kTeamManufacturer = HAL_CAN_Man_kTeamUse;
   static constexpr HAL_CANDeviceType kTeamDeviceType =
       HAL_CAN_Dev_kMiscellaneous;
diff --git a/wpilibc/src/main/native/include/frc/Compressor.h b/wpilibc/src/main/native/include/frc/Compressor.h
index 1329a3b..ed1b3b9 100644
--- a/wpilibc/src/main/native/include/frc/Compressor.h
+++ b/wpilibc/src/main/native/include/frc/Compressor.h
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include <hal/Types.h>
-#include <wpi/deprecated.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -59,19 +58,6 @@
   Compressor& operator=(Compressor&&) = default;
 
   /**
-   * Check if compressor output is active.
-   * To (re)enable the compressor use EnableDigital() or EnableAnalog(...).
-   *
-   * @return true if the compressor is on.
-   * @deprecated To avoid confusion in thinking this (re)enables the compressor
-   * use IsEnabled().
-   */
-  WPI_DEPRECATED(
-      "To avoid confusion in thinking this (re)enables the compressor use "
-      "IsEnabled()")
-  bool Enabled() const;
-
-  /**
    * Returns whether the compressor is active or not.
    *
    * @return true if the compressor is on - otherwise false.
diff --git a/wpilibc/src/main/native/include/frc/Counter.h b/wpilibc/src/main/native/include/frc/Counter.h
index d501b7e..71eebca 100644
--- a/wpilibc/src/main/native/include/frc/Counter.h
+++ b/wpilibc/src/main/native/include/frc/Counter.h
@@ -343,6 +343,34 @@
 
   int GetFPGAIndex() const;
 
+  /**
+   * Set the distance per pulse for this counter. This sets the multiplier used
+   * to determine the distance driven based on the count value from the encoder.
+   * Set this value based on the Pulses per Revolution and factor in any gearing
+   * reductions. This distance can be in any units you like, linear or angular.
+   *
+   * @param distancePerPulse The scale factor that will be used to convert
+   * pulses to useful units.
+   */
+  void SetDistancePerPulse(double distancePerPulse);
+
+  /**
+   * Read the current scaled counter value. Read the value at this instant,
+   * scaled by the distance per pulse (defaults to 1).
+   *
+   * @return The distance since the last reset
+   */
+  double GetDistance() const;
+
+  /**
+   * Get the current rate of the Counter. Read the current rate of the counter
+   * accounting for the distance per pulse value. The default value for distance
+   * per pulse (1) yields units of pulses per second.
+   *
+   * @return The rate in units/sec
+   */
+  double GetRate() const;
+
   // CounterBase interface
   /**
    * Read the current counter value.
@@ -434,6 +462,7 @@
 
  private:
   int m_index = 0;  // The index of this counter.
+  double m_distancePerPulse = 1;
 
   friend class DigitalGlitchFilter;
 };
diff --git a/wpilibc/src/main/native/include/frc/DataLogManager.h b/wpilibc/src/main/native/include/frc/DataLogManager.h
index fa7abba..336af4a 100644
--- a/wpilibc/src/main/native/include/frc/DataLogManager.h
+++ b/wpilibc/src/main/native/include/frc/DataLogManager.h
@@ -50,6 +50,11 @@
                     double period = 0.25);
 
   /**
+   * Stop data log manager.
+   */
+  static void Stop();
+
+  /**
    * Log a message to the "messages" entry. The message is also printed to
    * standard output (followed by a newline).
    *
diff --git a/wpilibc/src/main/native/include/frc/DoubleSolenoid.h b/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
index 381e3a1..f02ba5a 100644
--- a/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
+++ b/wpilibc/src/main/native/include/frc/DoubleSolenoid.h
@@ -98,7 +98,7 @@
    * If a solenoid is shorted, it is added to the DisabledList and disabled
    * until power cycle, or until faults are cleared.
    *
-   * @see ClearAllPCMStickyFaults()
+   * @see ClearAllStickyFaults()
    * @return If solenoid is disabled due to short.
    */
   bool IsFwdSolenoidDisabled() const;
@@ -109,7 +109,7 @@
    * If a solenoid is shorted, it is added to the DisabledList and disabled
    * until power cycle, or until faults are cleared.
    *
-   * @see ClearAllPCMStickyFaults()
+   * @see ClearAllStickyFaults()
    * @return If solenoid is disabled due to short.
    */
   bool IsRevSolenoidDisabled() const;
diff --git a/wpilibc/src/main/native/include/frc/DriverStation.h b/wpilibc/src/main/native/include/frc/DriverStation.h
index 4cbf6da..ea568a7 100644
--- a/wpilibc/src/main/native/include/frc/DriverStation.h
+++ b/wpilibc/src/main/native/include/frc/DriverStation.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 
 #include <units/time.h>
@@ -21,7 +22,7 @@
  */
 class DriverStation final {
  public:
-  enum Alliance { kRed, kBlue, kInvalid };
+  enum Alliance { kRed, kBlue };
   enum MatchType { kNone, kPractice, kQualification, kElimination };
 
   static constexpr int kJoystickPorts = 6;
@@ -210,6 +211,14 @@
   static bool IsTest();
 
   /**
+   * Check if the DS is commanding Test mode and if it has enabled the robot.
+   *
+   * @return True if the robot is being commanded to be in Test mode and
+   * enabled.
+   */
+  static bool IsTestEnabled();
+
+  /**
    * Check if the DS is attached.
    *
    * @return True if the DS is connected to the robot
@@ -227,6 +236,9 @@
   /**
    * Returns the game specific message provided by the FMS.
    *
+   * If the FMS is not connected, it is set from the game data setting on the
+   * driver station.
+   *
    * @return A string containing the game specific message.
    */
   static std::string GetGameSpecificMessage();
@@ -262,39 +274,58 @@
   static int GetReplayNumber();
 
   /**
-   * Return the alliance that the driver station says it is on.
+   * Return the alliance that the driver station says it is on from the FMS.
+   *
+   * If the FMS is not connected, it is set from the team alliance setting on
+   * the driver station.
    *
    * This could return kRed or kBlue.
    *
    * @return The Alliance enum (kRed, kBlue or kInvalid)
    */
-  static Alliance GetAlliance();
+  static std::optional<Alliance> GetAlliance();
 
   /**
-   * Return the driver station location on the field.
+   * Return the driver station location from the FMS.
+   *
+   * If the FMS is not connected, it is set from the team alliance setting on
+   * the driver station.
    *
    * This could return 1, 2, or 3.
    *
    * @return The location of the driver station (1-3, 0 for invalid)
    */
-  static int GetLocation();
+  static std::optional<int> GetLocation();
 
   /**
-   * Return the approximate match time.
+   * Wait for a DS connection.
    *
-   * The FMS does not send an official match time to the robots, but does send
-   * an approximate match time. The value will count down the time remaining in
-   * the current period (auto or teleop).
-   *
+   * @param timeout timeout in seconds. 0 for infinite.
+   * @return true if connected, false if timeout
+   */
+  static bool WaitForDsConnection(units::second_t timeout);
+
+  /**
+   * Return the approximate match time. The FMS does not send an official match
+   * time to the robots, but does send an approximate match time. The value will
+   * count down the time remaining in the current period (auto or teleop).
    * Warning: This is not an official time (so it cannot be used to dispute ref
    * calls or guarantee that a function will trigger before the match ends).
    *
-   * The Practice Match function of the DS approximates the behavior seen on
-   * the field.
+   * <p>When connected to the real field, this number only changes in full
+   * integer increments, and always counts down.
    *
-   * @return Time remaining in current match period (auto or teleop)
+   * <p>When the DS is in practice mode, this number is a floating point number,
+   * and counts down.
+   *
+   * <p>When the DS is in teleop or autonomous mode, this number is a floating
+   * point number, and counts up.
+   *
+   * <p>Simulation matches DS behavior without an FMS connected.
+   *
+   * @return Time remaining in current match period (auto or teleop) in seconds
    */
-  static double GetMatchTime();
+  static units::second_t GetMatchTime();
 
   /**
    * Read the battery voltage.
diff --git a/wpilibc/src/main/native/include/frc/Encoder.h b/wpilibc/src/main/native/include/frc/Encoder.h
index deb36c0..7315e16 100644
--- a/wpilibc/src/main/native/include/frc/Encoder.h
+++ b/wpilibc/src/main/native/include/frc/Encoder.h
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include <hal/Types.h>
-#include <wpi/deprecated.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -168,7 +167,7 @@
    * @return Period in seconds of the most recent pulse.
    * @deprecated Use getRate() in favor of this method.
    */
-  WPI_DEPRECATED("Use GetRate() in favor of this method")
+  [[deprecated("Use GetRate() in favor of this method")]]
   units::second_t GetPeriod() const override;
 
   /**
@@ -186,9 +185,9 @@
    *             periods and SetMinRate() scales using value from
    *             SetDistancePerPulse().
    */
-  WPI_DEPRECATED(
+  [[deprecated(
       "Use SetMinRate() in favor of this method.  This takes unscaled periods "
-      "and SetMinRate() scales using value from SetDistancePerPulse().")
+      "and SetMinRate() scales using value from SetDistancePerPulse().")]]
   void SetMaxPeriod(units::second_t maxPeriod) override;
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/Errors.h b/wpilibc/src/main/native/include/frc/Errors.h
index 252a335..4dae6c9 100644
--- a/wpilibc/src/main/native/include/frc/Errors.h
+++ b/wpilibc/src/main/native/include/frc/Errors.h
@@ -95,15 +95,16 @@
  * @param[in]  args error message format args
  * @return runtime error object
  */
-[[nodiscard]] RuntimeError MakeErrorV(int32_t status, const char* fileName,
-                                      int lineNumber, const char* funcName,
-                                      fmt::string_view format,
-                                      fmt::format_args args);
+[[nodiscard]]
+RuntimeError MakeErrorV(int32_t status, const char* fileName, int lineNumber,
+                        const char* funcName, fmt::string_view format,
+                        fmt::format_args args);
 
 template <typename... Args>
-[[nodiscard]] inline RuntimeError MakeError(
-    int32_t status, const char* fileName, int lineNumber, const char* funcName,
-    fmt::string_view format, Args&&... args) {
+[[nodiscard]]
+inline RuntimeError MakeError(int32_t status, const char* fileName,
+                              int lineNumber, const char* funcName,
+                              fmt::string_view format, Args&&... args) {
   return MakeErrorV(status, fileName, lineNumber, funcName, format,
                     fmt::make_format_args(args...));
 }
diff --git a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
index 77ed197..42b0c71 100644
--- a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
+++ b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h
@@ -23,6 +23,9 @@
  *
  * RobotInit() -- provide for initialization at robot power-on
  *
+ * DriverStationConnected() -- provide for initialization the first time the DS
+ * is connected
+ *
  * Init() functions -- each of the following functions is called once when the
  * appropriate mode is entered:
  *
@@ -68,6 +71,14 @@
   virtual void RobotInit();
 
   /**
+   * Code that needs to know the DS state should go here.
+   *
+   * Users should override this method for initialization that needs to occur
+   * after the DS is connected, such as needing the alliance information.
+   */
+  virtual void DriverStationConnected();
+
+  /**
    * Robot-wide simulation initialization code should go here.
    *
    * Users should override this method for default Robot-wide simulation
@@ -242,6 +253,7 @@
   Watchdog m_watchdog;
   bool m_ntFlushEnabled = true;
   bool m_lwEnabledInTest = true;
+  bool m_calledDsConnected = false;
 
   void PrintLoopOverrunMessage();
 };
diff --git a/wpilibc/src/main/native/include/frc/Joystick.h b/wpilibc/src/main/native/include/frc/Joystick.h
index fc0df35..a955718 100644
--- a/wpilibc/src/main/native/include/frc/Joystick.h
+++ b/wpilibc/src/main/native/include/frc/Joystick.h
@@ -6,6 +6,8 @@
 
 #include <array>
 
+#include <units/angle.h>
+
 #include "frc/GenericHID.h"
 
 namespace frc {
@@ -226,7 +228,9 @@
    * in radians.
    *
    * @return The direction of the vector in radians
+   * @deprecated Use GetDirection() instead.
    */
+  [[deprecated("Use GetDirection() instead.")]]
   double GetDirectionRadians() const;
 
   /**
@@ -234,9 +238,18 @@
    * in degrees.
    *
    * @return The direction of the vector in degrees
+   * @deprecated Use GetDirection() instead.
    */
+  [[deprecated("Use GetDirection() instead.")]]
   double GetDirectionDegrees() const;
 
+  /**
+   * Get the direction of the vector formed by the joystick and its origin.
+   *
+   * @return The direction of the vector.
+   */
+  units::radian_t GetDirection() const;
+
  private:
   enum Axis { kX, kY, kZ, kTwist, kThrottle, kNumAxes };
   enum Button { kTrigger = 1, kTop = 2 };
diff --git a/wpilibc/src/main/native/include/frc/Notifier.h b/wpilibc/src/main/native/include/frc/Notifier.h
index bda685c..0bd54d4 100644
--- a/wpilibc/src/main/native/include/frc/Notifier.h
+++ b/wpilibc/src/main/native/include/frc/Notifier.h
@@ -7,10 +7,10 @@
 #include <stdint.h>
 
 #include <atomic>
+#include <concepts>
 #include <functional>
 #include <string_view>
 #include <thread>
-#include <type_traits>
 #include <utility>
 
 #include <hal/Types.h>
@@ -20,7 +20,7 @@
 namespace frc {
 
 /**
- * Notifiers run a callback function on a separate thread at a specified period.
+ * Notifiers run a user-provided callback function on a separate thread.
  *
  * If StartSingle() is used, the callback will run once. If StartPeriodic() is
  * used, the callback will run repeatedly with the given period until stop() is
@@ -29,22 +29,25 @@
 class Notifier {
  public:
   /**
-   * Create a Notifier for timer event notification.
+   * Create a Notifier with the given callback.
    *
-   * @param handler The handler is called at the notification time which is set
-   *                using StartSingle or StartPeriodic.
+   * Configure when the callback runs with StartSingle() or StartPeriodic().
+   *
+   * @param callback The callback to run.
    */
-  explicit Notifier(std::function<void()> handler);
+  explicit Notifier(std::function<void()> callback);
 
-  template <
-      typename Callable, typename Arg, typename... Args,
-      typename = std::enable_if_t<std::is_invocable_v<Callable, Arg, Args...>>>
-  Notifier(Callable&& f, Arg&& arg, Args&&... args)
-      : Notifier(std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
+  template <typename Arg, typename... Args>
+  Notifier(std::invocable<Arg, Args...> auto&& callback, Arg&& arg,
+           Args&&... args)
+      : Notifier(std::bind(std::forward<decltype(callback)>(callback),
+                           std::forward<Arg>(arg),
                            std::forward<Args>(args)...)) {}
 
   /**
-   * Create a Notifier for timer event notification.
+   * Create a Notifier with the given callback.
+   *
+   * Configure when the callback runs with StartSingle() or StartPeriodic().
    *
    * This overload makes the underlying thread run with a real-time priority.
    * This is useful for reducing scheduling jitter on processes which are
@@ -53,16 +56,16 @@
    * @param priority The FIFO real-time scheduler priority ([1..99] where a
    *                 higher number represents higher priority). See "man 7
    *                 sched" for more details.
-   * @param handler  The handler is called at the notification time which is set
-   *                 using StartSingle or StartPeriodic.
+   * @param callback The callback to run.
    */
-  explicit Notifier(int priority, std::function<void()> handler);
+  explicit Notifier(int priority, std::function<void()> callback);
 
-  template <typename Callable, typename Arg, typename... Args>
-  Notifier(int priority, Callable&& f, Arg&& arg, Args&&... args)
-      : Notifier(priority,
-                 std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
-                           std::forward<Args>(args)...)) {}
+  template <typename Arg, typename... Args>
+  Notifier(int priority, std::invocable<Arg, Args...> auto&& callback,
+           Arg&& arg, Args&&... args)
+      : Notifier(priority, std::bind(std::forward<decltype(callback)>(callback),
+                                     std::forward<Arg>(arg),
+                                     std::forward<Args>(args)...)) {}
 
   /**
    * Free the resources for a timer event.
@@ -73,51 +76,54 @@
   Notifier& operator=(Notifier&& rhs);
 
   /**
-   * Sets the name of the notifier.  Used for debugging purposes only.
+   * Sets the name of the notifier. Used for debugging purposes only.
    *
    * @param name Name
    */
   void SetName(std::string_view name);
 
   /**
-   * Change the handler function.
+   * Change the callback function.
    *
-   * @param handler Handler
+   * @param callback The callback function.
+   * @deprecated Use SetCallback() instead.
    */
-  void SetHandler(std::function<void()> handler);
+  [[deprecated("Use SetCallback() instead.")]]
+  void SetHandler(std::function<void()> callback);
 
   /**
-   * Register for single event notification.
+   * Change the callback function.
    *
-   * A timer event is queued for a single event after the specified delay.
+   * @param callback The callback function.
+   */
+  void SetCallback(std::function<void()> callback);
+
+  /**
+   * Run the callback once after the given delay.
    *
-   * @param delay Amount of time to wait before the handler is called.
+   * @param delay Time to wait before the callback is called.
    */
   void StartSingle(units::second_t delay);
 
   /**
-   * Register for periodic event notification.
+   * Run the callback periodically with the given period.
    *
-   * A timer event is queued for periodic event notification. Each time the
-   * interrupt occurs, the event will be immediately requeued for the same time
-   * interval.
+   * The user-provided callback should be written so that it completes before
+   * the next time it's scheduled to run.
    *
-   * The user-provided callback should be written in a nonblocking manner so the
-   * callback can be recalled at the next periodic event notification.
-   *
-   * @param period Period to call the handler starting one period
-   *               after the call to this method.
+   * @param period Period after which to to call the callback starting one
+   *               period after the call to this method.
    */
   void StartPeriodic(units::second_t period);
 
   /**
-   * Stop timer events from occurring.
+   * Stop further callback invocations.
    *
-   * Stop any repeating timer events from occurring. This will also remove any
-   * single notification events from the queue.
+   * No further periodic callbacks will occur. Single invocations will also be
+   * cancelled if they haven't yet occurred.
    *
-   * If a timer-based call to the registered handler is in progress, this
-   * function will block until the handler call is complete.
+   * If a callback invocation is in progress, this function will block until the
+   * callback is complete.
    */
   void Stop();
 
@@ -154,22 +160,24 @@
   // The thread waiting on the HAL alarm
   std::thread m_thread;
 
-  // Held while updating process information
+  // The mutex held while updating process information
   wpi::mutex m_processMutex;
 
-  // HAL handle, atomic for proper destruction
+  // HAL handle (atomic for proper destruction)
   std::atomic<HAL_NotifierHandle> m_notifier{0};
 
-  // Address of the handler
-  std::function<void()> m_handler;
+  // The user-provided callback
+  std::function<void()> m_callback;
 
-  // The absolute expiration time
+  // The time at which the callback should be called. Has the same zero as
+  // Timer::GetFPGATimestamp().
   units::second_t m_expirationTime = 0_s;
 
-  // The relative time (either periodic or single)
+  // If periodic, stores the callback period; if single, stores the time until
+  // the callback call.
   units::second_t m_period = 0_s;
 
-  // True if this is a periodic event
+  // True if the callback is periodic
   bool m_periodic = false;
 };
 
diff --git a/wpilibc/src/main/native/include/frc/PS5Controller.h b/wpilibc/src/main/native/include/frc/PS5Controller.h
new file mode 100644
index 0000000..2a24f5e
--- /dev/null
+++ b/wpilibc/src/main/native/include/frc/PS5Controller.h
@@ -0,0 +1,529 @@
+// 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 "frc/GenericHID.h"
+
+namespace frc {
+
+/**
+ * Handle input from PS5 controllers connected to the Driver Station.
+ *
+ * <p>This class handles PS5 input that comes from the Driver Station. Each time
+ * a value is requested the most recent value is returned. There is a single
+ * class instance for each controller and the mapping of ports to hardware
+ * buttons depends on the code in the Driver Station.
+ */
+class PS5Controller : public GenericHID {
+ public:
+  /**
+   * Construct an instance of an PS5 controller.
+   *
+   * The controller index is the USB port on the Driver Station.
+   *
+   * @param port The port on the Driver Station that the controller is plugged
+   *             into (0-5).
+   */
+  explicit PS5Controller(int port);
+
+  ~PS5Controller() override = default;
+
+  PS5Controller(PS5Controller&&) = default;
+  PS5Controller& operator=(PS5Controller&&) = default;
+
+  /**
+   * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetLeftX() const;
+
+  /**
+   * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetRightX() const;
+
+  /**
+   * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetLeftY() const;
+
+  /**
+   * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value.
+   */
+  double GetRightY() const;
+
+  /**
+   * Get the L2 axis value of the controller. Note that this axis is bound to
+   * the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  double GetL2Axis() const;
+
+  /**
+   * Get the R2 axis value of the controller. Note that this axis is bound to
+   * the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value.
+   */
+  double GetR2Axis() const;
+
+  /**
+   * Read the value of the Square button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetSquareButton() const;
+
+  /**
+   * Whether the Square button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetSquareButtonPressed();
+
+  /**
+   * Whether the Square button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetSquareButtonReleased();
+
+  /**
+   * Constructs an event instance around the square button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the square button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Square(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Cross button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCrossButton() const;
+
+  /**
+   * Whether the Cross button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCrossButtonPressed();
+
+  /**
+   * Whether the Cross button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCrossButtonReleased();
+
+  /**
+   * Constructs an event instance around the cross button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the cross button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Cross(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Circle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCircleButton() const;
+
+  /**
+   * Whether the Circle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCircleButtonPressed();
+
+  /**
+   * Whether the Circle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCircleButtonReleased();
+
+  /**
+   * Constructs an event instance around the circle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the circle button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Circle(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Triangle button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetTriangleButton() const;
+
+  /**
+   * Whether the Triangle button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetTriangleButtonPressed();
+
+  /**
+   * Whether the Triangle button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetTriangleButtonReleased();
+
+  /**
+   * Constructs an event instance around the triangle button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the triangle button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Triangle(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL1Button() const;
+
+  /**
+   * Whether the L1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL1ButtonPressed();
+
+  /**
+   * Whether the L1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL1ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L1 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L1(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R1 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR1Button() const;
+
+  /**
+   * Whether the R1 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR1ButtonPressed();
+
+  /**
+   * Whether the R1 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR1ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R1 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R1 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R1(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L2 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL2Button() const;
+
+  /**
+   * Whether the L2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL2ButtonPressed();
+
+  /**
+   * Whether the L2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL2ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L2 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L2(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R2 button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR2Button() const;
+
+  /**
+   * Whether the R2 button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR2ButtonPressed();
+
+  /**
+   * Whether the R2 button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR2ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R2 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R2 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R2(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Create button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetCreateButton() const;
+
+  /**
+   * Whether the Create button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetCreateButtonPressed();
+
+  /**
+   * Whether the Create button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetCreateButtonReleased();
+
+  /**
+   * Constructs an event instance around the Create button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the Create button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Create(EventLoop* loop) const;
+
+  /**
+   * Read the value of the Options button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetOptionsButton() const;
+
+  /**
+   * Whether the Options button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetOptionsButtonPressed();
+
+  /**
+   * Whether the Options button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetOptionsButtonReleased();
+
+  /**
+   * Constructs an event instance around the options button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the options button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Options(EventLoop* loop) const;
+
+  /**
+   * Read the value of the L3 button (pressing the left analog stick) on the
+   * controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetL3Button() const;
+
+  /**
+   * Whether the L3 (left stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetL3ButtonPressed();
+
+  /**
+   * Whether the L3 (left stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetL3ButtonReleased();
+
+  /**
+   * Constructs an event instance around the L3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the L3 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent L3(EventLoop* loop) const;
+
+  /**
+   * Read the value of the R3 button (pressing the right analog stick) on the
+   * controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetR3Button() const;
+
+  /**
+   * Whether the R3 (right stick) button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetR3ButtonPressed();
+
+  /**
+   * Whether the R3 (right stick) button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetR3ButtonReleased();
+
+  /**
+   * Constructs an event instance around the R3 button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the R3 button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent R3(EventLoop* loop) const;
+
+  /**
+   * Read the value of the PS button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetPSButton() const;
+
+  /**
+   * Whether the PS button was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
+   */
+  bool GetPSButtonPressed();
+
+  /**
+   * Whether the PS button was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
+   */
+  bool GetPSButtonReleased();
+
+  /**
+   * Constructs an event instance around the PS button's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the PS button's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent PS(EventLoop* loop) const;
+
+  /**
+   * Read the value of the touchpad button on the controller.
+   *
+   * @return The state of the button.
+   */
+  bool GetTouchpad() const;
+
+  /**
+   * Whether the touchpad was pressed since the last check.
+   *
+   * @return Whether the touchpad was pressed since the last check.
+   */
+  bool GetTouchpadPressed();
+
+  /**
+   * Whether the touchpad was released since the last check.
+   *
+   * @return Whether the touchpad was released since the last check.
+   */
+  bool GetTouchpadReleased();
+
+  /**
+   * Constructs an event instance around the touchpad's digital signal.
+   *
+   * @param loop the event loop instance to attach the event to.
+   * @return an event instance representing the touchpad's digital signal
+   * attached to the given loop.
+   */
+  BooleanEvent Touchpad(EventLoop* loop) const;
+
+  struct Button {
+    static constexpr int kSquare = 3;
+    static constexpr int kCross = 1;
+    static constexpr int kCircle = 2;
+    static constexpr int kTriangle = 4;
+    static constexpr int kL1 = 5;
+    static constexpr int kR1 = 6;
+    static constexpr int kL2 = 7;
+    static constexpr int kR2 = 8;
+    static constexpr int kCreate = 9;
+    static constexpr int kOptions = 10;
+    static constexpr int kL3 = 12;
+    static constexpr int kR3 = 13;
+    static constexpr int kPS = 11;
+    static constexpr int kTouchpad = 14;
+  };
+
+  struct Axis {
+    static constexpr int kLeftX = 0;
+    static constexpr int kLeftY = 1;
+    static constexpr int kRightX = 3;
+    static constexpr int kRightY = 4;
+    static constexpr int kL2 = 2;
+    static constexpr int kR2 = 5;
+  };
+};
+
+}  // namespace frc
diff --git a/wpilibc/src/main/native/include/frc/PWM.h b/wpilibc/src/main/native/include/frc/PWM.h
index e508915..0871c9c 100644
--- a/wpilibc/src/main/native/include/frc/PWM.h
+++ b/wpilibc/src/main/native/include/frc/PWM.h
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <hal/Types.h>
+#include <units/time.h>
 #include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
@@ -18,18 +19,9 @@
  * Class implements the PWM generation in the FPGA.
  *
  * The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They
- * are mapped to the hardware dependent values, in this case 0-2000 for the
- * FPGA. Changes are immediately sent to the FPGA, and the update occurs at the
- * next FPGA cycle (5.005ms). There is no delay.
- *
- * As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as
- * follows:
- *   - 2000 = maximum pulse width
- *   - 1999 to 1001 = linear scaling from "full forward" to "center"
- *   - 1000 = center value
- *   - 999 to 2 = linear scaling from "center" to "full reverse"
- *   - 1 = minimum pulse width (currently 0.5ms)
- *   - 0 = disabled (i.e. PWM output is held low)
+ * are mapped to the microseconds to keep the pulse high, with a range of 0
+ * (off) to 4096. Changes are immediately sent to the FPGA, and the update
+ * occurs at the next FPGA cycle (5.05ms). There is no delay.
  */
 class PWM : public wpi::Sendable, public wpi::SendableHelper<PWM> {
  public:
@@ -40,15 +32,15 @@
    */
   enum PeriodMultiplier {
     /**
-     * Don't skip pulses. PWM pulses occur every 5.005 ms
+     * Don't skip pulses. PWM pulses occur every 5.05 ms
      */
     kPeriodMultiplier_1X = 1,
     /**
-     * Skip every other pulse. PWM pulses occur every 10.010 ms
+     * Skip every other pulse. PWM pulses occur every 10.10 ms
      */
     kPeriodMultiplier_2X = 2,
     /**
-     * Skip three out of four pulses. PWM pulses occur every 20.020 ms
+     * Skip three out of four pulses. PWM pulses occur every 20.20 ms
      */
     kPeriodMultiplier_4X = 4
   };
@@ -78,30 +70,29 @@
   PWM& operator=(PWM&&) = default;
 
   /**
-   * Set the PWM value directly to the hardware.
+   * Set the PWM pulse time directly to the hardware.
    *
-   * Write a raw value to a PWM channel.
+   * Write a microsecond value to a PWM channel.
    *
-   * @param value Raw PWM value.
+   * @param time Microsecond PWM value.
    */
-  virtual void SetRaw(uint16_t value);
+  virtual void SetPulseTime(units::microsecond_t time);
 
   /**
-   * Get the PWM value directly from the hardware.
+   * Get the PWM pulse time directly from the hardware.
    *
-   * Read a raw value from a PWM channel.
+   * Read a microsecond value from a PWM channel.
    *
-   * @return Raw PWM control value.
+   * @return Microsecond PWM control value.
    */
-  virtual uint16_t GetRaw() const;
+  virtual units::microsecond_t GetPulseTime() const;
 
   /**
    * Set the PWM value based on a position.
    *
    * This is intended to be used by servos.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @param pos The position to set the servo between 0.0 and 1.0.
    */
@@ -112,8 +103,7 @@
    *
    * This is intended to be used by servos.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @return The position the servo is set to between 0.0 and 1.0.
    */
@@ -124,11 +114,7 @@
    *
    * This is intended to be used by motor controllers.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetCenterPwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @param speed The speed to set the motor controller between -1.0 and 1.0.
    */
@@ -139,17 +125,14 @@
    *
    * This is intended to be used by motor controllers.
    *
-   * @pre SetMaxPositivePwm() called.
-   * @pre SetMinPositivePwm() called.
-   * @pre SetMaxNegativePwm() called.
-   * @pre SetMinNegativePwm() called.
+   * @pre SetBounds() called.
    *
    * @return The most recently set speed between -1.0 and 1.0.
    */
   virtual double GetSpeed() const;
 
   /**
-   * Temporarily disables the PWM output. The next set call will reenable
+   * Temporarily disables the PWM output. The next set call will re-enable
    * the output.
    */
   virtual void SetDisabled();
@@ -180,46 +163,38 @@
    * The values determine the upper and lower speeds as well as the deadband
    * bracket.
    *
-   * @param max         The max PWM pulse width in ms
-   * @param deadbandMax The high end of the deadband range pulse width in ms
-   * @param center      The center (off) pulse width in ms
-   * @param deadbandMin The low end of the deadband pulse width in ms
-   * @param min         The minimum pulse width in ms
+   * @param max         The max PWM pulse width in us
+   * @param deadbandMax The high end of the deadband range pulse width in us
+   * @param center      The center (off) pulse width in us
+   * @param deadbandMin The low end of the deadband pulse width in us
+   * @param min         The minimum pulse width in us
    */
-  void SetBounds(double max, double deadbandMax, double center,
-                 double deadbandMin, double min);
-
-  /**
-   * Set the bounds on the PWM values.
-   *
-   * This sets the bounds on the PWM values for a particular each type of
-   * controller. The values determine the upper and lower speeds as well as the
-   * deadband bracket.
-   *
-   * @param max         The Minimum pwm value
-   * @param deadbandMax The high end of the deadband range
-   * @param center      The center speed (off)
-   * @param deadbandMin The low end of the deadband range
-   * @param min         The minimum pwm value
-   */
-  void SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
-                    int min);
+  void SetBounds(units::microsecond_t max, units::microsecond_t deadbandMax,
+                 units::microsecond_t center, units::microsecond_t deadbandMin,
+                 units::microsecond_t min);
 
   /**
    * Get the bounds on the PWM values.
    *
-   * This Gets the bounds on the PWM values for a particular each type of
+   * This gets the bounds on the PWM values for a particular each type of
    * controller. The values determine the upper and lower speeds as well as the
    * deadband bracket.
    *
-   * @param max         The Minimum pwm value
+   * @param max         The maximum pwm value
    * @param deadbandMax The high end of the deadband range
    * @param center      The center speed (off)
    * @param deadbandMin The low end of the deadband range
    * @param min         The minimum pwm value
    */
-  void GetRawBounds(int32_t* max, int32_t* deadbandMax, int32_t* center,
-                    int32_t* deadbandMin, int32_t* min);
+  void GetBounds(units::microsecond_t* max, units::microsecond_t* deadbandMax,
+                 units::microsecond_t* center,
+                 units::microsecond_t* deadbandMin, units::microsecond_t* min);
+
+  /**
+   * Sets the PWM output to be a continuous high signal while enabled.
+   *
+   */
+  void SetAlwaysHighMode();
 
   int GetChannel() const;
 
diff --git a/wpilibc/src/main/native/include/frc/PneumaticHub.h b/wpilibc/src/main/native/include/frc/PneumaticHub.h
index f876b7a..b69b3d5 100644
--- a/wpilibc/src/main/native/include/frc/PneumaticHub.h
+++ b/wpilibc/src/main/native/include/frc/PneumaticHub.h
@@ -46,9 +46,10 @@
    * and will turn off when the pressure reaches {@code maxPressure}.
    *
    * @param minPressure The minimum pressure. The compressor will turn on when
-   * the pressure drops below this value.
+   * the pressure drops below this value. Range 0 - 120 PSI.
    * @param maxPressure The maximum pressure. The compressor will turn off when
-   * the pressure reaches this value.
+   * the pressure reaches this value. Range 0 - 120 PSI. Must be larger then
+   * minPressure.
    */
   void EnableCompressorAnalog(
       units::pounds_per_square_inch_t minPressure,
@@ -74,10 +75,11 @@
    *
    * @param minPressure The minimum pressure. The compressor will turn on when
    * the pressure drops below this value and the pressure switch indicates that
-   * the system is not full.
+   * the system is not full.  Range 0 - 120 PSI.
    * @param maxPressure The maximum pressure. The compressor will turn off when
    * the pressure reaches this value or the pressure switch is disconnected or
-   * indicates that the system is full.
+   * indicates that the system is full. Range 0 - 120 PSI. Must be larger then
+   * minPressure.
    */
   void EnableCompressorHybrid(
       units::pounds_per_square_inch_t minPressure,
diff --git a/wpilibc/src/main/native/include/frc/PneumaticsBase.h b/wpilibc/src/main/native/include/frc/PneumaticsBase.h
index b455dc6..59f899f 100644
--- a/wpilibc/src/main/native/include/frc/PneumaticsBase.h
+++ b/wpilibc/src/main/native/include/frc/PneumaticsBase.h
@@ -117,15 +117,15 @@
   /**
    * Sets solenoids on a pneumatics module.
    *
-   * @param mask mask
-   * @param values values
+   * @param mask bitmask to set
+   * @param values solenoid values
    */
   virtual void SetSolenoids(int mask, int values) = 0;
 
   /**
    * Gets a bitmask of solenoid values.
    *
-   * @return values
+   * @return solenoid values
    */
   virtual int GetSolenoids() const = 0;
 
@@ -183,8 +183,16 @@
    */
   virtual void UnreserveSolenoids(int mask) = 0;
 
+  /**
+   * Reserve the compressor.
+   *
+   * @return true if successful; false if compressor already reserved
+   */
   virtual bool ReserveCompressor() = 0;
 
+  /**
+   * Unreserve the compressor.
+   */
   virtual void UnreserveCompressor() = 0;
 
   /**
@@ -212,9 +220,29 @@
    */
   virtual units::pounds_per_square_inch_t GetPressure(int channel) const = 0;
 
+  /**
+   * Create a solenoid object for the specified channel.
+   *
+   * @param channel solenoid channel
+   * @return Solenoid object
+   */
   virtual Solenoid MakeSolenoid(int channel) = 0;
+
+  /**
+   * Create a double solenoid object for the specified channels.
+   *
+   * @param forwardChannel solenoid channel for forward
+   * @param reverseChannel solenoid channel for reverse
+   * @return DoubleSolenoid object
+   */
   virtual DoubleSolenoid MakeDoubleSolenoid(int forwardChannel,
                                             int reverseChannel) = 0;
+
+  /**
+   * Create a compressor object.
+   *
+   * @return Compressor object
+   */
   virtual Compressor MakeCompressor() = 0;
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h b/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
index acad5a1..3dd6d3a 100644
--- a/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
+++ b/wpilibc/src/main/native/include/frc/PneumaticsControlModule.h
@@ -118,7 +118,7 @@
   /**
    * Returns whether the compressor has been disconnected since sticky faults
    * were last cleared. This fault is persistent and can be cleared by
-   * ClearAllStickyFaults()}
+   * ClearAllStickyFaults()
    *
    * @return True if the compressor has been disconnected since sticky faults
    * were last cleared, otherwise false.
@@ -126,7 +126,22 @@
    */
   bool GetCompressorNotConnectedStickyFault() const;
 
+  /**
+   * Returns whether the solenoid is currently reporting a voltage fault.
+   *
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see GetSolenoidVoltageStickyFault()
+   */
   bool GetSolenoidVoltageFault() const;
+
+  /**
+   * Returns whether the solenoid has reported a voltage fault since sticky
+   * faults were last cleared. This fault is persistent and can be cleared by
+   * ClearAllStickyFaults()
+   *
+   * @return True if solenoid is reporting a fault, otherwise false.
+   * @see GetSolenoidVoltageFault()
+   */
   bool GetSolenoidVoltageStickyFault() const;
 
   /** Clears all sticky faults on this device. */
diff --git a/wpilibc/src/main/native/include/frc/Resource.h b/wpilibc/src/main/native/include/frc/Resource.h
index 4109cc4..6d9dfa5 100644
--- a/wpilibc/src/main/native/include/frc/Resource.h
+++ b/wpilibc/src/main/native/include/frc/Resource.h
@@ -17,7 +17,7 @@
 /**
  * The Resource class is a convenient way to track allocated resources.
  *
- * It tracks them as indicies in the range [0 .. elements - 1]. E.g. the library
+ * It tracks them as indices in the range [0 .. elements - 1]. E.g. the library
  * uses this to track hardware channel allocation.
  *
  * The Resource class does not allocate the hardware channels or other
@@ -46,7 +46,7 @@
    * Allocate storage for a new instance of Resource.
    *
    * Allocate a bool array of values that will get initialized to indicate that
-   * no resources have been allocated yet. The indicies of the resources are
+   * no resources have been allocated yet. The indices of the resources are
    * [0 .. elements - 1].
    */
   explicit Resource(uint32_t size);
diff --git a/wpilibc/src/main/native/include/frc/RobotBase.h b/wpilibc/src/main/native/include/frc/RobotBase.h
index 3ff4617..504ac81 100644
--- a/wpilibc/src/main/native/include/frc/RobotBase.h
+++ b/wpilibc/src/main/native/include/frc/RobotBase.h
@@ -115,28 +115,28 @@
 }
 
 /**
- * Implement a Robot Program framework.
+ * Implement a Robot Program framework. The RobotBase class is intended to be
+ * subclassed to create a robot program. The user must implement
+ * StartCompetition() which will be called once and is not expected to exit. The
+ * user must also implement EndCompetition(), which signals to the code in
+ * StartCompetition() that it should exit.
  *
- * The RobotBase class is intended to be subclassed by a user creating a robot
- * program. Overridden Autonomous() and OperatorControl() methods are called at
- * the appropriate time as the match proceeds. In the current implementation,
- * the Autonomous code will run to completion before the OperatorControl code
- * could start. In the future the Autonomous code might be spawned as a task,
- * then killed at the end of the Autonomous period.
+ * It is not recommended to subclass this class directly - instead subclass
+ * IterativeRobotBase or TimedRobot.
  */
 class RobotBase {
  public:
   /**
    * Determine if the Robot is currently enabled.
    *
-   * @return True if the Robot is currently enabled by the field controls.
+   * @return True if the Robot is currently enabled by the Driver Station.
    */
   bool IsEnabled() const;
 
   /**
    * Determine if the Robot is currently disabled.
    *
-   * @return True if the Robot is currently disabled by the field controls.
+   * @return True if the Robot is currently disabled by the Driver Station.
    */
   bool IsDisabled() const;
 
@@ -144,7 +144,7 @@
    * Determine if the robot is currently in Autonomous mode.
    *
    * @return True if the robot is currently operating Autonomously as determined
-   *         by the field controls.
+   *         by the Driver Station.
    */
   bool IsAutonomous() const;
 
@@ -152,7 +152,7 @@
    * Determine if the robot is currently in Autonomous mode and enabled.
    *
    * @return True if the robot us currently operating Autonomously while enabled
-   * as determined by the field controls.
+   * as determined by the Driver Station.
    */
   bool IsAutonomousEnabled() const;
 
@@ -160,7 +160,7 @@
    * Determine if the robot is currently in Operator Control mode.
    *
    * @return True if the robot is currently operating in Tele-Op mode as
-   *         determined by the field controls.
+   *         determined by the Driver Station.
    */
   bool IsTeleop() const;
 
@@ -168,25 +168,38 @@
    * Determine if the robot is current in Operator Control mode and enabled.
    *
    * @return True if the robot is currently operating in Tele-Op mode while
-   * wnabled as determined by the field-controls.
+   * enabled as determined by the Driver Station.
    */
   bool IsTeleopEnabled() const;
 
   /**
    * Determine if the robot is currently in Test mode.
    *
-   * @return True if the robot is currently running tests as determined by the
-   *         field controls.
+   * @return True if the robot is currently running in Test mode as determined
+   * by the Driver Station.
    */
   bool IsTest() const;
 
   /**
+   * Determine if the robot is current in Test mode and enabled.
+   *
+   * @return True if the robot is currently operating in Test mode while
+   * enabled as determined by the Driver Station.
+   */
+  bool IsTestEnabled() const;
+
+  /**
    * Gets the ID of the main robot thread.
    */
   static std::thread::id GetThreadId();
 
+  /**
+   * Start the main robot code. This function will be called once and should not
+   * exit until signalled by EndCompetition()
+   */
   virtual void StartCompetition() = 0;
 
+  /** Ends the main loop in StartCompetition(). */
   virtual void EndCompetition() = 0;
 
   /**
@@ -215,13 +228,17 @@
    * @return If the robot is running in simulation.
    */
   static constexpr bool IsSimulation() {
-    return !IsReal();
+#ifdef __FRC_ROBORIO__
+    return false;
+#else
+    return true;
+#endif
   }
 
   /**
    * Constructor for a generic robot program.
    *
-   * User code should be placed in the constructor that runs before the
+   * User code can be placed in the constructor that runs before the
    * Autonomous or Operator Control period starts. The constructor will run to
    * completion before Autonomous is entered.
    *
diff --git a/wpilibc/src/main/native/include/frc/RobotController.h b/wpilibc/src/main/native/include/frc/RobotController.h
index c020d34..bf65a8c 100644
--- a/wpilibc/src/main/native/include/frc/RobotController.h
+++ b/wpilibc/src/main/native/include/frc/RobotController.h
@@ -8,6 +8,7 @@
 
 #include <string>
 
+#include <units/temperature.h>
 #include <units/voltage.h>
 
 namespace frc {
@@ -63,6 +64,13 @@
   static std::string GetComments();
 
   /**
+   * Returns the team number configured for the robot controller.
+   *
+   * @return team number, or 0 if not found.
+   */
+  static int32_t GetTeamNumber();
+
+  /**
    * Read the microsecond-resolution timer on the FPGA.
    *
    * @return The current time in microseconds according to the FPGA (since FPGA
@@ -102,6 +110,19 @@
   static bool IsBrownedOut();
 
   /**
+   * Gets the current state of the Robot Signal Light (RSL)
+   * @return The current state of the RSL- true if on, false if off
+   */
+  static bool GetRSLState();
+
+  /**
+   * Gets if the system time is valid.
+   *
+   * @return True if the system time is valid, false otherwise
+   */
+  static bool IsSystemTimeValid();
+
+  /**
    * Get the input voltage to the robot controller.
    *
    * @return The controller input voltage value in Volts
@@ -130,9 +151,16 @@
   static double GetCurrent3V3();
 
   /**
-   * Get the enabled state of the 3.3V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 3.3V rail.
+   *
+   * @param enabled whether to enable the 3.3V rail.
+   */
+  static void SetEnabled3V3(bool enabled);
+
+  /**
+   * Get the enabled state of the 3.3V rail. The rail may be disabled due to
+   * calling SetEnabled3V3(), a controller brownout, a short circuit on the
+   * rail, or controller over-voltage.
    *
    * @return The controller 3.3V rail enabled value. True for enabled.
    */
@@ -161,9 +189,16 @@
   static double GetCurrent5V();
 
   /**
-   * Get the enabled state of the 5V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 5V rail.
+   *
+   * @param enabled whether to enable the 5V rail.
+   */
+  static void SetEnabled5V(bool enabled);
+
+  /**
+   * Get the enabled state of the 5V rail. The rail may be disabled due to
+   * calling SetEnabled5V(), a controller brownout, a short circuit on the rail,
+   * or controller over-voltage.
    *
    * @return The controller 5V rail enabled value. True for enabled.
    */
@@ -192,9 +227,16 @@
   static double GetCurrent6V();
 
   /**
-   * Get the enabled state of the 6V rail. The rail may be disabled due to a
-   * controller brownout, a short circuit on the rail, or controller
-   * over-voltage.
+   * Enables or disables the 6V rail.
+   *
+   * @param enabled whether to enable the 6V rail.
+   */
+  static void SetEnabled6V(bool enabled);
+
+  /**
+   * Get the enabled state of the 6V rail. The rail may be disabled due to
+   * calling SetEnabled6V(), a controller brownout, a short circuit on the rail,
+   * or controller over-voltage.
    *
    * @return The controller 6V rail enabled value. True for enabled.
    */
@@ -226,6 +268,13 @@
   static void SetBrownoutVoltage(units::volt_t brownoutVoltage);
 
   /**
+   * Get the current CPU temperature.
+   *
+   * @return current CPU temperature
+   */
+  static units::celsius_t GetCPUTemp();
+
+  /**
    * Get the current status of the CAN bus.
    *
    * @return The status of the CAN bus
diff --git a/wpilibc/src/main/native/include/frc/SPI.h b/wpilibc/src/main/native/include/frc/SPI.h
index 0d5b468..1158cbd 100644
--- a/wpilibc/src/main/native/include/frc/SPI.h
+++ b/wpilibc/src/main/native/include/frc/SPI.h
@@ -11,7 +11,6 @@
 
 #include <hal/SPITypes.h>
 #include <units/time.h>
-#include <wpi/deprecated.h>
 
 namespace frc {
 
@@ -28,10 +27,12 @@
  public:
   enum Port { kOnboardCS0 = 0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP };
   enum Mode {
-    kMode0 = HAL_SPI_kMode0,
-    kMode1 = HAL_SPI_kMode1,
-    kMode2 = HAL_SPI_kMode2,
-    kMode3 = HAL_SPI_kMode3
+    kMode0 = HAL_SPI_kMode0, /*!< Clock idle low, data sampled on rising edge */
+    kMode1 =
+        HAL_SPI_kMode1, /*!< Clock idle low, data sampled on falling edge */
+    kMode2 =
+        HAL_SPI_kMode2, /*!< Clock idle high, data sampled on falling edge */
+    kMode3 = HAL_SPI_kMode3 /*!< Clock idle high, data sampled on rising edge */
   };
 
   /**
@@ -59,60 +60,6 @@
   void SetClockRate(int hz);
 
   /**
-   * Configure the order that bits are sent and received on the wire
-   * to be most significant bit first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  WPI_DEPRECATED("Not supported by roboRIO.")
-  void SetMSBFirst();
-
-  /**
-   * Configure the order that bits are sent and received on the wire
-   * to be least significant bit first.
-   *
-   * @deprecated Does not work, will be removed.
-   */
-  WPI_DEPRECATED("Not supported by roboRIO.")
-  void SetLSBFirst();
-
-  /**
-   * Configure that the data is stable on the leading edge and the data
-   * changes on the trailing edge.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetSampleDataOnLeadingEdge();
-
-  /**
-   * Configure that the data is stable on the trailing edge and the data
-   * changes on the leading edge.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetSampleDataOnTrailingEdge();
-
-  /**
-   * Configure the clock output line to be active low.
-   * This is sometimes called clock polarity high or clock idle high.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetClockActiveLow();
-
-  /**
-   * Configure the clock output line to be active high.
-   * This is sometimes called clock polarity low or clock idle low.
-   *
-   * @deprecated Use SetMode() instead.
-   */
-  WPI_DEPRECATED("Use SetMode() instead")
-  void SetClockActiveHigh();
-
-  /**
    * Sets the mode for the SPI device.
    *
    * <p>Mode 0 is Clock idle low, data sampled on rising edge
diff --git a/wpilibc/src/main/native/include/frc/SerialPort.h b/wpilibc/src/main/native/include/frc/SerialPort.h
index 7fff14a..dcc0a2e 100644
--- a/wpilibc/src/main/native/include/frc/SerialPort.h
+++ b/wpilibc/src/main/native/include/frc/SerialPort.h
@@ -16,7 +16,7 @@
  * Driver for the RS-232 serial port on the roboRIO.
  *
  * The current implementation uses the VISA formatted I/O mode.  This means that
- * all traffic goes through the fomatted buffers.  This allows the intermingled
+ * all traffic goes through the formatted buffers.  This allows the intermingled
  * use of Printf(), Scanf(), and the raw buffer accessors Read() and Write().
  *
  * More information can be found in the NI-VISA User Manual here:
diff --git a/wpilibc/src/main/native/include/frc/Servo.h b/wpilibc/src/main/native/include/frc/Servo.h
index 96cbfa4..9383641 100644
--- a/wpilibc/src/main/native/include/frc/Servo.h
+++ b/wpilibc/src/main/native/include/frc/Servo.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <units/angle.h>
+
 #include "frc/PWM.h"
 
 namespace frc {
@@ -98,11 +100,11 @@
  private:
   double GetServoAngleRange() const;
 
-  static constexpr double kMaxServoAngle = 180.0;
+  static constexpr double kMaxServoAngle = 180.;
   static constexpr double kMinServoAngle = 0.0;
 
-  static constexpr double kDefaultMaxServoPWM = 2.4;
-  static constexpr double kDefaultMinServoPWM = 0.6;
+  static constexpr units::millisecond_t kDefaultMaxServoPWM = 2.4_ms;
+  static constexpr units::millisecond_t kDefaultMinServoPWM = 0.6_ms;
 };
 
 }  // namespace frc
diff --git a/wpilibc/src/main/native/include/frc/Solenoid.h b/wpilibc/src/main/native/include/frc/Solenoid.h
index a09ffc0..7fa4953 100644
--- a/wpilibc/src/main/native/include/frc/Solenoid.h
+++ b/wpilibc/src/main/native/include/frc/Solenoid.h
@@ -87,18 +87,22 @@
   bool IsDisabled() const;
 
   /**
-   * Set the pulse duration in the PCM. This is used in conjunction with
-   * the startPulse method to allow the PCM to control the timing of a pulse.
-   * The timing can be controlled in 0.01 second increments.
+   * Set the pulse duration in the pneumatics module. This is used in
+   * conjunction with the startPulse method to allow the pneumatics module to
+   * control the timing of a pulse.
    *
-   * @param duration The duration of the pulse, from 0.01 to 2.55 seconds.
+   * On the PCM, the timing can be controlled in 0.01 second increments, with a
+   * maximum of 2.55 seconds. On the PH, the timing can be controlled in 0.001
+   * second increments, with a maximum of 65.534 seconds.
+   *
+   * @param duration The duration of the pulse.
    *
    * @see startPulse()
    */
   void SetPulseDuration(units::second_t duration);
 
   /**
-   * %Trigger the PCM to generate a pulse of the duration set in
+   * %Trigger the pneumatics module to generate a pulse of the duration set in
    * setPulseDuration.
    *
    * @see setPulseDuration()
diff --git a/wpilibc/src/main/native/include/frc/Timer.h b/wpilibc/src/main/native/include/frc/Timer.h
index 14674ee..e163982 100644
--- a/wpilibc/src/main/native/include/frc/Timer.h
+++ b/wpilibc/src/main/native/include/frc/Timer.h
@@ -77,6 +77,14 @@
   void Start();
 
   /**
+   * Restart the timer by stopping the timer, if it is not already stopped,
+   * resetting the accumulated time, then starting the timer again. If you
+   * want an event to periodically reoccur at some time interval from the
+   * start time, consider using AdvanceIfElapsed() instead.
+   */
+  void Restart();
+
+  /**
    * Stop the timer.
    *
    * This computes the time as of now and clears the running flag, causing all
diff --git a/wpilibc/src/main/native/include/frc/XboxController.h b/wpilibc/src/main/native/include/frc/XboxController.h
index 370e46e..3caba1a 100644
--- a/wpilibc/src/main/native/include/frc/XboxController.h
+++ b/wpilibc/src/main/native/include/frc/XboxController.h
@@ -36,63 +36,87 @@
 
   /**
    * Get the X axis value of left side of the controller.
+   *
+   * @return the axis value
    */
   double GetLeftX() const;
 
   /**
    * Get the X axis value of right side of the controller.
+   *
+   * @return the axis value
    */
   double GetRightX() const;
 
   /**
    * Get the Y axis value of left side of the controller.
+   *
+   * @return the axis value
    */
   double GetLeftY() const;
 
   /**
    * Get the Y axis value of right side of the controller.
+   *
+   * @return the axis value
    */
   double GetRightY() const;
 
   /**
    * Get the left trigger (LT) axis value of the controller. Note that this axis
    * is bound to the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value
    */
   double GetLeftTriggerAxis() const;
 
   /**
    * Get the right trigger (RT) axis value of the controller. Note that this
    * axis is bound to the range of [0, 1] as opposed to the usual [-1, 1].
+   *
+   * @return the axis value
    */
   double GetRightTriggerAxis() const;
 
   /**
    * Read the value of the left bumper (LB) button on the controller.
+   *
+   * @return the state of the button
    */
   bool GetLeftBumper() const;
 
   /**
    * Read the value of the right bumper (RB) button on the controller.
+   *
+   * @return the state of the button
    */
   bool GetRightBumper() const;
 
   /**
    * Whether the left bumper (LB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetLeftBumperPressed();
 
   /**
    * Whether the right bumper (RB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetRightBumperPressed();
 
   /**
    * Whether the left bumper (LB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetLeftBumperReleased();
 
   /**
    * Whether the right bumper (RB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetRightBumperReleased();
 
@@ -116,31 +140,43 @@
 
   /**
    * Read the value of the left stick button (LSB) on the controller.
+   *
+   * @return the state of the button
    */
   bool GetLeftStickButton() const;
 
   /**
    * Read the value of the right stick button (RSB) on the controller.
+   *
+   * @return the state of the button
    */
   bool GetRightStickButton() const;
 
   /**
    * Whether the left stick button (LSB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check.
    */
   bool GetLeftStickButtonPressed();
 
   /**
    * Whether the right stick button (RSB) was pressed since the last check.
+   *
+   * @return Whether the button was pressed since the last check
    */
   bool GetRightStickButtonPressed();
 
   /**
    * Whether the left stick button (LSB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetLeftStickButtonReleased();
 
   /**
    * Whether the right stick button (RSB) was released since the last check.
+   *
+   * @return Whether the button was released since the last check.
    */
   bool GetRightStickButtonReleased();
 
@@ -283,9 +319,9 @@
   BooleanEvent Y(EventLoop* loop) const;
 
   /**
-   * Whether the Y button was released since the last check.
+   * Read the value of the back button on the controller.
    *
-   * @return Whether the button was released since the last check.
+   * @return The state of the button.
    */
   bool GetBackButton() const;
 
@@ -359,7 +395,7 @@
    * Constructs an event instance around the axis value of the left trigger.
    * The returned trigger will be true when the axis value is greater than 0.5.
    * @param loop the event loop instance to attach the event to.
-   * @return an event instance that is true when the right trigger's axis
+   * @return an event instance that is true when the left trigger's axis
    * exceeds 0.5, attached to the given event loop
    */
   BooleanEvent LeftTrigger(EventLoop* loop) const;
diff --git a/wpilibc/src/main/native/include/frc/event/BooleanEvent.h b/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
index 745a53c..7c19c90 100644
--- a/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
+++ b/wpilibc/src/main/native/include/frc/event/BooleanEvent.h
@@ -40,9 +40,10 @@
   BooleanEvent(EventLoop* loop, std::function<bool()> condition);
 
   /**
-   * Check whether this event is active or not.
+   * Check whether this event is active or not as of the last loop poll.
    *
-   * @return true if active.
+   * @return true if active, false if not active. If the event was never polled,
+   * it returns the state at event construction.
    */
   bool GetAsBoolean() const;
 
@@ -69,7 +70,7 @@
                [](EventLoop* loop, std::function<bool()> condition) {
                  return T(loop, condition);
                }) {
-    return ctor(m_loop, m_condition);
+    return ctor(m_loop, [state = m_state] { return *state; });
   }
 
   /**
@@ -84,7 +85,8 @@
    * Composes this event with another event, returning a new event that is
    * active when both events are active.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different
+   * event loops, the composed signal won't update until both loops are polled.
    *
    * @param rhs the event to compose with
    * @return the event that is active when both events are active
@@ -95,7 +97,8 @@
    * Composes this event with another event, returning a new event that is
    * active when either event is active.
    *
-   * <p>The new event will use this event's polling loop.
+   * <p>The events must use the same event loop. If the events use different
+   * event loops, the composed signal won't update until both loops are polled.
    *
    * @param rhs the event to compose with
    * @return the event that is active when either event is active
@@ -131,5 +134,6 @@
  private:
   EventLoop* m_loop;
   std::function<bool()> m_condition;
+  std::shared_ptr<bool> m_state;  // A programmer's worst nightmare.
 };
 }  // namespace frc
diff --git a/wpilibc/src/main/native/include/frc/event/EventLoop.h b/wpilibc/src/main/native/include/frc/event/EventLoop.h
index d18fac3..224dd3b 100644
--- a/wpilibc/src/main/native/include/frc/event/EventLoop.h
+++ b/wpilibc/src/main/native/include/frc/event/EventLoop.h
@@ -10,8 +10,8 @@
 #include <wpi/FunctionExtras.h>
 
 namespace frc {
-/** The loop polling BooleanEvent objects and executing the actions bound to
- * them. */
+/** A declarative way to bind a set of actions to a loop and execute them when
+ * the loop is polled. */
 class EventLoop {
  public:
   EventLoop();
@@ -20,7 +20,7 @@
   EventLoop& operator=(const EventLoop&) = delete;
 
   /**
-   * Bind a new action to run.
+   * Bind a new action to run when the loop is polled.
    *
    * @param action the action to run.
    */
diff --git a/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h b/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
index c95466a..77f1d5b 100644
--- a/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
+++ b/wpilibc/src/main/native/include/frc/interfaces/Accelerometer.h
@@ -8,8 +8,11 @@
 
 /**
  * Interface for 3-axis accelerometers.
+ *
+ * @deprecated This interface is being removed with no replacement.
  */
-class Accelerometer {
+class [[deprecated(
+    "This interface is being removed with no replacement.")]] Accelerometer {
  public:
   Accelerometer() = default;
   virtual ~Accelerometer() = default;
diff --git a/wpilibc/src/main/native/include/frc/interfaces/Gyro.h b/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
index b74a3cf..51fea7d 100644
--- a/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
+++ b/wpilibc/src/main/native/include/frc/interfaces/Gyro.h
@@ -12,8 +12,11 @@
 
 /**
  * Interface for yaw rate gyros.
+ *
+ * @deprecated This interface is being removed with no replacement.
  */
-class Gyro {
+class [[deprecated(
+    "This interface is being removed with no replacement.")]] Gyro {
  public:
   Gyro() = default;
   virtual ~Gyro() = default;
diff --git a/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h b/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
index 4533086..2a5fcae 100644
--- a/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/AddressableLEDSim.h
@@ -60,7 +60,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object storing this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -85,7 +86,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOutputPortCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOutputPortCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -110,8 +112,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterLengthCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterLengthCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Get the length of the LED strip.
@@ -135,7 +138,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRunningCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRunningCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -160,7 +164,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDataCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDataCallback(
       ConstBufferCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h b/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
index faa11d7..0210818 100644
--- a/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/AnalogGyroSim.h
@@ -40,8 +40,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAngleCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAngleCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Get the current angle of the gyro.
@@ -64,8 +65,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRateCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRateCallback(NotifyCallback callback,
+                                                      bool initialNotify);
 
   /**
    * Get the rate of angle change on this gyro.
@@ -89,7 +91,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h b/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
index 03d7548..05c9898 100644
--- a/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/AnalogInputSim.h
@@ -41,7 +41,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -66,7 +67,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAverageBitsCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAverageBitsCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -91,7 +93,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOversampleBitsCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOversampleBitsCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -116,7 +119,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -141,9 +145,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorInitializedCallback(NotifyCallback callback,
-                                         bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorInitializedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the accumulator has been initialized.
@@ -167,7 +171,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAccumulatorValueCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorValueCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -192,7 +197,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterAccumulatorCountCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorCountCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -217,9 +223,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorCenterCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorCenterCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the accumulator center.
@@ -243,9 +249,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterAccumulatorDeadbandCallback(NotifyCallback callback,
-                                      bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterAccumulatorDeadbandCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the accumulator deadband.
diff --git a/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h b/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
index ffec03a..42eb6c1 100644
--- a/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/AnalogOutputSim.h
@@ -40,7 +40,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -64,7 +65,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h b/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
index 04e9e9b..019a9a9 100644
--- a/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/AnalogTriggerSim.h
@@ -53,7 +53,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,9 +79,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterTriggerLowerBoundCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTriggerLowerBoundCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the lower bound.
@@ -104,9 +105,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterTriggerUpperBoundCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTriggerUpperBoundCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the upper bound.
diff --git a/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h b/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
index 9ffcf5b..74a4f16 100644
--- a/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/BuiltInAccelerometerSim.h
@@ -40,8 +40,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterActiveCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterActiveCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Check whether the accelerometer is active.
@@ -64,8 +65,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRangeCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRangeCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check the range of this accelerometer.
@@ -88,8 +90,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterXCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterXCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the X axis value.
@@ -112,8 +115,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterYCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterYCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Y axis value.
@@ -136,8 +140,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Z axis value.
diff --git a/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h b/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
index 96959ed..37113db 100644
--- a/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/CTREPCMSim.h
@@ -33,21 +33,24 @@
 
   ~CTREPCMSim() override = default;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetInitialized() const override;
 
   void SetInitialized(bool initialized) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
       int channel, NotifyCallback callback, bool initialNotify) override;
 
   bool GetSolenoidOutput(int channel) const override;
 
   void SetSolenoidOutput(int channel, bool solenoidOutput) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetCompressorOn() const override;
@@ -62,9 +65,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterClosedLoopEnabledCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterClosedLoopEnabledCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the closed loop compressor control is active.
@@ -88,7 +91,8 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   /**
@@ -112,9 +116,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) override;
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) override;
 
   /**
    * Read the compressor current.
diff --git a/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h b/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
index b1388bd..be3a325 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DCMotorSim.h
@@ -21,7 +21,9 @@
   /**
    * Creates a simulated DC motor mechanism.
    *
-   * @param plant              The linear system representing the DC motor.
+   * @param plant              The linear system representing the DC motor. This
+   *                           system can be created with
+   *                           LinearSystemId::DCMotorSystem().
    * @param gearbox            The type of and number of motors in the DC motor
    * gearbox.
    * @param gearing            The gearing of the DC motor (numbers greater than
@@ -46,6 +48,17 @@
              units::kilogram_square_meter_t moi,
              const std::array<double, 2>& measurementStdDevs = {0.0, 0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the state of the DC motor.
+   *
+   * @param angularPosition The new position
+   * @param angularVelocity The new velocity
+   */
+  void SetState(units::radian_t angularPosition,
+                units::radians_per_second_t angularVelocity);
+
   /**
    * Returns the DC motor position.
    *
diff --git a/wpilibc/src/main/native/include/frc/simulation/DIOSim.h b/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
index 9bbd3fb..5264c02 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DIOSim.h
@@ -48,7 +48,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -73,8 +74,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterValueCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterValueCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Read the value of the DIO port.
@@ -97,7 +99,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPulseLengthCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPulseLengthCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -122,7 +125,8 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterIsInputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterIsInputCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -147,7 +151,8 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterFilterIndexCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterFilterIndexCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h b/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
index c1cc1d7..78310d7 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DifferentialDrivetrainSim.h
@@ -19,7 +19,7 @@
 class DifferentialDrivetrainSim {
  public:
   /**
-   * Create a SimDrivetrain.
+   * Creates a simulated differential drivetrain.
    *
    * @param plant The LinearSystem representing the robot's drivetrain. This
    *              system can be created with
@@ -46,7 +46,7 @@
       const std::array<double, 7>& measurementStdDevs = {});
 
   /**
-   * Create a SimDrivetrain.
+   * Creates a simulated differential drivetrain.
    *
    * @param driveMotor  A DCMotor representing the left side of the drivetrain.
    * @param gearing     The gearing on the drive between motor and wheel, as
@@ -80,7 +80,7 @@
    * @param u The input vector.
    * @return The normalized input.
    */
-  Vectord<2> ClampInput(const Vectord<2>& u);
+  Eigen::Vector2d ClampInput(const Eigen::Vector2d& u);
 
   /**
    * Sets the applied voltage to the drivetrain. Note that positive voltage must
@@ -187,7 +187,7 @@
    */
   void SetPose(const frc::Pose2d& pose);
 
-  Vectord<7> Dynamics(const Vectord<7>& x, const Vectord<2>& u);
+  Vectord<7> Dynamics(const Vectord<7>& x, const Eigen::Vector2d& u);
 
   class State {
    public:
@@ -325,7 +325,7 @@
   double m_currentGearing;
 
   Vectord<7> m_x;
-  Vectord<2> m_u;
+  Eigen::Vector2d m_u;
   Vectord<7> m_y;
   std::array<double, 7> m_measurementStdDevs;
 };
diff --git a/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h b/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
index 53caee7..f9b974d 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DigitalPWMSim.h
@@ -54,7 +54,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,7 +79,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDutyCycleCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDutyCycleCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -102,8 +104,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPinCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPinCallback(NotifyCallback callback,
+                                                     bool initialNotify);
 
   /**
    * Check the pin number.
diff --git a/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h b/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
index 232c123..acc510c 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DriverStationSim.h
@@ -26,7 +26,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterEnabledCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterEnabledCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -51,8 +52,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterAutonomousCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterAutonomousCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the DS is in autonomous.
@@ -76,7 +78,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterTestCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterTestCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -101,7 +104,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterEStopCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterEStopCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -126,8 +130,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterFmsAttachedCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterFmsAttachedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the FMS is connected.
@@ -151,8 +156,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterDsAttachedCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterDsAttachedCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check if the DS is attached.
@@ -176,9 +182,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterAllianceStationIdCallback(NotifyCallback callback,
-                                    bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterAllianceStationIdCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the alliance station ID (color + number).
@@ -202,7 +208,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore> RegisterMatchTimeCallback(
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterMatchTimeCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h b/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
index 19577bb..18c04f0 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DutyCycleEncoderSim.h
@@ -26,20 +26,82 @@
   explicit DutyCycleEncoderSim(const DutyCycleEncoder& encoder);
 
   /**
-   * Set the position tin turns.
+   * Constructs from a digital input channel.
+   *
+   * @param channel digital input channel
+   */
+  explicit DutyCycleEncoderSim(int channel);
+
+  /**
+   * Get the position in turns.
+   *
+   * @return The position.
+   */
+  double Get();
+
+  /**
+   * Set the position in turns.
    *
    * @param turns The position.
    */
   void Set(units::turn_t turns);
 
   /**
-   * Set the position.
+   * Get the distance.
+   *
+   * @return The distance.
+   */
+
+  double GetDistance();
+
+  /**
+   * Set the distance.
+   *
+   * @param distance The distance.
    */
   void SetDistance(double distance);
 
+  /**
+   * Get the absolute position.
+   *
+   * @return The absolute position
+   */
+  double GetAbsolutePosition();
+
+  /**
+   * Set the absolute position.
+   *
+   * @param position The absolute position
+   */
+  void SetAbsolutePosition(double position);
+
+  /**
+   * Get the distance per rotation for this encoder.
+   *
+   * @return The scale factor that will be used to convert rotation to useful
+   * units.
+   */
+  double GetDistancePerRotation();
+
+  /**
+   * Get if the encoder is connected.
+   *
+   * @return true if the encoder is connected.
+   */
+  bool IsConnected();
+
+  /**
+   * Set if the encoder is connected.
+   *
+   * @param isConnected Whether or not the sensor is connected.
+   */
+  void SetConnected(bool isConnected);
+
  private:
   hal::SimDouble m_simPosition;
   hal::SimDouble m_simDistancePerRotation;
+  hal::SimDouble m_simAbsolutePosition;
+  hal::SimBoolean m_simIsConnected;
 };
 
 }  // namespace sim
diff --git a/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h b/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
index ec7b48c..f6f89eb 100644
--- a/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/DutyCycleSim.h
@@ -51,7 +51,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -75,7 +76,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterFrequencyCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterFrequencyCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -99,8 +101,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterOutputCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterOutputCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Measure the output from this duty cycle port.
diff --git a/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h b/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
index cf40810..3d5c433 100644
--- a/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/ElevatorSim.h
@@ -19,25 +19,32 @@
  */
 class ElevatorSim : public LinearSystemSim<2, 1, 1> {
  public:
+  template <typename Distance>
+  using Velocity_t = units::unit_t<
+      units::compound_unit<Distance, units::inverse<units::seconds>>>;
+
+  template <typename Distance>
+  using Acceleration_t = units::unit_t<units::compound_unit<
+      units::compound_unit<Distance, units::inverse<units::seconds>>,
+      units::inverse<units::seconds>>>;
+
   /**
    * Constructs a simulated elevator mechanism.
    *
    * @param plant              The linear system that represents the elevator.
+   *                           This system can be created with
+   *                           LinearSystemId::ElevatorSystem().
    * @param gearbox            The type of and number of motors in your
    *                           elevator gearbox.
-   * @param gearing            The gearing of the elevator (numbers greater
-   *                           than 1 represent reductions).
-   * @param drumRadius         The radius of the drum that your cable is
-   *                           wrapped around.
    * @param minHeight          The minimum allowed height of the elevator.
    * @param maxHeight          The maximum allowed height of the elevator.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
    * @param measurementStdDevs The standard deviation of the measurements.
    */
   ElevatorSim(const LinearSystem<2, 1, 1>& plant, const DCMotor& gearbox,
-              double gearing, units::meter_t drumRadius,
               units::meter_t minHeight, units::meter_t maxHeight,
-              bool simulateGravity,
+              bool simulateGravity, units::meter_t startingHeight,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
   /**
@@ -53,15 +60,48 @@
    * @param minHeight          The minimum allowed height of the elevator.
    * @param maxHeight          The maximum allowed height of the elevator.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
    * @param measurementStdDevs The standard deviation of the measurements.
    */
   ElevatorSim(const DCMotor& gearbox, double gearing,
               units::kilogram_t carriageMass, units::meter_t drumRadius,
               units::meter_t minHeight, units::meter_t maxHeight,
-              bool simulateGravity,
+              bool simulateGravity, units::meter_t startingHeight,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
   /**
+   * Constructs a simulated elevator mechanism.
+   *
+   * @param kV                 The velocity gain.
+   * @param kA                 The acceleration gain.
+   * @param gearbox            The type of and number of motors in your
+   *                           elevator gearbox.
+   * @param minHeight          The minimum allowed height of the elevator.
+   * @param maxHeight          The maximum allowed height of the elevator.
+   * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingHeight     The starting height of the elevator.
+   * @param measurementStdDevs The standard deviation of the measurements.
+   */
+  template <typename Distance>
+    requires std::same_as<units::meter, Distance> ||
+             std::same_as<units::radian, Distance>
+  ElevatorSim(decltype(1_V / Velocity_t<Distance>(1)) kV,
+              decltype(1_V / Acceleration_t<Distance>(1)) kA,
+              const DCMotor& gearbox, units::meter_t minHeight,
+              units::meter_t maxHeight, bool simulateGravity,
+              units::meter_t startingHeight,
+              const std::array<double, 1>& measurementStdDevs = {0.0});
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the elevator's state. The new position will be limited between the
+   * minimum and maximum allowed heights.
+   * @param position The new position
+   * @param velocity The new velocity
+   */
+  void SetState(units::meter_t position, units::meters_per_second_t velocity);
+
+  /**
    * Returns whether the elevator would hit the lower limit.
    *
    * @param elevatorHeight The elevator height.
@@ -132,10 +172,8 @@
 
  private:
   DCMotor m_gearbox;
-  units::meter_t m_drumRadius;
   units::meter_t m_minHeight;
   units::meter_t m_maxHeight;
-  double m_gearing;
   bool m_simulateGravity;
 };
 }  // namespace frc::sim
diff --git a/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h b/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
index 0d578b4..80ad146 100644
--- a/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/EncoderSim.h
@@ -53,7 +53,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -78,8 +79,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCountCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCountCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Read the count of the encoder.
@@ -103,8 +105,9 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPeriodCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPeriodCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Read the period of the encoder.
@@ -127,8 +130,9 @@
    * @param initialNotify whether to run the callback on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterResetCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterResetCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check if the encoder has been reset.
@@ -152,7 +156,8 @@
    * @param initialNotify whether to run the callback on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterMaxPeriodCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterMaxPeriodCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -177,7 +182,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDirectionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDirectionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -202,7 +208,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterReverseDirectionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterReverseDirectionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -227,7 +234,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSamplesToAverageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSamplesToAverageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -252,7 +260,8 @@
    * @param initialNotify if true, the callback will be run on the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterDistancePerPulseCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterDistancePerPulseCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -286,7 +295,7 @@
    *
    * @return the encoder distance
    */
-  double GetDistance();
+  double GetDistance() const;
 
   /**
    * Change the rate of the encoder.
@@ -300,7 +309,7 @@
    *
    * @return the rate of change
    */
-  double GetRate();
+  double GetRate() const;
 
  private:
   explicit EncoderSim(int index) : m_index{index} {}
diff --git a/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h b/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
index 9f2272c..cc0eca9 100644
--- a/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/FlywheelSim.h
@@ -20,7 +20,9 @@
   /**
    * Creates a simulated flywheel mechanism.
    *
-   * @param plant              The linear system representing the flywheel.
+   * @param plant              The linear system representing the flywheel. This
+   *                           system can be created with
+   *                           LinearSystemId::FlywheelSystem().
    * @param gearbox            The type of and number of motors in the flywheel
    *                           gearbox.
    * @param gearing            The gearing of the flywheel (numbers greater than
@@ -45,6 +47,15 @@
               units::kilogram_square_meter_t moi,
               const std::array<double, 1>& measurementStdDevs = {0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the flywheel's state.
+   *
+   * @param velocity The new velocity
+   */
+  void SetState(units::radians_per_second_t velocity);
+
   /**
    * Returns the flywheel velocity.
    *
diff --git a/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h b/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h
new file mode 100644
index 0000000..6b9e2c7
--- /dev/null
+++ b/wpilibc/src/main/native/include/frc/simulation/PS5ControllerSim.h
@@ -0,0 +1,176 @@
+// 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 "frc/simulation/GenericHIDSim.h"
+
+namespace frc {
+
+class PS5Controller;
+
+namespace sim {
+
+/**
+ * Class to control a simulated PS5 controller.
+ */
+class PS5ControllerSim : public GenericHIDSim {
+ public:
+  /**
+   * Constructs from a PS5Controller object.
+   *
+   * @param joystick controller to simulate
+   */
+  explicit PS5ControllerSim(const PS5Controller& joystick);
+
+  /**
+   * Constructs from a joystick port number.
+   *
+   * @param port port number
+   */
+  explicit PS5ControllerSim(int port);
+
+  /**
+   * Change the X axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  void SetLeftX(double value);
+
+  /**
+   * Change the X axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  void SetRightX(double value);
+
+  /**
+   * Change the Y axis value of the controller's left stick.
+   *
+   * @param value the new value
+   */
+  void SetLeftY(double value);
+
+  /**
+   * Change the Y axis value of the controller's right stick.
+   *
+   * @param value the new value
+   */
+  void SetRightY(double value);
+
+  /**
+   * Change the L2 axis axis value of the controller.
+   *
+   * @param value the new value
+   */
+  void SetL2Axis(double value);
+
+  /**
+   * Change the R2 axis value of the controller.
+   *
+   * @param value the new value
+   */
+  void SetR2Axis(double value);
+
+  /**
+   * Change the value of the Square button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetSquareButton(bool value);
+
+  /**
+   * Change the value of the Cross button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCrossButton(bool value);
+
+  /**
+   * Change the value of the Circle button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCircleButton(bool value);
+
+  /**
+   * Change the value of the Triangle button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetTriangleButton(bool value);
+
+  /**
+   * Change the value of the L1 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL1Button(bool value);
+
+  /**
+   * Change the value of the R1 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR1Button(bool value);
+
+  /**
+   * Change the value of the L2 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL2Button(bool value);
+
+  /**
+   * Change the value of the R2 button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR2Button(bool value);
+
+  /**
+   * Change the value of the Create button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetCreateButton(bool value);
+
+  /**
+   * Change the value of the Options button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetOptionsButton(bool value);
+
+  /**
+   * Change the value of the L3 (left stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetL3Button(bool value);
+
+  /**
+   * Change the value of the R3 (right stick) button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetR3Button(bool value);
+
+  /**
+   * Change the value of the PS button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetPSButton(bool value);
+
+  /**
+   * Change the value of the touchpad button on the controller.
+   *
+   * @param value the new value
+   */
+  void SetTouchpad(bool value);
+};
+
+}  // namespace sim
+}  // namespace frc
diff --git a/wpilibc/src/main/native/include/frc/simulation/PWMSim.h b/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
index 8b4156e..424b7979 100644
--- a/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/PWMSim.h
@@ -48,7 +48,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -66,28 +67,29 @@
   void SetInitialized(bool initialized);
 
   /**
-   * Register a callback to be run when the PWM raw value changes.
+   * Register a callback to be run when the PWM pulse microsecond value changes.
    *
    * @param callback the callback
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRawValueCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPulseMicrosecondCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
-   * Get the PWM raw value.
+   * Get the PWM pulse microsecond value.
    *
-   * @return the PWM raw value
+   * @return the PWM pulse microsecond value
    */
-  int GetRawValue() const;
+  int32_t GetPulseMicrosecond() const;
 
   /**
-   * Set the PWM raw value.
+   * Set the PWM pulse microsecond value.
    *
-   * @param rawValue the PWM raw value
+   * @param microsecondPulseTime the PWM pulse microsecond value
    */
-  void SetRawValue(int rawValue);
+  void SetPulseMicrosecond(int32_t microsecondPulseTime);
 
   /**
    * Register a callback to be run when the PWM speed changes.
@@ -96,8 +98,9 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSpeedCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSpeedCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Get the PWM speed.
@@ -120,7 +123,8 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPositionCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPositionCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -144,7 +148,8 @@
    * @param initialNotify whether to run the callback with the initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPeriodScaleCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPeriodScaleCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -168,7 +173,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZeroLatchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZeroLatchCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h b/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
index 11110e3..510349f 100644
--- a/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/PneumaticsBaseSim.h
@@ -42,8 +42,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterInitializedCallback(NotifyCallback callback, bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check if the compressor is on.
@@ -68,8 +69,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterCompressorOnCallback(NotifyCallback callback, bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check the solenoid output on a specific channel.
@@ -98,9 +100,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterSolenoidOutputCallback(int channel, NotifyCallback callback,
-                                 bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+      int channel, NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Check the value of the pressure switch.
@@ -126,9 +128,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterPressureSwitchCallback(NotifyCallback callback,
-                                 bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Read the compressor current.
@@ -153,9 +155,9 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) = 0;
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) = 0;
 
   /**
    * Get the current value of all solenoid outputs.
diff --git a/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h b/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
index ac54da2..76fa7ba 100644
--- a/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/PowerDistributionSim.h
@@ -40,7 +40,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -65,7 +66,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterTemperatureCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterTemperatureCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -90,7 +92,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterVoltageCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterVoltageCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -116,7 +119,8 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCurrentCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCurrentCallback(
       int channel, NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h b/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
index fe305ba..917eb1f 100644
--- a/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
@@ -37,21 +37,24 @@
 
   ~REVPHSim() override = default;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetInitialized() const override;
 
   void SetInitialized(bool solenoidInitialized) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
       int channel, NotifyCallback callback, bool initialNotify) override;
 
   bool GetSolenoidOutput(int channel) const override;
 
   void SetSolenoidOutput(int channel, bool solenoidOutput) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   /**
@@ -76,9 +79,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorConfigTypeCallback(NotifyCallback callback,
-                                       bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorConfigTypeCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the closed loop compressor control is active.
@@ -94,16 +97,17 @@
    */
   void SetCompressorConfigType(int compressorConfigType);
 
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
       NotifyCallback callback, bool initialNotify) override;
 
   bool GetPressureSwitch() const override;
 
   void SetPressureSwitch(bool pressureSwitch) override;
 
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterCompressorCurrentCallback(NotifyCallback callback,
-                                    bool initialNotify) override;
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterCompressorCurrentCallback(
+      NotifyCallback callback, bool initialNotify) override;
 
   double GetCompressorCurrent() const override;
 
diff --git a/wpilibc/src/main/native/include/frc/simulation/RelaySim.h b/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
index 5034885..ecc4ded 100644
--- a/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/RelaySim.h
@@ -40,9 +40,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterInitializedForwardCallback(NotifyCallback callback,
-                                     bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedForwardCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the forward direction has been initialized.
@@ -65,9 +65,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore>
-  RegisterInitializedReverseCallback(NotifyCallback callback,
-                                     bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterInitializedReverseCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Check whether the reverse direction has been initialized.
@@ -90,7 +90,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterForwardCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterForwardCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
@@ -114,7 +115,8 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterReverseCallback(
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterReverseCallback(
       NotifyCallback callback, bool initialNotify);
 
   /**
diff --git a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
index ee959b66..64e81e1 100644
--- a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <units/current.h>
+#include <units/temperature.h>
 #include <units/voltage.h>
 
 #include "frc/simulation/CallbackStore.h"
@@ -26,8 +27,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterFPGAButtonCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterFPGAButtonCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Query the state of the FPGA button.
@@ -50,8 +52,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterVInVoltageCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterVInVoltageCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the Vin voltage.
@@ -75,8 +78,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterVInCurrentCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterVInCurrentCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the Vin current.
@@ -100,8 +104,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 6V rail voltage.
@@ -125,8 +130,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 6V rail current.
@@ -150,8 +156,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 6V rail active state.
@@ -175,8 +182,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 5V rail voltage.
@@ -200,8 +208,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 5V rail current.
@@ -225,8 +234,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 5V rail active state.
@@ -250,8 +260,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserVoltage3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserVoltage3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 3.3V rail voltage.
@@ -275,8 +286,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserCurrent3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserCurrent3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the 3.3V rail current.
@@ -300,8 +312,9 @@
    *                      initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserActive3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserActive3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 3.3V rail active state.
@@ -326,8 +339,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults6VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults6VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 6V rail number of faults.
@@ -352,8 +366,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults5VCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults5VCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 5V rail number of faults.
@@ -378,8 +393,9 @@
    *                      initial value
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterUserFaults3V3Callback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterUserFaults3V3Callback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Get the 3.3V rail number of faults.
@@ -402,8 +418,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] static std::unique_ptr<CallbackStore>
-  RegisterBrownoutVoltageCallback(NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterBrownoutVoltageCallback(
+      NotifyCallback callback, bool initialNotify);
 
   /**
    * Measure the brownout voltage.
@@ -420,6 +437,56 @@
   static void SetBrownoutVoltage(units::volt_t brownoutVoltage);
 
   /**
+   * Register a callback to be run whenever the cpu temp changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the CallbackStore object associated with this callback
+   */
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterCPUTempCallback(
+      NotifyCallback callback, bool initialNotify);
+
+  /**
+   * Get the cpu temp.
+   *
+   * @return the cpu temp.
+   */
+  static units::celsius_t GetCPUTemp();
+
+  /**
+   * Define the cpu temp.
+   *
+   * @param cpuTemp the new cpu temp.
+   */
+  static void SetCPUTemp(units::celsius_t cpuTemp);
+
+  /**
+   * Register a callback to be run whenever the team number changes.
+   *
+   * @param callback the callback
+   * @param initialNotify whether to call the callback with the initial state
+   * @return the CallbackStore object associated with this callback
+   */
+  [[nodiscard]]
+  static std::unique_ptr<CallbackStore> RegisterTeamNumberCallback(
+      NotifyCallback callback, bool initialNotify);
+
+  /**
+   * Get the team number.
+   *
+   * @return the team number.
+   */
+  static int32_t GetTeamNumber();
+
+  /**
+   * Set the team number.
+   *
+   * @param teamNumber the new team number.
+   */
+  static void SetTeamNumber(int32_t teamNumber);
+
+  /**
    * Get the serial number.
    *
    * @return The serial number.
diff --git a/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h b/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
index 9d31bd0..a84391e 100644
--- a/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/SPIAccelerometerSim.h
@@ -25,8 +25,9 @@
    * @param initialNotify whether to run the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterActiveCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterActiveCallback(NotifyCallback callback,
+                                                        bool initialNotify);
 
   /**
    * Check whether the accelerometer is active.
@@ -49,8 +50,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterRangeCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterRangeCallback(NotifyCallback callback,
+                                                       bool initialNotify);
 
   /**
    * Check the range of this accelerometer.
@@ -73,8 +75,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterXCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterXCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the X axis value.
@@ -97,8 +100,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterYCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterYCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Y axis value.
@@ -121,8 +125,9 @@
    * @param initialNotify whether to call the callback with the initial state
    * @return the CallbackStore object associated with this callback
    */
-  [[nodiscard]] std::unique_ptr<CallbackStore> RegisterZCallback(
-      NotifyCallback callback, bool initialNotify);
+  [[nodiscard]]
+  std::unique_ptr<CallbackStore> RegisterZCallback(NotifyCallback callback,
+                                                   bool initialNotify);
 
   /**
    * Measure the Z axis value.
diff --git a/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h b/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
index 0a4e4d8..6e16116 100644
--- a/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/SimDeviceSim.h
@@ -43,6 +43,20 @@
   SimDeviceSim(const char* name, int index, int channel);
 
   /**
+   * Constructs a SimDeviceSim.
+   *
+   * @param handle the low level handle for the corresponding SimDevice.
+   */
+  explicit SimDeviceSim(HAL_SimDeviceHandle handle);
+
+  /**
+   * Get the name of this object.
+   *
+   * @return name
+   */
+  std::string GetName() const;
+
+  /**
    * Get the property object with the given name.
    *
    * @param name the property name
diff --git a/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h b/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
index 57ad6b1..12a719b 100644
--- a/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/SingleJointedArmSim.h
@@ -23,22 +23,24 @@
   /**
    * Creates a simulated arm mechanism.
    *
-   * @param system             The system representing this arm.
+   * @param system             The system representing this arm. This system can
+   *                           be created with
+   *                           LinearSystemId::SingleJointedArmSystem().
    * @param gearbox            The type and number of motors on the arm gearbox.
    * @param gearing            The gear ratio of the arm (numbers greater than 1
    *                           represent reductions).
    * @param armLength          The length of the arm.
    * @param minAngle           The minimum angle that the arm is capable of.
    * @param maxAngle           The maximum angle that the arm is capable of.
-   * @param armMass            The mass of the arm.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingAngle      The initial position of the arm.
    * @param measurementStdDevs The standard deviations of the measurements.
    */
   SingleJointedArmSim(const LinearSystem<2, 1, 1>& system,
                       const DCMotor& gearbox, double gearing,
                       units::meter_t armLength, units::radian_t minAngle,
-                      units::radian_t maxAngle, units::kilogram_t armMass,
-                      bool simulateGravity,
+                      units::radian_t maxAngle, bool simulateGravity,
+                      units::radian_t startingAngle,
                       const std::array<double, 1>& measurementStdDevs = {0.0});
   /**
    * Creates a simulated arm mechanism.
@@ -51,17 +53,28 @@
    * @param armLength          The length of the arm.
    * @param minAngle           The minimum angle that the arm is capable of.
    * @param maxAngle           The maximum angle that the arm is capable of.
-   * @param mass               The mass of the arm.
    * @param simulateGravity    Whether gravity should be simulated or not.
+   * @param startingAngle      The initial position of the arm.
    * @param measurementStdDevs The standard deviation of the measurement noise.
    */
   SingleJointedArmSim(const DCMotor& gearbox, double gearing,
                       units::kilogram_square_meter_t moi,
                       units::meter_t armLength, units::radian_t minAngle,
-                      units::radian_t maxAngle, units::kilogram_t mass,
-                      bool simulateGravity,
+                      units::radian_t maxAngle, bool simulateGravity,
+                      units::radian_t startingAngle,
                       const std::array<double, 1>& measurementStdDevs = {0.0});
 
+  using LinearSystemSim::SetState;
+
+  /**
+   * Sets the arm's state. The new angle will be limited between the minimum and
+   * maximum allowed limits.
+   *
+   * @param angle The new angle.
+   * @param velocity The new angular velocity.
+   */
+  void SetState(units::radian_t angle, units::radians_per_second_t velocity);
+
   /**
    * Returns whether the arm would hit the lower limit.
    *
@@ -146,10 +159,9 @@
                      units::second_t dt) override;
 
  private:
-  units::meter_t m_r;
+  units::meter_t m_armLen;
   units::radian_t m_minAngle;
   units::radian_t m_maxAngle;
-  units::kilogram_t m_armMass;
   const DCMotor m_gearbox;
   double m_gearing;
   bool m_simulateGravity;
diff --git a/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h b/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
index 66e3589..dd2e8e7 100644
--- a/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/SolenoidSim.h
@@ -16,6 +16,7 @@
   SolenoidSim(std::shared_ptr<PneumaticsBaseSim> moduleSim, int channel);
   SolenoidSim(int module, PneumaticsModuleType type, int channel);
   SolenoidSim(PneumaticsModuleType type, int channel);
+  virtual ~SolenoidSim() = default;
 
   bool GetOutput() const;
   void SetOutput(bool output);
@@ -29,7 +30,8 @@
    * Save a reference to this object; it being deconstructed cancels the
    * callback.
    */
-  [[nodiscard]] virtual std::unique_ptr<CallbackStore> RegisterOutputCallback(
+  [[nodiscard]]
+  virtual std::unique_ptr<CallbackStore> RegisterOutputCallback(
       NotifyCallback callback, bool initialNotify);
 
   std::shared_ptr<PneumaticsBaseSim> GetModuleSim() const;
diff --git a/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h b/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
index efa9b37..5db41f7 100644
--- a/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
+++ b/wpilibc/src/main/native/include/frc/simulation/UltrasonicSim.h
@@ -14,16 +14,24 @@
 namespace sim {
 
 /**
- * Class to control a simulated ADXRS450 gyroscope.
+ * Class to control a simulated {@link Ultrasonic}.
  */
 class UltrasonicSim {
  public:
   /**
-   * Constructs from a ADXRS450_Gyro object.
+   * Constructor.
    *
-   * @param gyro ADXRS450_Gyro to simulate
+   * @param ultrasonic The real ultrasonic to simulate
    */
-  explicit UltrasonicSim(const Ultrasonic& gyro);
+  explicit UltrasonicSim(const Ultrasonic& ultrasonic);
+
+  /**
+   * Constructor.
+   *
+   * @param ping unused.
+   * @param echo the ultrasonic's echo channel.
+   */
+  UltrasonicSim(int ping, int echo);
 
   /**
    * Sets if the range measurement is valid.
@@ -37,7 +45,7 @@
    *
    * @param range The range
    */
-  void SetRange(units::meter_t range);
+  void SetRange(units::inch_t range);
 
  private:
   hal::SimBoolean m_simRangeValid;
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h b/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
index c4185e7..81cbc97 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/MechanismObject2d.h
@@ -4,11 +4,11 @@
 
 #pragma once
 
+#include <concepts>
 #include <memory>
 #include <stdexcept>
 #include <string>
 #include <string_view>
-#include <type_traits>
 #include <utility>
 
 #include <networktables/NetworkTable.h>
@@ -62,9 +62,8 @@
    * assignments and call chaining.
    * @throw if an object with the given name already exists.
    */
-  template <typename T, typename... Args,
-            typename =
-                std::enable_if_t<std::is_convertible_v<T*, MechanismObject2d*>>>
+  template <typename T, typename... Args>
+    requires std::convertible_to<T*, MechanismObject2d*>
   T* Append(std::string_view name, Args&&... args) {
     std::scoped_lock lock(m_mutex);
     auto& obj = m_objects[name];
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
index 4bd4289..baee6ff 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableBuilderImpl.h
@@ -96,44 +96,73 @@
   void AddBooleanProperty(std::string_view key, std::function<bool()> getter,
                           std::function<void(bool)> setter) override;
 
+  void PublishConstBoolean(std::string_view key, bool value) override;
+
   void AddIntegerProperty(std::string_view key, std::function<int64_t()> getter,
                           std::function<void(int64_t)> setter) override;
 
+  void PublishConstInteger(std::string_view key, int64_t value) override;
+
   void AddFloatProperty(std::string_view key, std::function<float()> getter,
                         std::function<void(float)> setter) override;
 
+  void PublishConstFloat(std::string_view key, float value) override;
+
   void AddDoubleProperty(std::string_view key, std::function<double()> getter,
                          std::function<void(double)> setter) override;
 
+  void PublishConstDouble(std::string_view key, double value) override;
+
   void AddStringProperty(std::string_view key,
                          std::function<std::string()> getter,
                          std::function<void(std::string_view)> setter) override;
 
+  void PublishConstString(std::string_view key,
+                          std::string_view value) override;
+
   void AddBooleanArrayProperty(
       std::string_view key, std::function<std::vector<int>()> getter,
       std::function<void(std::span<const int>)> setter) override;
 
+  void PublishConstBooleanArray(std::string_view key,
+                                std::span<const int> value) override;
+
   void AddIntegerArrayProperty(
       std::string_view key, std::function<std::vector<int64_t>()> getter,
       std::function<void(std::span<const int64_t>)> setter) override;
 
+  void PublishConstIntegerArray(std::string_view key,
+                                std::span<const int64_t> value) override;
+
   void AddFloatArrayProperty(
       std::string_view key, std::function<std::vector<float>()> getter,
       std::function<void(std::span<const float>)> setter) override;
 
+  void PublishConstFloatArray(std::string_view key,
+                              std::span<const float> value) override;
+
   void AddDoubleArrayProperty(
       std::string_view key, std::function<std::vector<double>()> getter,
       std::function<void(std::span<const double>)> setter) override;
 
+  void PublishConstDoubleArray(std::string_view key,
+                               std::span<const double> value) override;
+
   void AddStringArrayProperty(
       std::string_view key, std::function<std::vector<std::string>()> getter,
       std::function<void(std::span<const std::string>)> setter) override;
 
+  void PublishConstStringArray(std::string_view key,
+                               std::span<const std::string> value) override;
+
   void AddRawProperty(
       std::string_view key, std::string_view typeString,
       std::function<std::vector<uint8_t>()> getter,
       std::function<void(std::span<const uint8_t>)> setter) override;
 
+  void PublishConstRaw(std::string_view key, std::string_view typeString,
+                       std::span<const uint8_t> value) override;
+
   void AddSmallStringProperty(
       std::string_view key,
       std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
@@ -198,6 +227,9 @@
   template <typename Topic, typename Getter, typename Setter>
   void AddPropertyImpl(Topic topic, Getter getter, Setter setter);
 
+  template <typename Topic, typename Value>
+  void PublishConstImpl(Topic topic, Value value);
+
   template <typename T, size_t Size, typename Topic, typename Getter,
             typename Setter>
   void AddSmallPropertyImpl(Topic topic, Getter getter, Setter setter);
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
index 7fe0b59..f06c252 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <concepts>
+#include <functional>
 #include <memory>
 #include <string_view>
 
@@ -27,9 +29,10 @@
  * @see SmartDashboard
  */
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 class SendableChooser : public SendableChooserBase {
   wpi::StringMap<T> m_choices;
-
+  std::function<void(T)> m_listener;
   template <class U>
   static U _unwrap_smart_ptr(const U& value);
 
@@ -80,7 +83,15 @@
    */
   auto GetSelected() -> decltype(_unwrap_smart_ptr(m_choices[""]));
 
-  void InitSendable(nt::NTSendableBuilder& builder) override;
+  /**
+   * Bind a listener that's called when the selected value changes.
+   * Only one listener can be bound. Calling this function will replace the
+   * previous listener.
+   * @param listener The function to call that accepts the new value
+   */
+  void OnChange(std::function<void(T)>);
+
+  void InitSendable(wpi::SendableBuilder& builder) override;
 };
 
 }  // namespace frc
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
index e90542b..e40befd 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooser.inc
@@ -5,30 +5,34 @@
 #pragma once
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <string>
 #include <string_view>
 #include <utility>
 #include <vector>
 
-#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableBuilder.h>
 
 #include "frc/smartdashboard/SendableChooser.h"
 
 namespace frc {
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 void SendableChooser<T>::AddOption(std::string_view name, T object) {
   m_choices[name] = std::move(object);
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 void SendableChooser<T>::SetDefaultOption(std::string_view name, T object) {
   m_defaultChoice = name;
   AddOption(name, std::move(object));
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 auto SendableChooser<T>::GetSelected()
     -> decltype(_unwrap_smart_ptr(m_choices[""])) {
   std::string selected = m_defaultChoice;
@@ -46,16 +50,17 @@
 }
 
 template <class T>
-void SendableChooser<T>::InitSendable(nt::NTSendableBuilder& builder) {
+  requires std::copy_constructible<T> && std::default_initializable<T>
+void SendableChooser<T>::OnChange(std::function<void(T)> listener) {
+  std::scoped_lock lock(m_mutex);
+  m_listener = listener;
+}
+
+template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
+void SendableChooser<T>::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("String Chooser");
-  {
-    std::scoped_lock lock(m_mutex);
-    m_instancePubs.emplace_back(
-        nt::IntegerTopic{builder.GetTopic(kInstance)}.Publish());
-    m_instancePubs.back().Set(m_instance);
-    m_activePubs.emplace_back(
-        nt::StringTopic{builder.GetTopic(kActive)}.Publish());
-  }
+  builder.PublishConstInteger(kInstance, m_instance);
   builder.AddStringArrayProperty(
       kOptions,
       [=, this] {
@@ -91,28 +96,40 @@
       nullptr);
   builder.AddStringProperty(kSelected, nullptr,
                             [=, this](std::string_view val) {
-                              std::scoped_lock lock(m_mutex);
-                              m_haveSelected = true;
-                              m_selected = val;
-                              for (auto& pub : m_activePubs) {
-                                pub.Set(val);
+                              T choice{};
+                              std::function<void(T)> listener;
+                              {
+                                std::scoped_lock lock(m_mutex);
+                                m_haveSelected = true;
+                                m_selected = val;
+                                if (m_previousVal != val && m_listener) {
+                                  choice = m_choices[val];
+                                  listener = m_listener;
+                                }
+                                m_previousVal = val;
+                              }
+                              if (listener) {
+                                listener(choice);
                               }
                             });
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 U SendableChooser<T>::_unwrap_smart_ptr(const U& value) {
   return value;
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 U* SendableChooser<T>::_unwrap_smart_ptr(const std::unique_ptr<U>& value) {
   return value.get();
 }
 
 template <class T>
+  requires std::copy_constructible<T> && std::default_initializable<T>
 template <class U>
 std::weak_ptr<U> SendableChooser<T>::_unwrap_smart_ptr(
     const std::shared_ptr<U>& value) {
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
index b5df73c..f0b4fed 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableChooserBase.h
@@ -7,11 +7,8 @@
 #include <atomic>
 #include <string>
 
-#include <networktables/IntegerTopic.h>
-#include <networktables/NTSendable.h>
-#include <networktables/StringTopic.h>
-#include <wpi/SmallVector.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
 #include <wpi/sendable/SendableHelper.h>
 
 namespace frc {
@@ -22,7 +19,7 @@
  * It contains static, non-templated variables to avoid their duplication in the
  * template class.
  */
-class SendableChooserBase : public nt::NTSendable,
+class SendableChooserBase : public wpi::Sendable,
                             public wpi::SendableHelper<SendableChooserBase> {
  public:
   SendableChooserBase();
@@ -41,10 +38,9 @@
   std::string m_defaultChoice;
   std::string m_selected;
   bool m_haveSelected = false;
-  wpi::SmallVector<nt::IntegerPublisher, 2> m_instancePubs;
-  wpi::SmallVector<nt::StringPublisher, 2> m_activePubs;
   wpi::mutex m_mutex;
   int m_instance;
+  std::string m_previousVal;
   static std::atomic_int s_instances;
 };
 
diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h b/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
index aeb4947..04435f0 100644
--- a/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
+++ b/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <span>
 #include <string>