Compare commits

...

54 Commits

Author SHA1 Message Date
Jack Cammarata
1efaaefd78 [wpimath] Fix UnscentedKalmanFilter and improve math docs (#7850)
Throughout the code the state sqrt covariance S and innovation covariance Sy are maintained as upper triangular cholesky factors of those covariance matrices. The original paper defines P=S*S', so S should be lower triangular. The functions in the paper reflect this. In the code implementation, the sqrt covariance matrices are upper triangular, but the algorithm expects them to be lower triangular.

This bug was likely missed because the incorrect version of the filter is able to converge for some systems where all the states are observed, and the test case is set up such that all states are observed.

To fix the bug, a couple things needed to be changed:
all instances of rankUpdate() needed to be changed to use the lower triangular cholesky factor,
In the unscented transform, when S is found via QR decomposition, we need to take the transpose because R is upper triangular,
P() and SetP() functions need to be modified to be P=S*S' instead of P=S'*S, and P.llt().matrixL() instead of P.llt().matrixU() respectively.

Each part of the algorithm has also had the comments changed to clarify exactly which equation from the paper it implements.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-14 10:05:15 -07:00
Tyler Veness
71b6e8ec58 [wpiutil] Avoid including execinfo.h for Emscripten target (#7854) 2025-03-06 23:15:22 -08:00
Tyler Veness
1a835fec01 [wpimath] Fix singularities in Ellipse2d::Nearest() (#7851)
The problem was ill-conditioned if either semiaxis had zero length.
2025-03-04 18:52:17 -08:00
crueter
0ad595c33c [wpimath] Add Debouncer type and time setters (#7839)
Signed-off-by: swurl <swurl@swurl.xyz>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-03 17:21:38 -07:00
Thad House
93bf6c70ba [build] Remove sources from ceres-cpp (#7844) 2025-03-02 10:46:50 -08:00
arbessette
a7ae22d764 [docs] Update code of conduct (#7833) 2025-02-27 11:02:13 -08:00
Tyler Veness
822457d45b [wpimath] Fix feedforward returning NaN when kᵥ = 0 (#7790) 2025-02-25 19:07:51 -08:00
Adrien Bourdeaux
75321f1d84 [wpimath] Add Translation2d/Translation3d slew rate limiter (#7806)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
2025-02-25 19:06:00 -08:00
Tyler Veness
cd6fee7fea [sysid] Refactor feedback analysis (#7827) 2025-02-25 19:05:05 -08:00
Tyler Veness
517344fe80 [wpimath] Fix another infinite loop in ArmFeedforward (#7823) 2025-02-25 19:04:31 -08:00
Rain Heuer
b0e588fd49 [glass] Update Field2D default field to 2025 (#7820) 2025-02-25 19:04:16 -08:00
Tyler Veness
0f0e93722e [ci] Upgrade to Clang 17 sanitizers (#7819) 2025-02-25 19:01:41 -08:00
sciencewhiz
f9307de04c [docs] Document that /format is disabled (#7810)
Add instructions for manual workarounds
2025-02-20 20:13:09 -08:00
sciencewhiz
15dcdebe21 [docs] Update contributing so breaking changes go to 2027 (#7809) 2025-02-20 20:12:48 -08:00
Tyler Veness
0c3c2c1fda [sysid] Remove extra period from exception messages (#7805) 2025-02-19 21:08:39 -08:00
HarryXChen
c898853b4d [wpilib] LinearSystemSim.setState: calculate new output state (#7799)
Establishes the invariant that the state and measurement always match up, even immediately after construction.
2025-02-18 22:49:28 -08:00
Sam Carlberg
bd2211119f [epilogue] Make nonloggable type warnings configurable (#7792)
Now a flag at the class level controls whether warning messages are printed.

Defaults to false (no warning messages).
2025-02-18 21:48:51 -08:00
Tyler Veness
13626063dc [wpimath] Fix units typo (#7789) 2025-02-13 23:22:44 -08:00
Peter Johnson
7e6077c546 [wpimath] Fix up order and docs for Feedforward gain setters (#7788) 2025-02-13 23:20:08 -08:00
Jade
4d126b158c [wpimath] Add setters to Feedforward gains (#7784)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-02-13 21:09:28 -08:00
Peter Johnson
e22f76ce73 [build] Bump native-utils to 2025.9.1 (#7783) 2025-02-13 20:23:07 -08:00
Jonah Bonner
e648b9c86d [wpinet] Serve index HTML file from WebServer if available (#7780) 2025-02-13 18:10:02 -08:00
Kevin-OConnor
23658a8c62 [apriltag] Split 2025 AprilTag Maps (#7781)
Splits maps for welded vs AndyMark field perimeters. More info about why and what fields are at what events will be in TU12.
2025-02-13 17:53:11 -08:00
Tyler Veness
155b3d45e7 [wpiutil] Remove broken StackWalker library (#7777)
A Discord user reported that StackWalker gives blank stacktraces.

MSVC's C++23 support is unstable, so we can't use std::stacktrace yet.
In the meantime, we can just return an empty string and remove the
unmaintained StackWalker library and its hacky upstream_utils script.
2025-02-12 22:54:41 -08:00
Austin Schuh
d62ab12855 [bazel] Add missing -ldl when building libuv in bazel (#7775)
I was getting:
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: bazel-out/k8-opt--roborio/bin/external/com_github_wpilibsuite_allwpilib/wpinet/libwpinet.static.a(fs.o): undefined reference to symbol 'dlsym@@GLIBC_2.4'
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/lib/libdl.so.2: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

This fixes that error for me.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-02-12 16:17:11 -07:00
Dustin Spicuzza
1921d7b79a [wpilibc] Remove Alert magic static with map lookup (#7712)
The magic static adds yet another thing that needs to be reset if you
want to run a unit test with a completely reset wpilib state. Use the
existing Sendable static map to store the data instead.

This leaks memory if ResetSmartDashboardInstance is called.
2025-02-11 22:05:22 -08:00
Gold856
9dbb0c8c3d [ci] Turn on sync labels for the labeler (#7770)
This means PRs that had labels for certain changes will have those labels removed if those changes are removed.
2025-02-09 23:04:36 -08:00
Michael Fisher
b2584c6bdf [cmake] Use binary output dir for generated field images code (#7772) 2025-02-09 23:03:15 -08:00
Peter Johnson
53df127535 [cmake] Suppress enum warning on all clang, not just Apple (#7771) 2025-02-09 23:01:51 -08:00
Peter Johnson
d2611d4ad5 [hal] SPI: Remove byte limit on size in Java API (#7774)
The underlying Linux spidev supports up to page size length.
2025-02-09 23:01:01 -08:00
Ryan Blue
b60b2b64bd [hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).

This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.

To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.

Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 13:36:41 -07:00
Ryan Blue
a0976a1fd9 [build] Update developerRobot JRE (#7764) 2025-02-07 11:24:30 -07:00
DeltaDizzy
b3b515e1dd [sysid] Error on missing tests in loaded DataLog (#7747)
* add exception

* detect missing tests

* throw in analyzer

* define tests setter

* change to std::map

* alignas fail

* make missingTests parameter const&

* const& impl

* use set and naive comparison

* use default comparison

* add <utility>

* Revert "alignas fail"

This reverts commit 5e97940f34.

* indent validtests

* format
2025-02-04 18:12:00 -05:00
Joseph Eng
296986397b [wpimath] Document drift from desaturating discretized chassis speeds (NFC) (#7741) 2025-02-03 11:46:18 -07:00
Sam Carlberg
18321e5551 [epilogue] Fix epilogue with package-info files (#7749) 2025-01-30 13:34:51 -07:00
Tyler Veness
b31fd17d05 [wpimath] Fix infinite loop in ArmFeedforward::Calculate(xₖ, vₖ, vₖ₊₁) (#7745)
Small values of kₐ make the iterative solver ill-conditioned. This
change reverts to the constant-acceleration feedforward in that case. It
gives _very_ bad results (hence why we added the iterative solver in the
first place), but it's better than hanging.

```
TEST(ArmFeedforwardTest, CalculateIllConditioned) {
  constexpr auto Ks = 0.5_V;
  constexpr auto Kv = 20_V / 1_rad_per_s;
  constexpr auto Ka = 1e-2_V / 1_rad_per_s_sq;
  constexpr auto Kg = 0_V;
  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};

  // Calculate(currentAngle, currentVelocity, nextAngle, dt)
  CalculateAndSimulate(armFF, 0_rad, 0_rad_per_s, 2_rad_per_s, 20_ms);
}
```
This produces 1 V and doesn't accelerate the system at all. Using
nextVelocity instead of currentVelocity in the feedforward outputs 41 V
and still only accelerates to 0.4 rad/s of the requested 2 rad/s.

I picked the kₐ cutoff by increasing kₐ until the iterative solver
started converging.

Fixes #7743.
2025-01-30 13:33:39 -07:00
Gold856
b44a80c07a [build] cmake: Install wpimath nanopb headers (#7731) 2025-01-24 23:26:09 -08:00
Gold856
25d11524e8 [ci] Re-enable Artifactory cleaner cron job (#7721)
The query now targets the local repo for extra safety.
2025-01-23 21:46:13 -08:00
Peter Johnson
d86a2ec83b [ci] Fix labeler indentation (#7716) 2025-01-21 12:51:48 -07:00
sciencewhiz
0690d3d832 [ci] Update labeler for wpical and usage reporting (#7710) 2025-01-20 09:14:27 -07:00
Ryan Blue
304b98c0c9 [wpilibc] Alert: Fix first alert in group not publishing data (#7711) 2025-01-20 09:10:03 -07:00
Joseph Eng
72541c10e6 [wpilib, commands] Improve HID direction documentation (NFC) (#7672) 2025-01-19 20:34:07 -08:00
sciencewhiz
00445f4f27 [hal] Add Kitbot framework usage reporting (#7709)
Used in FIRST's kitbot code
2025-01-18 15:02:41 -08:00
Matthew Wozniak
8fc3767b30 [wpimath] Fix macro name typo (#7707) 2025-01-17 22:19:21 -08:00
Peter Johnson
5ab0409c15 [wpilib] ADIS164xx: report product ID on mismatch (#7706) 2025-01-17 18:14:20 -08:00
Peter Johnson
4caa16e254 [wpilibj] ADIS16470: Allow product ID of 16470 (#7704)
C++ allows either 16982 or 16470, do the same for Java.
2025-01-17 13:53:20 -08:00
Sam Carlberg
e52f400687 [wpiunits] Add Measure.per overloads for all known unit types (#7699)
Instead of only providing per(TimeUnit)

Useful for making conversion factors easier, eg `Inches.of(10).per(Rotation)` vs `Inches.of(10).per(Rotation.one())`

Update VelocityUnit.one() and VelocityUnit.zero() to return Velocity objects instead of generic Measure<? extends VelocityUnit<D>>; VelocityUnit is final, so the wildcard generic is unnecessary, and this makes the generated `per` functions possible for this type
2025-01-16 23:24:11 -08:00
PJ Reiniger
a9f3fc6b2c [bazel] Update toolchain to support systemcore (#7689) 2025-01-16 10:52:43 -07:00
David Racovan
a14545102f [wpimath] DifferentialDriveWheelPositions: tag as Proto/StructSerializable (#7622) 2025-01-13 14:52:40 -07:00
Tyler Veness
25e6549398 [wpimath] Fix various constexpr support bugs (#7676) 2025-01-13 14:44:55 -07:00
ハイドラント
cd92b07321 [wpimath] Add Pose2d and Pose3d rotateAround() (#7659) 2025-01-13 12:55:26 -07:00
Jason Daming
fc9e413ce1 [hal, wpilib] Add note about support for WS2815 (#7664) 2025-01-13 12:26:54 -07:00
Tyler Veness
007526089e [wpimath] Fix LinearSystemId return type and docs (#7675)
Fixes #7674.
2025-01-13 12:22:53 -07:00
Sam Carlberg
c5f7a2b4ac [epilogue] Fix lazy logging of mutable arrays (#7665) 2025-01-11 10:25:47 -08:00
192 changed files with 7181 additions and 2705 deletions

View File

@@ -16,9 +16,15 @@ import shared/bazel/compiler_flags/base_linux_flags.rc
import shared/bazel/compiler_flags/linux_flags.rc
import shared/bazel/compiler_flags/osx_flags.rc
import shared/bazel/compiler_flags/roborio_flags.rc
import shared/bazel/compiler_flags/systemcore_flags.rc
import shared/bazel/compiler_flags/windows_flags.rc
import shared/bazel/compiler_flags/coverage_flags.rc
# Alias toolchain names to what wpilibsuite uses for CI/Artifact naming
build:athena --config=roborio
build:linuxarm32 --config=raspibookworm32
build:linuxarm64 --config=bookworm64
build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=allwpilib-build-java
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp
build:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example

9
.github/labeler.yml vendored
View File

@@ -54,3 +54,12 @@
'component: wpiutil':
- changed-files:
- any-glob-to-any-file: wpiutil/**
'component: wpical':
- changed-files:
- any-glob-to-any-file: wpical/**
'component: usage reporting':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**
'attn: NI':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**

View File

@@ -3,7 +3,7 @@
{
"aql": {
"items.find": {
"repo": "wpilib-mvn-development",
"repo": "wpilib-mvn-development-local",
"path": { "$nmatch":"*edu/wpi/first/thirdparty*" },
"$or":[
{

View File

@@ -2,6 +2,8 @@ name: Artifactory Nightly Cleanup
on:
workflow_dispatch:
schedule:
- cron: '15 2 * * *'
jobs:
wpilib-mvn-development_unused_cleanup:

View File

@@ -1,6 +1,6 @@
name: "Pull Request Labeler"
on:
- pull_request_target
- pull_request_target
jobs:
labeler:
@@ -9,4 +9,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v5
with:
sync-labels: true

View File

@@ -29,11 +29,11 @@ jobs:
ctest-env: ""
ctest-flags: ""
name: "${{ matrix.name }}"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2025-22.04
runs-on: ubuntu-24.04
container: wpilib/roborio-cross-ubuntu:2025-24.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java clang-14 libprotobuf-dev protobuf-compiler ninja-build
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java clang-17 libprotobuf-dev protobuf-compiler ninja-build
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
@@ -41,7 +41,7 @@ jobs:
- uses: actions/checkout@v4
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-17 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-17 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -120,12 +120,6 @@ jobs:
./mpack.py clone
./mpack.py copy-src
./mpack.py format-patch
- name: Run stack_walker.py
run: |
cd upstream_utils
./stack_walker.py clone
./stack_walker.py copy-src
./stack_walker.py format-patch
- name: Run memory.py
run: |
cd upstream_utils

3
.gitignore vendored
View File

@@ -255,3 +255,6 @@ bazel_auth.rc
# ctest
/Testing/
# Meson
.meson-subproject*

View File

@@ -1,69 +1,47 @@
# Contributor Covenant Code of Conduct
# Contributor Community Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
As members, contributors, and leaders, we commit to fostering a community where everyone feels safe, respected, and valued. We are dedicated to ensuring that participation in this community is harassment-free, inclusive, and welcoming, regardless of age, body type, abilities (visible or invisible), ethnicity, gender identity or expression, sexual orientation, socioeconomic background, education, nationality, personal appearance, race, or religion.
Above all, we pledge to act with integrity, kindness, and empathy—striving to be not only good participants but also good humans.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
Positive and respectful behavior is essential to creating a thriving community. This includes:
* Exhibiting Gracious Professionalism® at all times. Gracious Professionalism
* Practicing **Gracious Professionalism®** at all times. Gracious Professionalism
is a way of doing things that encourages high-quality work, emphasizes the
value of others, and respects individuals and the community.
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
* Showing empathy, kindness, and patience.
* Respecting diverse perspectives and experiences.
* Giving and receiving constructive feedback with openness and humility.
* Owning mistakes, apologizing when necessary, and learning from them.
* Prioritizing the well-being and success of the entire community over individual interests.
Examples of unacceptable behavior include:
Unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Using sexualized language, imagery, or making inappropriate advances
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Harassment in any form, whether public or private.
* Sharing private information (e.g., email or physical addresses) without explicit consent.
* Any behavior that is unprofessional, harmful, or exclusionary.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Community leaders are responsible for maintaining these standards and will take appropriate action to address any behavior deemed harmful, threatening, or inappropriate. Actions may include removing content, issuing warnings, or, when necessary, banning individuals. Moderation decisions will be communicated transparently where appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
This Code of Conduct applies to all community spaces, events, and instances where individuals represent the community (e.g., official email accounts, social media posts, or in-person/virtual events).
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[conduct@wpilib.org](mailto:conduct@wpilib.org).
[wpilib@wpi.edu](mailto:wpilib@wpi.edu).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -115,6 +93,9 @@ individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## A Note on Kindness
Building a community isnt just about rules—its about connection. Every interaction is an opportunity to be understanding, compassionate, and supportive. Being a good human is the key to our ethos.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],

View File

@@ -80,7 +80,7 @@ xₖ₊₁ = Axₖ + Buₖ
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
During the build season, breaking changes or other changes intended for the next season can be created as a pull request against the development branch of WPILib. After the season is over, the changes in the development branch will be merged into main.
Particularly large and/or breaking changes should be targeted to the 2027 branch, which targets the [SystemCore Robot Controller](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller). The intent is minimize changes for 2026, to allow development to focus on preparing for 2027.
### Merge Process

View File

@@ -136,6 +136,9 @@ If you have installed the FRC Toolchain to a directory other than the default, o
Once a PR has been submitted, formatting can be run in CI by commenting `/format` on the PR. A new commit will be pushed with the formatting changes.
> [!NOTE]
> The `/format` action has been temporarily disabled. The individual formatting commands can be run locally as shown below. Alternately, the Lint and Format action for a PR will upload a patch file that can be downloaded and applied manually.
#### wpiformat
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.

View File

@@ -33,7 +33,6 @@ jQuery wpinet/src/main/native/resources/jquery-*
popper.js wpinet/src/main/native/resources/popper-*
units wpimath/src/main/native/include/units/
Eigen wpimath/src/main/native/thirdparty/eigen/include/
StackWalker wpiutil/src/main/native/windows/StackWalker.*
Team 254 Library wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
@@ -54,6 +53,7 @@ nanopb wpiutil/src/main/native/thirdparty/nanopb
protobuf wpiutil/src/main/native/thirdparty/protobuf
mrcal wpical/src/main/native/thirdparty/mrcal
libdogleg wpical/src/main/native/thirdparty/libdogleg
Simd hal/src/main/native/athena/simd
Additionally, glfw, memory, and nanopb were all modified for use in WPILib.
@@ -1025,35 +1025,6 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
defined by the Mozilla Public License, v. 2.0.
===================
StackWalker License
===================
Copyright (c) 2005-2013, Jochen Kalmbach
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of Jochen Kalmbach nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================
Team 254 Library
================
@@ -1702,3 +1673,29 @@ This program is free software: you can redistribute it and/or modify it under th
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
The full text of the license is available at http://www.gnu.org/licenses
============
Simd License
============
MIT License
Copyright (c) 2011-2017 Ihar Yermalayeu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -35,8 +35,8 @@ maven_install(
# Download toolchains
http_archive(
name = "rules_bzlmodrio_toolchains",
sha256 = "fe267e2af53c1def1e962700a9aeda9e8fdfa9fb46b72167c615ec0e25447dd6",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1/rules_bzlmodRio_toolchains-2025-1.tar.gz",
sha256 = "ff25b5f9445cbd43759be4c6582b987d1065cf817c593eedc7ada1a699298c84",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1.bcr2/rules_bzlmodRio_toolchains-2025-1.bcr2.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
@@ -50,8 +50,8 @@ load_toolchains()
#
http_archive(
name = "rules_bzlmodrio_jdk",
sha256 = "a00d5fa971fbcad8a17b1968cdc5350688397035e90b0cb94e040d375ecd97b4",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.8.1-1/rules_bzlmodRio_jdk-17.0.8.1-1.tar.gz",
sha256 = "81869fe9860e39b17e4a9bc1d33c1ca2faede7e31d9538ed0712406f753a2163",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.12-7/rules_bzlmodRio_jdk-17.0.12-7.tar.gz",
)
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
@@ -62,9 +62,15 @@ register_toolchains(
"@local_roborio//:macos",
"@local_roborio//:linux",
"@local_roborio//:windows",
"@local_raspi_32//:macos",
"@local_raspi_32//:linux",
"@local_raspi_32//:windows",
"@local_systemcore//:macos",
"@local_systemcore//:linux",
"@local_systemcore//:windows",
"@local_raspi_bullseye_32//:macos",
"@local_raspi_bullseye_32//:linux",
"@local_raspi_bullseye_32//:windows",
"@local_raspi_bookworm_32//:macos",
"@local_raspi_bookworm_32//:linux",
"@local_raspi_bookworm_32//:windows",
"@local_bullseye_32//:macos",
"@local_bullseye_32//:linux",
"@local_bullseye_32//:windows",
@@ -83,8 +89,8 @@ setup_legacy_setup_jdk_dependencies()
http_archive(
name = "bzlmodrio-ni",
sha256 = "197fceac88bf44fb8427d5e000b0083118d3346172dd2ad31eccf83a5e61b3ce",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.0.0/bzlmodRio-ni-2025.0.0.tar.gz",
sha256 = "fff62c3cb3e83f9a0d0a01f1739477c9ca5e9a6fac05be1ad59dafcd385801f7",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.2.0/bzlmodRio-ni-2025.2.0.tar.gz",
)
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
@@ -93,8 +99,8 @@ setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "4f4a607956ca8555618736c3058dd96e09d02df19e95088c1e352d2319fd70c7",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-2/bzlmodRio-opencv-2025.4.10.0-2.tar.gz",
sha256 = "ba3f4910ce9cc0e08abff732aeb5835b1bcfd864ca5296edeadcf2935f7e81b9",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-3.bcr1/bzlmodRio-opencv-2025.4.10.0-3.bcr1.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")

View File

@@ -14,14 +14,16 @@ public enum AprilTagFields {
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json"),
/** 2025 Reefscape. */
k2025Reefscape("2025-reefscape.json");
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2025Reefscape;
public static final AprilTagFields kDefaultField = k2025ReefscapeWelded;
/** Resource filename. */
public final String m_resourceFile;

View File

@@ -133,7 +133,8 @@ namespace frc {
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
std::string_view GetResource_2025_reefscape_json();
std::string_view GetResource_2025_reefscape_welded_json();
std::string_view GetResource_2025_reefscape_andymark_json();
} // namespace frc
@@ -149,8 +150,11 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::k2025Reefscape:
fieldString = GetResource_2025_reefscape_json();
case AprilTagField::k2025ReefscapeWelded:
fieldString = GetResource_2025_reefscape_welded_json();
break;
case AprilTagField::k2025ReefscapeAndyMark:
fieldString = GetResource_2025_reefscape_andymark_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");

View File

@@ -20,10 +20,12 @@ enum class AprilTagField {
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
/// 2025 Reefscape.
k2025Reefscape,
/// 2025 Reefscape AndyMark (see TU12).
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// Alias to the current game.
kDefaultField = k2025Reefscape,
kDefaultField = k2025ReefscapeWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users

View File

@@ -0,0 +1,23 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,656.98,24.73,58.5,126,0
2,656.98,291.9,58.5,234,0
3,452.4,316.21,51.25,270,0
4,365.2,241.44,73.54,0,30
5,365.2,75.19,73.54,0,30
6,530.49,129.97,12.13,300,0
7,546.87,158.3,12.13,0,0
8,530.49,186.63,12.13,60,0
9,497.77,186.63,12.13,120,0
10,481.39,158.3,12.13,180,0
11,497.77,129.97,12.13,240,0
12,33.91,24.73,58.5,54,0
13,33.91,291.9,58.5,306,0
14,325.68,241.44,73.54,180,30
15,325.68,75.19,73.54,180,30
16,238.49,0.42,51.25,90,0
17,160.39,129.97,12.13,240,0
18,144,158.3,12.13,180,0
19,160.39,186.63,12.13,120,0
20,193.1,186.63,12.13,60,0
21,209.49,158.3,12.13,0,0
22,193.1,129.97,12.13,300,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 656.98 24.73 58.5 126 0
3 2 656.98 291.9 58.5 234 0
4 3 452.4 316.21 51.25 270 0
5 4 365.2 241.44 73.54 0 30
6 5 365.2 75.19 73.54 0 30
7 6 530.49 129.97 12.13 300 0
8 7 546.87 158.3 12.13 0 0
9 8 530.49 186.63 12.13 60 0
10 9 497.77 186.63 12.13 120 0
11 10 481.39 158.3 12.13 180 0
12 11 497.77 129.97 12.13 240 0
13 12 33.91 24.73 58.5 54 0
14 13 33.91 291.9 58.5 306 0
15 14 325.68 241.44 73.54 180 30
16 15 325.68 75.19 73.54 180 30
17 16 238.49 0.42 51.25 90 0
18 17 160.39 129.97 12.13 240 0
19 18 144 158.3 12.13 180 0
20 19 160.39 186.63 12.13 120 0
21 20 193.1 186.63 12.13 60 0
22 21 209.49 158.3 12.13 0 0
23 22 193.1 129.97 12.13 300 0

View File

@@ -0,0 +1,404 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 16.687292,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.4539904997395468,
"X": 0.0,
"Y": 0.0,
"Z": 0.8910065241883678
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.687292,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.45399049973954675,
"X": -0.0,
"Y": 0.0,
"Z": 0.8910065241883679
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.49096,
"y": 8.031733999999998,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 13.474446,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 13.890498,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 13.474446,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.643358,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.227305999999999,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.643358,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.8910065241883679,
"X": 0.0,
"Y": 0.0,
"Z": 0.45399049973954675
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.8910065241883678,
"X": -0.0,
"Y": 0.0,
"Z": 0.45399049973954686
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 8.272272,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 8.272272,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 6.057646,
"y": 0.010667999999999999,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 3.6576,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 5.321046,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
}
],
"field": {
"length": 17.548,
"width": 8.042
}
}

View File

@@ -9,5 +9,5 @@ repositories {
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2025.9.0"
implementation "edu.wpi.first:native-utils:2025.9.1"
}

View File

@@ -28,7 +28,7 @@ public class WPIJREArtifact extends MavenArtifact {
private boolean checkJreVersion = true;
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-1"
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-3"
@Inject
public WPIJREArtifact(String name, RemoteTarget target) {

View File

@@ -46,7 +46,7 @@ macro(wpilib_target_warnings target)
# Suppress warning "enumeration types with a fixed underlying type are a
# Clang extension"
if(APPLE)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-fixed-enum-extension>)
endif()

View File

@@ -68,7 +68,9 @@ public class AnnotationProcessor extends AbstractProcessor {
customLoggers.putAll(processCustomLoggers(roundEnv, customLogger));
});
// Get all root types (classes and interfaces), excluding packages and modules
roundEnv.getRootElements().stream()
.filter(e -> e instanceof TypeElement)
.filter(
e ->
processingEnv
@@ -267,12 +269,18 @@ public class AnnotationProcessor extends AbstractProcessor {
return false;
}
processingEnv
.getMessager()
.printMessage(
Diagnostic.Kind.NOTE,
"[EPILOGUE] Excluded from logs because " + type + " is not a loggable data type",
element);
var classConfig = element.getEnclosingElement().getAnnotation(Logged.class);
if (classConfig == null || classConfig.warnForNonLoggableTypes()) {
// Not loggable and not explicitly opted out of logging; print a warning message
processingEnv
.getMessager()
.printMessage(
Diagnostic.Kind.NOTE,
"[EPILOGUE] Excluded from logs because " + type + " is not a loggable data type",
element);
}
return true;
}

View File

@@ -71,6 +71,11 @@ public class LoggerGenerator {
public Naming defaultNaming() {
return Naming.USE_CODE_NAME;
}
@Override
public boolean warnForNonLoggableTypes() {
return false;
}
};
public LoggerGenerator(ProcessingEnvironment processingEnv, List<ElementHandler> handlers) {

View File

@@ -1772,7 +1772,7 @@ class AnnotationProcessorTest {
"""
package edu.wpi.first.epilogue;
@Logged
@Logged(warnForNonLoggableTypes = true)
class Example {
Throwable t;
}
@@ -1974,6 +1974,37 @@ class AnnotationProcessorTest {
assertLoggerGenerates(source, expectedRootLogger);
}
@Test
void doesNotBreakWithPackageInfo() {
String source =
"""
package example;
import edu.wpi.first.epilogue.*;
@Logged
class Example {}
""";
String packageInfo = """
package example;
""";
Compilation compilation =
javac()
.withOptions(kJavaVersionOptions)
.withProcessors(new AnnotationProcessor())
.compile(
JavaFileObjects.forSourceString("example.Example", source),
JavaFileObjects.forSourceString("example.package-info", packageInfo));
assertThat(compilation).succeeded();
compilation.generatedSourceFiles().stream()
.filter(jfo -> jfo.getName().contains("Example"))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Logger file was not generated!"));
}
private void assertCompilationError(
String message, long lineNumber, long col, Diagnostic<? extends JavaFileObject> diagnostic) {
assertAll(

View File

@@ -124,4 +124,12 @@ public @interface Logged {
* for all logged fields and methods in an annotated class
*/
Naming defaultNaming() default Naming.USE_CODE_NAME;
/**
* Class-level only: if {@link #strategy()} is {@link Strategy#OPT_OUT}, this can be used to quiet
* the warnings that are printed for non-loggable fields and methods detected within the class.
*
* @return true if warnings should be printed, or false if warnings should not be printed
*/
boolean warnForNonLoggableTypes() default false;
}

View File

@@ -117,7 +117,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -130,7 +130,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -143,7 +143,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -156,7 +156,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -169,7 +169,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -182,7 +182,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -208,7 +208,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value);
}
@@ -234,7 +234,7 @@ public class LazyBackend implements EpilogueBackend {
return;
}
m_previousValues.put(identifier, value);
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value, struct);
}
}

View File

@@ -0,0 +1,45 @@
// 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.
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.struct.Struct;
import edu.wpi.first.util.struct.StructSerializable;
import java.nio.ByteBuffer;
public record CustomStruct(int x) implements StructSerializable {
public static final Serializer struct = new Serializer();
public static final class Serializer implements Struct<CustomStruct> {
@Override
public Class<CustomStruct> getTypeClass() {
return CustomStruct.class;
}
@Override
public String getTypeName() {
return "CustomStruct";
}
@Override
public int getSize() {
return kSizeInt32;
}
@Override
public String getSchema() {
return "int32 x;";
}
@Override
public CustomStruct unpack(ByteBuffer bb) {
return new CustomStruct(bb.getInt());
}
@Override
public void pack(ByteBuffer bb, CustomStruct value) {
bb.putInt(value.x);
}
}
}

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.epilogue.logging;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -53,4 +54,135 @@ class LazyBackendTest {
backend.getEntries());
}
}
@Test
void inPlaceByteArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
byte[] arr = new byte[] {0};
lazy.log("arr", arr);
arr[0] = 1;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new byte[] {0}, (byte[]) backend.getEntries().get(0).value());
assertArrayEquals(new byte[] {1}, (byte[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceIntArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
int[] arr = new int[] {0};
lazy.log("arr", arr);
arr[0] = 1;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new int[] {0}, (int[]) backend.getEntries().get(0).value());
assertArrayEquals(new int[] {1}, (int[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceLongArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
long[] arr = new long[] {0};
lazy.log("arr", arr);
arr[0] = 1;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new long[] {0}, (long[]) backend.getEntries().get(0).value());
assertArrayEquals(new long[] {1}, (long[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceFloatArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
float[] arr = new float[] {0};
lazy.log("arr", arr);
arr[0] = 1;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new float[] {0}, (float[]) backend.getEntries().get(0).value());
assertArrayEquals(new float[] {1}, (float[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceDoubleArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
double[] arr = new double[] {0};
lazy.log("arr", arr);
arr[0] = 1;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new double[] {0}, (double[]) backend.getEntries().get(0).value());
assertArrayEquals(new double[] {1}, (double[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceBooleanArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
boolean[] arr = new boolean[] {false};
lazy.log("arr", arr);
arr[0] = true;
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new boolean[] {false}, (boolean[]) backend.getEntries().get(0).value());
assertArrayEquals(new boolean[] {true}, (boolean[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceStringArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
String[] arr = new String[] {"0"};
lazy.log("arr", arr);
arr[0] = "1";
lazy.log("arr", arr);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(new String[] {"0"}, (String[]) backend.getEntries().get(0).value());
assertArrayEquals(new String[] {"1"}, (String[]) backend.getEntries().get(1).value());
}
@Test
void inPlaceStructArray() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
CustomStruct[] arr = new CustomStruct[] {new CustomStruct(0)};
lazy.log("arr", arr, CustomStruct.struct);
arr[0] = new CustomStruct(1);
lazy.log("arr", arr, CustomStruct.struct);
assertEquals(2, backend.getEntries().size());
assertArrayEquals(
new byte[] {0x00, 0x00, 0x00, 0x00}, (byte[]) backend.getEntries().get(0).value());
assertArrayEquals(
new byte[] {0x01, 0x00, 0x00, 0x00}, (byte[]) backend.getEntries().get(1).value());
}
}

View File

@@ -55,32 +55,32 @@ public class TestBackend implements EpilogueBackend {
@Override
public void log(String identifier, byte[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public void log(String identifier, int[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public void log(String identifier, long[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public void log(String identifier, float[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public void log(String identifier, double[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public void log(String identifier, boolean[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
@@ -90,19 +90,27 @@ public class TestBackend implements EpilogueBackend {
@Override
public void log(String identifier, String[] value) {
m_entries.add(new LogEntry<>(identifier, value));
m_entries.add(new LogEntry<>(identifier, value.clone()));
}
@Override
public <S> void log(String identifier, S value, Struct<S> struct) {
var serialized = StructBuffer.create(struct).write(value).array();
var buffer = StructBuffer.create(struct).write(value).position(0);
var serialized = new byte[buffer.capacity()];
for (int i = 0; i < buffer.capacity(); i++) {
serialized[i] = buffer.get();
}
m_entries.add(new LogEntry<>(identifier, serialized));
}
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {
var serialized = StructBuffer.create(struct).writeArray(value).array();
var buffer = StructBuffer.create(struct).writeArray(value).position(0);
var serialized = new byte[buffer.capacity()];
for (int i = 0; i < buffer.capacity(); i++) {
serialized[i] = buffer.get();
}
m_entries.add(new LogEntry<>(identifier, serialized));
}

View File

@@ -36,13 +36,13 @@ endif()
generate_resources(
src/main/native/resources/edu/wpi/first/fields
generated/main/cpp
${CMAKE_CURRENT_BINARY_DIR}/generated/main/cpp
FIELDS
fields
field_images_resources_src
)
add_library(fieldImages src/main/native/cpp/fields.cpp ${field_images_resources_src})
add_library(fieldImages ${field_images_resources_src} src/main/native/cpp/fields.cpp)
set_target_properties(fieldImages PROPERTIES DEBUG_POSTFIX "d")
set_property(TARGET fieldImages PROPERTY FOLDER "libraries")

View File

@@ -224,8 +224,8 @@ class ObjectInfo {
class FieldInfo {
public:
static constexpr auto kDefaultWidth = 16.541052_m;
static constexpr auto kDefaultHeight = 8.211_m;
static constexpr auto kDefaultWidth = 17.5483_m;
static constexpr auto kDefaultHeight = 8.0519_m;
explicit FieldInfo(Storage& storage);
@@ -345,7 +345,7 @@ static bool InputPose(frc::Pose2d* pose) {
}
FieldInfo::FieldInfo(Storage& storage)
: m_builtin{storage.GetString("builtin", "2024 Crescendo")},
: m_builtin{storage.GetString("builtin", "2025 Reefscape")},
m_filename{storage.GetString("image")},
m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},

View File

@@ -15,6 +15,8 @@ kFramework_ROS = 5
kFramework_RobotBuilder = 6
kFramework_AdvantageKit = 7
kFramework_MagicBot = 8
kFramework_KitBotTraditional = 9
kFramework_KitBotInline = 10
kRobotDrive_ArcadeStandard = 1
kRobotDrive_ArcadeButtonSpin = 2
kRobotDrive_ArcadeRatioCurve = 3

View File

@@ -317,6 +317,10 @@ public final class FRCNetComm {
public static final int kFramework_AdvantageKit = 7;
/** kFramework_MagicBot = 8. */
public static final int kFramework_MagicBot = 8;
/** kFramework_KitBotTraditional = 9. */
public static final int kFramework_KitBotTraditional = 9;
/** kFramework_KitBotInline = 10. */
public static final int kFramework_KitBotInline = 10;
/** kRobotDrive_ArcadeStandard = 1. */
public static final int kRobotDrive_ArcadeStandard = 1;
/** kRobotDrive_ArcadeButtonSpin = 2. */

View File

@@ -197,6 +197,8 @@ namespace HALUsageReporting {
kFramework_RobotBuilder = 6,
kFramework_AdvantageKit = 7,
kFramework_MagicBot = 8,
kFramework_KitBotTraditional = 9,
kFramework_KitBotInline = 10,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,

View File

@@ -170,6 +170,8 @@ typedef enum
kFramework_RobotBuilder = 6,
kFramework_AdvantageKit = 7,
kFramework_MagicBot = 8,
kFramework_KitBotTraditional = 9,
kFramework_KitBotInline = 10,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,

View File

@@ -10,6 +10,13 @@ package edu.wpi.first.hal;
* @see "hal/AddressableLED.h"
*/
public class AddressableLEDJNI extends JNIWrapper {
public static final int COLOR_ORDER_RGB = 0;
public static final int COLOR_ORDER_RBG = 1;
public static final int COLOR_ORDER_BGR = 2;
public static final int COLOR_ORDER_BRG = 3;
public static final int COLOR_ORDER_GBR = 4;
public static final int COLOR_ORDER_GRB = 5;
/**
* Initialize Addressable LED using a PWM Digital handle.
*
@@ -27,6 +34,16 @@ public class AddressableLEDJNI extends JNIWrapper {
*/
public static native void free(int handle);
/**
* Sets the color order for the addressable LED output. The default order is GRB.
*
* <p>This will take effect on the next call to {@link #setData(int, byte[])}.
*
* @param handle the Addressable LED handle
* @param colorOrder the color order
*/
public static native void setColorOrder(int handle, int colorOrder);
/**
* Sets the length of the LED strip.
*
@@ -53,7 +70,8 @@ public class AddressableLEDJNI extends JNIWrapper {
/**
* Sets the bit timing.
*
* <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to be set for those.
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing needs to be set for
* those.
*
* @param handle the Addressable LED handle
* @param highTime0NanoSeconds high time for 0 bit (default 400ns)
@@ -72,7 +90,7 @@ public class AddressableLEDJNI extends JNIWrapper {
/**
* Sets the sync time.
*
* <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B.
* <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B and WS2815.
*
* @param handle the Addressable LED handle
* @param syncTimeMicroSeconds the sync time (default 280us)

View File

@@ -61,12 +61,12 @@ public class SPIJNI extends JNIWrapper {
* @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
* @param dataToSend Buffer of data to send as part of the transaction.
* @param dataReceived Buffer to read data into.
* @param size Number of bytes to transfer. [0..7]
* @param size Number of bytes to transfer.
* @return Number of bytes transferred, -1 for error
* @see "HAL_TransactionSPI"
*/
public static native int spiTransaction(
int port, ByteBuffer dataToSend, ByteBuffer dataReceived, byte size);
int port, ByteBuffer dataToSend, ByteBuffer dataReceived, int size);
/**
* Performs an SPI send/receive transaction.
@@ -77,12 +77,12 @@ public class SPIJNI extends JNIWrapper {
* @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
* @param dataToSend Buffer of data to send as part of the transaction.
* @param dataReceived Buffer to read data into.
* @param size Number of bytes to transfer. [0..7]
* @param size Number of bytes to transfer.
* @return Number of bytes transferred, -1 for error
* @see "HAL_TransactionSPI"
*/
public static native int spiTransactionB(
int port, byte[] dataToSend, byte[] dataReceived, byte size);
int port, byte[] dataToSend, byte[] dataReceived, int size);
/**
* Executes a write transaction with the device.
@@ -95,7 +95,7 @@ public class SPIJNI extends JNIWrapper {
* @return The number of bytes written. -1 for an error
* @see "HAL_WriteSPI"
*/
public static native int spiWrite(int port, ByteBuffer dataToSend, byte sendSize);
public static native int spiWrite(int port, ByteBuffer dataToSend, int sendSize);
/**
* Executes a write transaction with the device.
@@ -108,7 +108,7 @@ public class SPIJNI extends JNIWrapper {
* @return The number of bytes written. -1 for an error
* @see "HAL_WriteSPI"
*/
public static native int spiWriteB(int port, byte[] dataToSend, byte sendSize);
public static native int spiWriteB(int port, byte[] dataToSend, int sendSize);
/**
* Executes a read from the device.
@@ -121,11 +121,11 @@ public class SPIJNI extends JNIWrapper {
* @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
* @param initiate initiates a transaction when true. Just reads when false.
* @param dataReceived A pointer to the array of bytes to store the data read from the device.
* @param size The number of bytes to read in the transaction. [1..7]
* @param size The number of bytes to read in the transaction.
* @return Number of bytes read. -1 for error.
* @see "HAL_ReadSPI"
*/
public static native int spiRead(int port, boolean initiate, ByteBuffer dataReceived, byte size);
public static native int spiRead(int port, boolean initiate, ByteBuffer dataReceived, int size);
/**
* Executes a read from the device.
@@ -138,11 +138,11 @@ public class SPIJNI extends JNIWrapper {
* @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
* @param initiate initiates a transaction when true. Just reads when false.
* @param dataReceived A pointer to the array of bytes to store the data read from the device.
* @param size The number of bytes to read in the transaction. [1..7]
* @param size The number of bytes to read in the transaction.
* @return Number of bytes read. -1 for error.
* @see "HAL_ReadSPI"
*/
public static native int spiReadB(int port, boolean initiate, byte[] dataReceived, byte size);
public static native int spiReadB(int port, boolean initiate, byte[] dataReceived, int size);
/**
* Closes the SPI port.

View File

@@ -9,6 +9,7 @@
#include <fmt/format.h>
#include "AddressableLEDSimd.h"
#include "ConstantsInternal.h"
#include "DigitalInternal.h"
#include "FPGACalls.h"
@@ -21,6 +22,7 @@
#include "hal/handles/LimitedHandleResource.h"
using namespace hal;
using namespace hal::detail;
namespace {
struct AddressableLED {
@@ -28,6 +30,7 @@ struct AddressableLED {
void* ledBuffer;
size_t ledBufferSize;
int32_t stringLength = 1;
HAL_AddressableLEDColorOrder colorOrder = HAL_ALED_GRB;
};
} // namespace
@@ -47,6 +50,37 @@ void InitializeAddressableLED() {
static constexpr const char* HmbName = "HMB_0_LED";
static void ConvertAndCopyLEDData(void* dst,
const struct HAL_AddressableLEDData* src,
int32_t len,
HAL_AddressableLEDColorOrder order) {
switch (order) {
case HAL_ALED_GRB:
std::memcpy(dst, src, len * sizeof(HAL_AddressableLEDData));
break;
case HAL_ALED_RGB:
ConvertPixels<HAL_ALED_RGB>(reinterpret_cast<const uint8_t*>(src),
reinterpret_cast<uint8_t*>(dst), len);
break;
case HAL_ALED_RBG:
ConvertPixels<HAL_ALED_RBG>(reinterpret_cast<const uint8_t*>(src),
reinterpret_cast<uint8_t*>(dst), len);
break;
case HAL_ALED_BGR:
ConvertPixels<HAL_ALED_BGR>(reinterpret_cast<const uint8_t*>(src),
reinterpret_cast<uint8_t*>(dst), len);
break;
case HAL_ALED_BRG:
ConvertPixels<HAL_ALED_BRG>(reinterpret_cast<const uint8_t*>(src),
reinterpret_cast<uint8_t*>(dst), len);
break;
case HAL_ALED_GBR:
ConvertPixels<HAL_ALED_GBR>(reinterpret_cast<const uint8_t*>(src),
reinterpret_cast<uint8_t*>(dst), len);
break;
}
}
extern "C" {
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
@@ -125,6 +159,19 @@ void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
addressableLEDHandles->Free(handle);
}
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status) {
auto led = addressableLEDHandles->Get(handle);
if (!led) {
*status = HAL_HANDLE_ERROR;
return;
}
led->colorOrder = colorOrder;
}
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
HAL_DigitalHandle outputPort,
int32_t* status) {
@@ -203,7 +250,7 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
return;
}
std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData));
ConvertAndCopyLEDData(led->ledBuffer, data, length, led->colorOrder);
asm("dmb");

View File

@@ -0,0 +1,273 @@
// 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 <utility>
#include "hal/AddressableLEDTypes.h"
#include "simd/simd.h"
// Timing info
// https://developer.arm.com/documentation/ddi0409/i/instruction-timing/instruction-specific-scheduling/advanced-simd-load-store-instructions?lang=en
namespace hal::detail {
using namespace Simd::Neon;
template <typename T>
using ConvertFunc = void (*)(T);
/*
* Conversion funtions perform in-place conversion by swapping elements.
* The names of the functions indicate the wire output (default GRB),
* but the FPGA takes sequences of BGR_.
*/
template <typename T>
void ToRGB(T val) {
std::swap(val[1], val[2]); // swap G and R
}
template <typename T>
void ToRBG(T val) {
std::swap(val[1], val[2]); // swap G and R
std::swap(val[0], val[2]); // swap B and G
}
template <typename T>
void ToBGR(T val) {
std::swap(val[0], val[1]); // swap B and G
std::swap(val[0], val[2]); // swap G and R
}
template <typename T>
void ToBRG(T val) {
std::swap(val[0], val[1]); // swap B and G
}
template <typename T>
void ToGBR(T val) {
std::swap(val[0], val[2]); // swap B and R
}
/**
* Copies 16 pixels from src to dst using NEON instructions, converting using
* the provided conversion function. Optimizes based on alignment of input and
* output arrays specified by srcAlign and dstAlign
* @tparam srcAlign whether src is aligned to the size of a NEON register (16
* bytes)
* @tparam dstAlign whether dst is aligned to the size of a NEON register (16
* bytes)
* @tparam the conversion function
* @param[in] src The source array
* @param[out] dst the destination array
* @pre src and dst must contain at least 64 bytes (16 pixels)
* @pre if srcAlign is true, src must be 16 byte aligned
* @pre if dstAlign is true, src muts be 16 byte aligned
*/
template <bool srcAlign, bool dstAlign, ConvertFunc<uint8x16_t*> Convert>
void ConvertNEON_16(const uint8_t* src, uint8_t* dst) {
uint8x16x4_t pixels = Load4<srcAlign>(src);
Convert(pixels.val);
Store4<dstAlign>(dst, pixels);
}
/**
* Copies 8 pixels from src to dst using NEON instructions, converting using
* the provided conversion function. Optimizes based on alignment of input and
* output arrays specified by srcAlign and dstAlign
* @tparam srcAlign whether src is aligned to the size of a NEON register (16
* bytes)
* @tparam dstAlign whether dst is aligned to the size of a NEON register (16
* bytes)
* @tparam the conversion function
* @param[in] src The source array
* @param[out] dst the destination array
* @pre src and dst must contain at least 32 bytes (8 pixels)
* @pre if srcAlign is true, src must be 16 byte aligned
* @pre if dstAlign is true, src muts be 16 byte aligned
*/
template <bool srcAlign, bool dstAlign, ConvertFunc<uint8x8_t*> Convert>
void ConvertNEON_8(const uint8_t* src, uint8_t* dst) {
uint8x8x4_t pixels = LoadHalf4<srcAlign>(src);
Convert(pixels.val);
Store4<dstAlign>(dst, pixels);
}
/**
* Copies 16 pixels from src to dst, converting from GRB (wire order) to order.
* Optimizes based on alignment of input and output arrays specified by srcAlign
* and dstAlign
* @tparam order the color order to convert to
* @tparam srcAlign whether src is aligned to the size of a NEON register (16
* bytes)
* @tparam dstAlign whether dst is aligned to the size of a NEON register (16
* bytes)
* @param[in] src The source array
* @param[out] dst the destination array
* @pre src and dst must contain at least 64 bytes (16 pixels)
* @pre if srcAlign is true, src must be 16 byte aligned
* @pre if dstAlign is true, src muts be 16 byte aligned
*/
template <HAL_AddressableLEDColorOrder order, bool srcAlign, bool dstAlign>
void Convert16Pixels(const uint8_t* src, uint8_t* dst) {
switch (order) {
case HAL_ALED_RGB:
ConvertNEON_16<srcAlign, dstAlign, ToRGB>(src, dst);
break;
case HAL_ALED_RBG:
ConvertNEON_16<srcAlign, dstAlign, ToRBG>(src, dst);
break;
case HAL_ALED_BGR:
ConvertNEON_16<srcAlign, dstAlign, ToBGR>(src, dst);
break;
case HAL_ALED_BRG:
ConvertNEON_16<srcAlign, dstAlign, ToBRG>(src, dst);
break;
case HAL_ALED_GBR:
ConvertNEON_16<srcAlign, dstAlign, ToGBR>(src, dst);
break;
}
}
/**
* Copies 8 pixels from src to dst, converting from GRB (wire order) to order.
* Optimizes based on alignment of input and output arrays specified by srcAlign
* and dstAlign
* @tparam order the color order to convert to
* @tparam srcAlign whether src is aligned to the size of a NEON register (16
* bytes)
* @tparam dstAlign whether dst is aligned to the size of a NEON register (16
* bytes)
* @param[in] src The source array
* @param[out] dst the destination array
* @pre src and dst must contain at least 32 bytes (8 pixels)
* @pre if srcAlign is true, src must be 16 byte aligned
* @pre if dstAlign is true, src muts be 16 byte aligned
*/
template <HAL_AddressableLEDColorOrder order, bool srcAlign, bool dstAlign>
void Convert8Pixels(const uint8_t* src, uint8_t* dst) {
switch (order) {
case HAL_ALED_RGB:
ConvertNEON_8<srcAlign, dstAlign, ToRGB>(src, dst);
break;
case HAL_ALED_RBG:
ConvertNEON_8<srcAlign, dstAlign, ToRBG>(src, dst);
break;
case HAL_ALED_BGR:
ConvertNEON_8<srcAlign, dstAlign, ToBGR>(src, dst);
break;
case HAL_ALED_BRG:
ConvertNEON_8<srcAlign, dstAlign, ToBRG>(src, dst);
break;
case HAL_ALED_GBR:
ConvertNEON_8<srcAlign, dstAlign, ToGBR>(src, dst);
break;
}
}
/**
* Copies 1 pixel from src to dst, converting from RGB to the specified order.
* @param[in] order the color order to convert to
* @param[in] in the source array
* @param[out] the destination array
* @pre in and out must contain at least 1 pixel (4 bytes).
*/
void Convert1Pixel(HAL_AddressableLEDColorOrder order, const uint8_t* src,
uint8_t* dst) {
uint8_t tmp[4];
std::memcpy(tmp, src, 4); // Load 4 bytes
// convert based on order
switch (order) {
case HAL_ALED_RGB:
ToRGB(tmp);
break;
case HAL_ALED_RBG:
ToRBG(tmp);
break;
case HAL_ALED_BGR:
ToBGR(tmp);
break;
case HAL_ALED_BRG:
ToBRG(tmp);
break;
case HAL_ALED_GBR:
ToGBR(tmp);
break;
case HAL_ALED_GRB:
break; // this shouldn't ever get hit but compiler
// wants this to be exhaustive
}
std::memcpy(dst, tmp, 4); // Store 4 bytes
}
/**
* Copies len pixels from src to dst, converting from GRB (wire order) to order.
* Optimizes based on alignment of input and output arrays specified by srcAlign
* and dstAlign
* @tparam order the color order to convert to
* @tparam srcAlign whether src is aligned to the size of a NEON register (16
* bytes)
* @tparam dstAlign whether dst is aligned to the size of a NEON register (16
* bytes)
* @param[in] src The source array
* @param[out] dst the destination array
* @param[in] len the size (in pixels, len = (size in bytes) / 4)
* @pre src and dst must have at least len*4 capacity in bytes
* @pre if srcAlign is true, src must be 16 byte aligned
* @pre if dstAlign is true, src muts be 16 byte aligned
*/
template <HAL_AddressableLEDColorOrder order, bool srcAlign, bool dstAlign>
void ConvertPixels(const uint8_t* src, uint8_t* dst, size_t len) {
if (len >= 16) {
constexpr size_t A4 =
A * 4; // Stride of 1 16-pixel conversion operation. (4 NEON registers)
size_t size = len * 4;
size_t aligned = Simd::AlignLo(
size, A4); // number of bytes we can copy with whole 16-pixel strides
for (size_t i = 0; i < aligned; i += A4) {
Convert16Pixels<order, srcAlign, dstAlign>(src + i, dst + i);
}
if (aligned < size) {
Convert16Pixels<order, false, false>(
src + size - A4,
dst + size - A4); // copy last 16 pixels, possibly recopying.
}
} else if (len >= 8) {
// If len between 8 and 16, we can do 1 or 2 8-pixel copies
Convert8Pixels<order, srcAlign, dstAlign>(src, dst);
if (len > 8) {
size_t recopyOffset = (len * 4) - (HA * 4);
Convert8Pixels<order, false, false>(
src + recopyOffset,
dst + recopyOffset); // copy last 8 pixels, possibly recopying
}
} else {
// Just copy pixel-by-pixel for <8
for (size_t i = 0; i < len; i += 4) {
Convert1Pixel(order, src + i, dst + i);
}
}
}
/**
* Copies pixelCount pixels from src to dst, converting from RGB to the
* specified order
* @tparam order the color order to convert to
* @param src the source array
* @param dst the destination array
* @param pixelCount the number of pixels to convert and copy
*/
template <HAL_AddressableLEDColorOrder order>
void ConvertPixels(const uint8_t* src, uint8_t* dst, size_t pixelCount) {
if (Aligned(src) && Aligned(dst)) {
ConvertPixels<order, true, true>(src, dst, pixelCount);
} else if (Aligned(src)) {
ConvertPixels<order, true, false>(src, dst, pixelCount);
} else if (Aligned(dst)) {
ConvertPixels<order, false, true>(src, dst, pixelCount);
} else {
ConvertPixels<order, false, false>(src, dst, pixelCount);
}
}
} // namespace hal::detail

View File

@@ -0,0 +1,174 @@
// 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.
// This file contains modified snippets from the Simd Library by Ihar Yermalayeu
// (http://ermig1979.github.io/Simd). The original source file names are listed
// above each section.
/*
* Simd Library (http://ermig1979.github.io/Simd).
*
* Copyright (c) 2011-2024 Yermalayeu Ihar.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <arm_neon.h>
#include <cstddef>
#include <cstring>
// SimdLib.h
#define SIMD_INLINE inline __attribute__((always_inline))
// SimdMemory.h
namespace Simd {
SIMD_INLINE size_t AlignLo(size_t size, size_t align) {
return size & ~(align - 1);
}
SIMD_INLINE void* AlignLo(const void* ptr, size_t align) {
return reinterpret_cast<void*>(reinterpret_cast<size_t>(ptr) & ~(align - 1));
}
SIMD_INLINE bool Aligned(size_t size, size_t align) {
return size == AlignLo(size, align);
}
SIMD_INLINE bool Aligned(const void* ptr, size_t align) {
return ptr == AlignLo(ptr, align);
}
} // namespace Simd
namespace Simd::Neon {
SIMD_INLINE bool Aligned(size_t size, size_t align = sizeof(uint8x16_t)) {
return Simd::Aligned(size, align);
}
SIMD_INLINE bool Aligned(const void* ptr, size_t align = sizeof(uint8x16_t)) {
return Simd::Aligned(ptr, align);
}
} // namespace Simd::Neon
// SimdConst.h
namespace Simd::Neon {
const size_t A = sizeof(uint8x16_t);
const size_t DA = 2 * A;
const size_t QA = 4 * A;
const size_t OA = 8 * A;
const size_t HA = A / 2;
} // namespace Simd::Neon
// SimdLoad.h
namespace Simd::Neon {
template <bool align>
SIMD_INLINE uint8x8x4_t LoadHalf4(const uint8_t* p);
template <>
SIMD_INLINE uint8x8x4_t LoadHalf4<false>(const uint8_t* p) {
#if defined(__GNUC__) && SIMD_NEON_PREFECH_SIZE
__builtin_prefetch(p + SIMD_NEON_PREFECH_SIZE);
#endif
return vld4_u8(p);
}
template <>
SIMD_INLINE uint8x8x4_t LoadHalf4<true>(const uint8_t* p) {
#if defined(__GNUC__)
#if SIMD_NEON_PREFECH_SIZE
__builtin_prefetch(p + SIMD_NEON_PREFECH_SIZE);
#endif
uint8_t* _p = static_cast<uint8_t*>(__builtin_assume_aligned(p, 8));
return vld4_u8(_p);
#elif defined(_MSC_VER)
return vld4_u8_ex(p, 64);
#else
return vld4_u8(p);
#endif
}
template <bool align>
SIMD_INLINE uint8x16x4_t Load4(const uint8_t* p);
template <>
SIMD_INLINE uint8x16x4_t Load4<false>(const uint8_t* p) {
#if defined(__GNUC__) && SIMD_NEON_PREFECH_SIZE
__builtin_prefetch(p + SIMD_NEON_PREFECH_SIZE);
#endif
return vld4q_u8(p);
}
template <>
SIMD_INLINE uint8x16x4_t Load4<true>(const uint8_t* p) {
#if defined(__GNUC__)
#if SIMD_NEON_PREFECH_SIZE
__builtin_prefetch(p + SIMD_NEON_PREFECH_SIZE);
#endif
uint8_t* _p = static_cast<uint8_t*>(__builtin_assume_aligned(p, 16));
return vld4q_u8(_p);
#elif defined(_MSC_VER)
return vld4q_u8_ex(p, 128);
#else
return vld4q_u8(p);
#endif
}
// SimdStore.h
template <bool align>
SIMD_INLINE void Store4(uint8_t* p, uint8x16x4_t a);
template <>
SIMD_INLINE void Store4<false>(uint8_t* p, uint8x16x4_t a) {
vst4q_u8(p, a);
}
template <>
SIMD_INLINE void Store4<true>(uint8_t* p, uint8x16x4_t a) {
#if defined(__GNUC__)
uint8_t* _p = static_cast<uint8_t*>(__builtin_assume_aligned(p, 16));
vst4q_u8(_p, a);
#elif defined(_MSC_VER)
vst4q_u8_ex(p, a, 128);
#else
vst4q_u8(p, a);
#endif
}
template <bool align>
SIMD_INLINE void Store4(uint8_t* p, uint8x8x4_t a);
template <>
SIMD_INLINE void Store4<false>(uint8_t* p, uint8x8x4_t a) {
vst4_u8(p, a);
}
template <>
SIMD_INLINE void Store4<true>(uint8_t* p, uint8x8x4_t a) {
#if defined(__GNUC__)
uint8_t* _p = static_cast<uint8_t*>(__builtin_assume_aligned(p, 8));
vst4_u8(_p, a);
#elif defined(_MSC_VER)
vst4_u8_ex(p, a, 64);
#else
vst4_u8(p, a);
#endif
}
} // namespace Simd::Neon

View File

@@ -15,6 +15,19 @@ using namespace wpi::java;
static_assert(sizeof(jbyte) * 4 == sizeof(HAL_AddressableLEDData));
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_RGB ==
HAL_ALED_RGB);
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_RBG ==
HAL_ALED_RBG);
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_BGR ==
HAL_ALED_BGR);
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_BRG ==
HAL_ALED_BRG);
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_GBR ==
HAL_ALED_GBR);
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_GRB ==
HAL_ALED_GRB);
extern "C" {
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
@@ -46,6 +59,22 @@ Java_edu_wpi_first_hal_AddressableLEDJNI_free
}
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setColorOrder
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_setColorOrder
(JNIEnv* env, jclass, jint handle, jint colorOrder)
{
int32_t status = 0;
HAL_SetAddressableLEDColorOrder(
static_cast<HAL_AddressableLEDHandle>(handle),
static_cast<HAL_AddressableLEDColorOrder>(colorOrder), &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setLength

View File

@@ -55,12 +55,12 @@ Java_edu_wpi_first_hal_SPIJNI_spiInitialize
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiTransaction
* Signature: (ILjava/lang/Object;Ljava/lang/Object;B)I
* Signature: (ILjava/lang/Object;Ljava/lang/Object;I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiTransaction
(JNIEnv* env, jclass, jint port, jobject dataToSend, jobject dataReceived,
jbyte size)
jint size)
{
uint8_t* dataToSendPtr = nullptr;
if (dataToSend != nullptr) {
@@ -77,12 +77,12 @@ Java_edu_wpi_first_hal_SPIJNI_spiTransaction
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiTransactionB
* Signature: (I[B[BB)I
* Signature: (I[B[BI)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiTransactionB
(JNIEnv* env, jclass, jint port, jbyteArray dataToSend,
jbyteArray dataReceived, jbyte size)
jbyteArray dataReceived, jint size)
{
if (size < 0) {
ThrowIllegalArgumentException(env, "SPIJNI.spiTransactionB() size < 0");
@@ -104,11 +104,11 @@ Java_edu_wpi_first_hal_SPIJNI_spiTransactionB
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiWrite
* Signature: (ILjava/lang/Object;B)I
* Signature: (ILjava/lang/Object;I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiWrite
(JNIEnv* env, jclass, jint port, jobject dataToSend, jbyte size)
(JNIEnv* env, jclass, jint port, jobject dataToSend, jint size)
{
uint8_t* dataToSendPtr = nullptr;
if (dataToSend != nullptr) {
@@ -123,11 +123,11 @@ Java_edu_wpi_first_hal_SPIJNI_spiWrite
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiWriteB
* Signature: (I[BB)I
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiWriteB
(JNIEnv* env, jclass, jint port, jbyteArray dataToSend, jbyte size)
(JNIEnv* env, jclass, jint port, jbyteArray dataToSend, jint size)
{
jint retVal = HAL_WriteSPI(static_cast<HAL_SPIPort>(port),
reinterpret_cast<const uint8_t*>(
@@ -139,12 +139,12 @@ Java_edu_wpi_first_hal_SPIJNI_spiWriteB
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiRead
* Signature: (IZLjava/lang/Object;B)I
* Signature: (IZLjava/lang/Object;I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiRead
(JNIEnv* env, jclass, jint port, jboolean initiate, jobject dataReceived,
jbyte size)
jint size)
{
if (size < 0) {
ThrowIllegalArgumentException(env, "SPIJNI.spiRead() size < 0");
@@ -169,12 +169,12 @@ Java_edu_wpi_first_hal_SPIJNI_spiRead
/*
* Class: edu_wpi_first_hal_SPIJNI
* Method: spiReadB
* Signature: (IZ[BB)I
* Signature: (IZ[BI)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_SPIJNI_spiReadB
(JNIEnv* env, jclass, jint port, jboolean initiate, jbyteArray dataReceived,
jbyte size)
jint size)
{
if (size < 0) {
ThrowIllegalArgumentException(env, "SPIJNI.spiReadB() size < 0");

View File

@@ -36,6 +36,17 @@ HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
*/
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle);
/**
* Sets the color order for the addressable LED output. The default order is
* GRB. This will take effect on the next call to HAL_WriteAddressableLEDData().
* @param[in] handle the Addressable LED handle
* @param[in] colorOrder the color order
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status);
/**
* Set the Addressable LED PWM Digital port.
*
@@ -77,8 +88,8 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
/**
* Sets the bit timing.
*
* <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to
* be set for those.
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing
* needs to be set for those.
*
* @param[in] handle the Addressable LED handle
* @param[in] highTime0NanoSeconds high time for 0 bit (default 400ns)
@@ -98,7 +109,7 @@ void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
* Sets the sync time.
*
* <p>The sync time is the time to hold output so LEDs enable. Default set for
* WS2812B.
* WS2812B and WS2815.
*
* @param[in] handle the Addressable LED handle
* @param[in] syncTimeMicroSeconds the sync time (default 280us)

View File

@@ -4,6 +4,7 @@
#pragma once
#include <hal/Types.h>
#include <stdint.h>
/** max length of LED strip supported by FPGA. */
@@ -16,3 +17,21 @@ struct HAL_AddressableLEDData {
uint8_t r; ///< red value
uint8_t padding;
};
/**
* Order that color data is sent over the wire.
*/
HAL_ENUM(HAL_AddressableLEDColorOrder) {
HAL_ALED_RGB,
HAL_ALED_RBG,
HAL_ALED_BGR,
HAL_ALED_BRG,
HAL_ALED_GBR,
HAL_ALED_GRB
};
#ifdef __cplusplus
constexpr auto format_as(HAL_AddressableLEDColorOrder order) {
return static_cast<int32_t>(order);
}
#endif

View File

@@ -91,6 +91,10 @@ void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
SimAddressableLEDData[led->index].initialized = false;
}
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status) {}
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
HAL_DigitalHandle outputPort,
int32_t* status) {

View File

@@ -0,0 +1,10 @@
build:systemcore --config=base_linux
build:systemcore --platforms=@rules_bzlmodrio_toolchains//platforms/systemcore
build:systemcore --build_tag_filters=-no-bookworm
build:systemcore --features=compiler_param_file
build:systemcore --platform_suffix=systemcore
build:systemcore --incompatible_enable_cc_toolchain_resolution
build:systemcore --cxxopt=-Wno-error=deprecated-declarations

View File

@@ -4,7 +4,6 @@ nativeUtils {
groupId = "edu.wpi.first.thirdparty.frc2024.ceres"
artifactId = "ceres-cpp"
headerClassifier = "headers"
sourceClassifier = "sources"
ext = "zip"
version = '2.2-3'
targetPlatforms.addAll(nativeUtils.wpi.platforms.desktopPlatforms)

View File

@@ -106,8 +106,10 @@ void Application(std::string_view saveDir) {
auto analyzer = std::make_unique<sysid::Analyzer>(storage, gLogger);
logLoader->unload.connect([ds = dataSelector.get()] { ds->Reset(); });
dataSelector->testdata = [_analyzer = analyzer.get()](auto data) {
dataSelector->testdata = [_analyzer = analyzer.get(),
ds = dataSelector.get()](auto data) {
_analyzer->m_data = data;
_analyzer->SetMissingTests(ds->m_missingTests);
_analyzer->AnalyzeData();
};

View File

@@ -8,6 +8,7 @@
#include <frc/controller/LinearQuadraticRegulator.h>
#include <frc/system/LinearSystem.h>
#include <frc/system/plant/LinearSystemId.h>
#include <units/acceleration.h>
#include <units/velocity.h>
#include <units/voltage.h>
@@ -18,6 +19,7 @@ using namespace sysid;
using Kv_t = decltype(1_V / 1_mps);
using Ka_t = decltype(1_V / 1_mps_sq);
using Matrix1d = Eigen::Matrix<double, 1, 1>;
FeedbackGains sysid::CalculatePositionFeedbackGains(
const FeedbackControllerPreset& preset, const LQRParameters& params,
@@ -26,39 +28,32 @@ FeedbackGains sysid::CalculatePositionFeedbackGains(
return {0.0, 0.0};
}
// If acceleration requires no effort, velocity becomes an input for position
// control. We choose an appropriate model in this case to avoid numerical
// If acceleration for position control requires no effort, velocity becomes
// an input. We choose an appropriate model in this case to avoid numerical
// instabilities in the LQR.
if (Ka > 1E-7) {
// Create a position system from our feedforward gains.
frc::LinearSystem<2, 1, 1> system{
frc::Matrixd<2, 2>{{0.0, 1.0}, {0.0, -Kv / Ka}},
frc::Matrixd<2, 1>{0.0, 1.0 / Ka}, frc::Matrixd<1, 2>{1.0, 0.0},
frc::Matrixd<1, 1>{0.0}};
// Create an LQR with 2 states to control -- position and velocity.
frc::LinearQuadraticRegulator<2, 1> controller{
system, {params.qp, params.qv}, {params.r}, preset.period};
// Compensate for any latency from sensor measurements, filtering, etc.
if (std::abs(Ka) < 1e-7) {
// System has position state and velocity input
frc::LinearSystem<1, 1, 1> system{Matrix1d{0.0}, Matrix1d{1.0},
Matrix1d{1.0}, Matrix1d{0.0}};
frc::LinearQuadraticRegulator<1, 1> controller{
system, {params.qp}, {params.r}, preset.period};
controller.LatencyCompensate(system, preset.period,
preset.measurementDelay);
return {
controller.K(0, 0) * preset.outputConversionFactor,
controller.K(0, 1) * preset.outputConversionFactor /
(preset.normalized ? 1 : units::second_t{preset.period}.value())};
return {Kv * controller.K(0, 0) * preset.outputConversionFactor, 0.0};
}
// This is our special model to avoid instabilities in the LQR.
auto system = frc::LinearSystem<1, 1, 1>(
Eigen::Matrix<double, 1, 1>{0.0}, Eigen::Matrix<double, 1, 1>{1.0},
Eigen::Matrix<double, 1, 1>{1.0}, Eigen::Matrix<double, 1, 1>{0.0});
// Create an LQR with one state -- position.
frc::LinearQuadraticRegulator<1, 1> controller{
system, {params.qp}, {params.r}, preset.period};
// Compensate for any latency from sensor measurements, filtering, etc.
auto system = frc::LinearSystemId::IdentifyPositionSystem<units::meters>(
Kv_t{Kv}, Ka_t{Ka});
frc::LinearQuadraticRegulator<2, 1> controller{
system, {params.qp, params.qv}, {params.r}, preset.period};
controller.LatencyCompensate(system, preset.period, preset.measurementDelay);
return {Kv * controller.K(0, 0) * preset.outputConversionFactor, 0.0};
return {controller.K(0, 0) * preset.outputConversionFactor,
controller.K(0, 1) * preset.outputConversionFactor /
(preset.normalized ? 1 : units::second_t{preset.period}.value())};
}
FeedbackGains sysid::CalculateVelocityFeedbackGains(
@@ -69,20 +64,16 @@ FeedbackGains sysid::CalculateVelocityFeedbackGains(
}
// If acceleration for velocity control requires no effort, the feedback
// control gains approach zero. We special-case it here because numerical
// instabilities arise in LQR otherwise.
if (Ka < 1E-7) {
// control gains approach zero. We special-case it here to avoid numerical
// instabilities in LQR.
if (std::abs(Ka) < 1E-7) {
return {0.0, 0.0};
}
// Create a velocity system from our feedforward gains.
frc::LinearSystem<1, 1, 1> system{
frc::Matrixd<1, 1>{-Kv / Ka}, frc::Matrixd<1, 1>{1.0 / Ka},
frc::Matrixd<1, 1>{1.0}, frc::Matrixd<1, 1>{0.0}};
// Create an LQR controller with 1 state -- velocity.
auto system = frc::LinearSystemId::IdentifyVelocitySystem<units::meters>(
Kv_t{Kv}, Ka_t{Ka});
frc::LinearQuadraticRegulator<1, 1> controller{
system, {params.qv}, {params.r}, preset.period};
// Compensate for any latency from sensor measurements, filtering, etc.
controller.LatencyCompensate(system, preset.period, preset.measurementDelay);
return {controller.K(0, 0) * preset.outputConversionFactor /

View File

@@ -10,6 +10,7 @@
#include <numbers>
#include <string>
#include <thread>
#include <vector>
#include <fmt/format.h>
#include <glass/Context.h>
@@ -251,6 +252,13 @@ void Analyzer::Display() {
}
break;
}
case AnalyzerState::kMissingTestsError: {
CreateErrorPopup(m_errorPopup, m_exception);
if (!m_errorPopup) {
m_state = AnalyzerState::kWaitingForData;
}
break;
}
case AnalyzerState::kGeneralDataError:
case AnalyzerState::kTestDurationError:
case AnalyzerState::kVelocityThresholdError: {
@@ -269,6 +277,9 @@ void Analyzer::Display() {
void Analyzer::PrepareData() {
WPI_INFO(m_logger, "{}", "Preparing data");
try {
if (m_missingTests.size() > 0) {
throw sysid::MissingTestsError{m_missingTests};
}
m_manager->PrepareData();
UpdateFeedforwardGains();
UpdateFeedbackGains();
@@ -281,6 +292,9 @@ void Analyzer::PrepareData() {
} catch (const sysid::NoDynamicDataError& e) {
m_state = AnalyzerState::kTestDurationError;
HandleError(e.what());
} catch (const sysid::MissingTestsError& e) {
m_state = AnalyzerState::kMissingTestsError;
HandleError(e.what());
} catch (const AnalysisManager::FileReadingError& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
@@ -324,6 +338,10 @@ void Analyzer::HandleError(std::string_view msg) {
PrepareRawGraphs();
}
void Analyzer::SetMissingTests(const std::vector<std::string>& missingTests) {
m_missingTests = missingTests;
}
void Analyzer::DisplayGraphs() {
ImGui::SetNextWindowPos(ImVec2{kDiagnosticPlotWindowPos},
ImGuiCond_FirstUseEver);

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@@ -111,6 +112,7 @@ void DataSelector::Display() {
continue;
}
WPI_INFO(m_logger, "Loaded test state {}", it2->first);
m_executedTests.insert(it2->first);
++it2;
}
if (it->second.empty()) {
@@ -132,6 +134,15 @@ void DataSelector::Display() {
return;
}
if (m_executedTests.size() < 4 && !m_testCountValidated) {
for (auto test : kValidTests) {
if (!m_executedTests.contains(test)) {
m_missingTests.push_back(test);
m_testCountValidated = true;
}
}
}
#if 0
// Test filtering
if (ImGui::BeginCombo("Test", m_selectedTest.c_str())) {

View File

@@ -4,15 +4,16 @@
#pragma once
#include <cmath>
#include <exception>
#include <functional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <frc/filter/LinearFilter.h>
#include <units/time.h>
#include <wpi/StringMap.h>
@@ -40,7 +41,7 @@ class InvalidDataError : public std::exception {
*/
explicit InvalidDataError(std::string_view message) {
m_message = fmt::format(
"{}. Please verify that your units and data is reasonable and then "
"{} Please verify that your units and data is reasonable and then "
"adjust your velocity threshold, test duration, and/or window size to "
"try to fix this issue.",
message);
@@ -68,6 +69,25 @@ class NoQuasistaticDataError : public std::exception {
}
};
/**
* Exception for not all tests being present.
*/
class MissingTestsError : public std::exception {
public:
explicit MissingTestsError(std::vector<std::string> MissingTests)
: missingTests(std::move(MissingTests)) {
errorString = fmt::format(
"The following tests were not detected: {}. Make sure to perform all "
"four tests as described in the SysId documentation.",
fmt::join(missingTests, ", "));
}
const char* what() const noexcept override { return errorString.c_str(); }
private:
std::vector<std::string> missingTests;
std::string errorString;
};
/**
* Exception for Dynamic Data being completely removed.
*/

View File

@@ -8,6 +8,7 @@
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include <glass/View.h>
#include <implot.h>
@@ -46,6 +47,7 @@ class Analyzer : public glass::View {
kVelocityThresholdError,
kTestDurationError,
kGeneralDataError,
kMissingTestsError,
kFileError
};
/**
@@ -91,6 +93,11 @@ class Analyzer : public glass::View {
*/
void AnalyzeData();
/**
* Used by DataSelector to import any missing tests.
*/
void SetMissingTests(const std::vector<std::string>& missingTests);
private:
/**
* Kills the data preparation thread
@@ -199,6 +206,7 @@ class Analyzer : public glass::View {
// Stores the exception message.
std::string m_exception;
std::vector<std::string> m_missingTests;
bool m_calcDefaults = false;

View File

@@ -7,6 +7,7 @@
#include <functional>
#include <future>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
@@ -55,6 +56,7 @@ class DataSelector : public glass::View {
* Called when new test data is loaded.
*/
std::function<void(TestData)> testdata;
std::vector<std::string> m_missingTests;
private:
wpi::Logger& m_logger;
@@ -74,6 +76,11 @@ class DataSelector : public glass::View {
int m_selectedAnalysis = 0;
std::future<TestData> m_testdataFuture;
std::vector<std::string> m_testdataStats;
std::set<std::string> kValidTests = {"quasistatic-forward",
"quasistatic-reverse", "dynamic-forward",
"dynamic-reverse"};
std::set<std::string> m_executedTests;
bool m_testCountValidated = false;
static Tests LoadTests(const glass::DataLogReaderEntry& testStateEntry);
TestData BuildTestData();

View File

@@ -7,7 +7,7 @@
#include "sysid/analysis/FeedbackAnalysis.h"
#include "sysid/analysis/FeedbackControllerPreset.h"
TEST(FeedbackAnalysisTest, Velocity1) {
TEST(FeedbackAnalysisTest, VelocitySystem1) {
auto Kv = 3.060;
auto Ka = 0.327;
@@ -20,7 +20,7 @@ TEST(FeedbackAnalysisTest, Velocity1) {
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, Velocity2) {
TEST(FeedbackAnalysisTest, VelocitySystem2) {
auto Kv = 0.0693;
auto Ka = 0.1170;
@@ -33,6 +33,19 @@ TEST(FeedbackAnalysisTest, Velocity2) {
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocitySystemWithSmallKa) {
auto Kv = 3.060;
auto Ka = 0.0;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 0.00, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityConversion) {
auto Kv = 0.0693;
auto Ka = 0.1170;
@@ -117,6 +130,19 @@ TEST(FeedbackAnalysisTest, Position) {
EXPECT_NEAR(Kd, 2.48, 0.05);
}
TEST(FeedbackAnalysisTest, PositionWithSmallKa) {
auto Kv = 3.060;
auto Ka = 1e-10;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 19.97, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, PositionWithLatencyCompensation) {
auto Kv = 3.060;
auto Ka = 0.327;

View File

@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Wed, 18 May 2022 09:14:24 -0700
Subject: [PATCH 1/2] Disable warnings
Subject: [PATCH 1/3] Disable warnings
---
Eigen/src/Core/util/DisableStupidWarnings.h | 6 ++++++

View File

@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 20 Jan 2023 23:41:56 -0800
Subject: [PATCH 2/2] Intellisense fix
Subject: [PATCH 2/3] Intellisense fix
---
Eigen/src/Core/util/ConfigureVectorization.h | 7 +++++++

View File

@@ -0,0 +1,305 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Sun, 12 Jan 2025 21:04:07 -0800
Subject: [PATCH 3/3] Make assignment constexpr
---
Eigen/src/Core/AssignEvaluator.h | 165 +++++++++++--------
Eigen/src/Core/EigenBase.h | 2 +-
Eigen/src/Core/functors/AssignmentFunctors.h | 2 +-
3 files changed, 102 insertions(+), 67 deletions(-)
diff --git a/Eigen/src/Core/AssignEvaluator.h b/Eigen/src/Core/AssignEvaluator.h
index f7f0b238b8ca70bbc9100262479cc1dbebab9979..9c2436afa7fe98692a036e6ef255ed104a5bf388 100644
--- a/Eigen/src/Core/AssignEvaluator.h
+++ b/Eigen/src/Core/AssignEvaluator.h
@@ -263,7 +263,7 @@ struct copy_using_evaluator_innervec_CompleteUnrolling {
DstAlignment = Kernel::AssignmentTraits::DstAlignment
};
- EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void run(Kernel& kernel) {
+ EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE constexpr void run(Kernel& kernel) {
kernel.template assignPacketByOuterInner<DstAlignment, SrcAlignment, PacketType>(outer, inner);
enum { NextIndex = Index + unpacket_traits<PacketType>::size };
copy_using_evaluator_innervec_CompleteUnrolling<Kernel, NextIndex, Stop>::run(kernel);
@@ -431,17 +431,25 @@ struct dense_assignment_loop<Kernel, LinearVectorizedTraversal, NoUnrolling> {
template <typename Kernel>
struct dense_assignment_loop<Kernel, LinearVectorizedTraversal, CompleteUnrolling> {
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE EIGEN_CONSTEXPR void run(Kernel& kernel) {
- typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
- typedef typename Kernel::PacketType PacketType;
-
- enum {
- size = DstXprType::SizeAtCompileTime,
- packetSize = unpacket_traits<PacketType>::size,
- alignedSize = (int(size) / packetSize) * packetSize
- };
-
- copy_using_evaluator_linearvec_CompleteUnrolling<Kernel, 0, alignedSize>::run(kernel);
- copy_using_evaluator_LinearTraversal_CompleteUnrolling<Kernel, alignedSize, size>::run(kernel);
+ if (internal::is_constant_evaluated()) {
+ for (Index outer = 0; outer < kernel.outerSize(); ++outer) {
+ for (Index inner = 0; inner < kernel.innerSize(); ++inner) {
+ kernel.assignCoeffByOuterInner(outer, inner);
+ }
+ }
+ } else {
+ typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
+ typedef typename Kernel::PacketType PacketType;
+
+ enum {
+ size = DstXprType::SizeAtCompileTime,
+ packetSize = unpacket_traits<PacketType>::size,
+ alignedSize = (int(size) / packetSize) * packetSize
+ };
+
+ copy_using_evaluator_linearvec_CompleteUnrolling<Kernel, 0, alignedSize>::run(kernel);
+ copy_using_evaluator_LinearTraversal_CompleteUnrolling<Kernel, alignedSize, size>::run(kernel);
+ }
}
};
@@ -465,9 +473,17 @@ struct dense_assignment_loop<Kernel, InnerVectorizedTraversal, NoUnrolling> {
template <typename Kernel>
struct dense_assignment_loop<Kernel, InnerVectorizedTraversal, CompleteUnrolling> {
- EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void run(Kernel& kernel) {
- typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
- copy_using_evaluator_innervec_CompleteUnrolling<Kernel, 0, DstXprType::SizeAtCompileTime>::run(kernel);
+ EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE constexpr void run(Kernel& kernel) {
+ if (internal::is_constant_evaluated()) {
+ for (Index outer = 0; outer < kernel.outerSize(); ++outer) {
+ for (Index inner = 0; inner < kernel.innerSize(); ++inner) {
+ kernel.assignCoeffByOuterInner(outer, inner);
+ }
+ }
+ } else {
+ typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
+ copy_using_evaluator_innervec_CompleteUnrolling<Kernel, 0, DstXprType::SizeAtCompileTime>::run(kernel);
+ }
}
};
@@ -498,8 +514,16 @@ struct dense_assignment_loop<Kernel, LinearTraversal, NoUnrolling> {
template <typename Kernel>
struct dense_assignment_loop<Kernel, LinearTraversal, CompleteUnrolling> {
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE EIGEN_CONSTEXPR void run(Kernel& kernel) {
- typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
- copy_using_evaluator_LinearTraversal_CompleteUnrolling<Kernel, 0, DstXprType::SizeAtCompileTime>::run(kernel);
+ if (internal::is_constant_evaluated()) {
+ for (Index outer = 0; outer < kernel.outerSize(); ++outer) {
+ for (Index inner = 0; inner < kernel.innerSize(); ++inner) {
+ kernel.assignCoeffByOuterInner(outer, inner);
+ }
+ }
+ } else {
+ typedef typename Kernel::DstEvaluatorType::XprType DstXprType;
+ copy_using_evaluator_LinearTraversal_CompleteUnrolling<Kernel, 0, DstXprType::SizeAtCompileTime>::run(kernel);
+ }
}
};
@@ -510,41 +534,49 @@ struct dense_assignment_loop<Kernel, LinearTraversal, CompleteUnrolling> {
template <typename Kernel>
struct dense_assignment_loop<Kernel, SliceVectorizedTraversal, NoUnrolling> {
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE EIGEN_CONSTEXPR void run(Kernel& kernel) {
- typedef typename Kernel::Scalar Scalar;
- typedef typename Kernel::PacketType PacketType;
- enum {
- packetSize = unpacket_traits<PacketType>::size,
- requestedAlignment = int(Kernel::AssignmentTraits::InnerRequiredAlignment),
- alignable =
- packet_traits<Scalar>::AlignedOnScalar || int(Kernel::AssignmentTraits::DstAlignment) >= sizeof(Scalar),
- dstIsAligned = int(Kernel::AssignmentTraits::DstAlignment) >= int(requestedAlignment),
- dstAlignment = alignable ? int(requestedAlignment) : int(Kernel::AssignmentTraits::DstAlignment)
- };
- const Scalar* dst_ptr = kernel.dstDataPtr();
- if ((!bool(dstIsAligned)) && (std::uintptr_t(dst_ptr) % sizeof(Scalar)) > 0) {
- // the pointer is not aligned-on scalar, so alignment is not possible
- return dense_assignment_loop<Kernel, DefaultTraversal, NoUnrolling>::run(kernel);
- }
- const Index packetAlignedMask = packetSize - 1;
- const Index innerSize = kernel.innerSize();
- const Index outerSize = kernel.outerSize();
- const Index alignedStep = alignable ? (packetSize - kernel.outerStride() % packetSize) & packetAlignedMask : 0;
- Index alignedStart =
- ((!alignable) || bool(dstIsAligned)) ? 0 : internal::first_aligned<requestedAlignment>(dst_ptr, innerSize);
-
- for (Index outer = 0; outer < outerSize; ++outer) {
- const Index alignedEnd = alignedStart + ((innerSize - alignedStart) & ~packetAlignedMask);
- // do the non-vectorizable part of the assignment
- for (Index inner = 0; inner < alignedStart; ++inner) kernel.assignCoeffByOuterInner(outer, inner);
-
- // do the vectorizable part of the assignment
- for (Index inner = alignedStart; inner < alignedEnd; inner += packetSize)
- kernel.template assignPacketByOuterInner<dstAlignment, Unaligned, PacketType>(outer, inner);
-
- // do the non-vectorizable part of the assignment
- for (Index inner = alignedEnd; inner < innerSize; ++inner) kernel.assignCoeffByOuterInner(outer, inner);
-
- alignedStart = numext::mini((alignedStart + alignedStep) % packetSize, innerSize);
+ if (internal::is_constant_evaluated()) {
+ for (Index outer = 0; outer < kernel.outerSize(); ++outer) {
+ for (Index inner = 0; inner < kernel.innerSize(); ++inner) {
+ kernel.assignCoeffByOuterInner(outer, inner);
+ }
+ }
+ } else {
+ typedef typename Kernel::Scalar Scalar;
+ typedef typename Kernel::PacketType PacketType;
+ enum {
+ packetSize = unpacket_traits<PacketType>::size,
+ requestedAlignment = int(Kernel::AssignmentTraits::InnerRequiredAlignment),
+ alignable =
+ packet_traits<Scalar>::AlignedOnScalar || int(Kernel::AssignmentTraits::DstAlignment) >= sizeof(Scalar),
+ dstIsAligned = int(Kernel::AssignmentTraits::DstAlignment) >= int(requestedAlignment),
+ dstAlignment = alignable ? int(requestedAlignment) : int(Kernel::AssignmentTraits::DstAlignment)
+ };
+ const Scalar* dst_ptr = kernel.dstDataPtr();
+ if ((!bool(dstIsAligned)) && (std::uintptr_t(dst_ptr) % sizeof(Scalar)) > 0) {
+ // the pointer is not aligned-on scalar, so alignment is not possible
+ return dense_assignment_loop<Kernel, DefaultTraversal, NoUnrolling>::run(kernel);
+ }
+ const Index packetAlignedMask = packetSize - 1;
+ const Index innerSize = kernel.innerSize();
+ const Index outerSize = kernel.outerSize();
+ const Index alignedStep = alignable ? (packetSize - kernel.outerStride() % packetSize) & packetAlignedMask : 0;
+ Index alignedStart =
+ ((!alignable) || bool(dstIsAligned)) ? 0 : internal::first_aligned<requestedAlignment>(dst_ptr, innerSize);
+
+ for (Index outer = 0; outer < outerSize; ++outer) {
+ const Index alignedEnd = alignedStart + ((innerSize - alignedStart) & ~packetAlignedMask);
+ // do the non-vectorizable part of the assignment
+ for (Index inner = 0; inner < alignedStart; ++inner) kernel.assignCoeffByOuterInner(outer, inner);
+
+ // do the vectorizable part of the assignment
+ for (Index inner = alignedStart; inner < alignedEnd; inner += packetSize)
+ kernel.template assignPacketByOuterInner<dstAlignment, Unaligned, PacketType>(outer, inner);
+
+ // do the non-vectorizable part of the assignment
+ for (Index inner = alignedEnd; inner < innerSize; ++inner) kernel.assignCoeffByOuterInner(outer, inner);
+
+ alignedStart = numext::mini((alignedStart + alignedStep) % packetSize, innerSize);
+ }
}
}
};
@@ -594,9 +626,9 @@ class generic_dense_assignment_kernel {
typedef copy_using_evaluator_traits<DstEvaluatorTypeT, SrcEvaluatorTypeT, Functor> AssignmentTraits;
typedef typename AssignmentTraits::PacketType PacketType;
- EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE generic_dense_assignment_kernel(DstEvaluatorType& dst,
- const SrcEvaluatorType& src,
- const Functor& func, DstXprType& dstExpr)
+ EIGEN_DEVICE_FUNC
+ EIGEN_STRONG_INLINE constexpr generic_dense_assignment_kernel(DstEvaluatorType& dst, const SrcEvaluatorType& src,
+ const Functor& func, DstXprType& dstExpr)
: m_dst(dst), m_src(src), m_functor(func), m_dstExpr(dstExpr) {
#ifdef EIGEN_DEBUG_ASSIGN
AssignmentTraits::debug();
@@ -614,7 +646,7 @@ class generic_dense_assignment_kernel {
EIGEN_DEVICE_FUNC const SrcEvaluatorType& srcEvaluator() const EIGEN_NOEXCEPT { return m_src; }
/// Assign src(row,col) to dst(row,col) through the assignment functor.
- EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void assignCoeff(Index row, Index col) {
+ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void assignCoeff(Index row, Index col) {
m_functor.assignCoeff(m_dst.coeffRef(row, col), m_src.coeff(row, col));
}
@@ -624,7 +656,7 @@ class generic_dense_assignment_kernel {
}
/// \sa assignCoeff(Index,Index)
- EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void assignCoeffByOuterInner(Index outer, Index inner) {
+ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void assignCoeffByOuterInner(Index outer, Index inner) {
Index row = rowIndexByOuterInner(outer, inner);
Index col = colIndexByOuterInner(outer, inner);
assignCoeff(row, col);
@@ -648,7 +680,7 @@ class generic_dense_assignment_kernel {
assignPacket<StoreMode, LoadMode, Packet>(row, col);
}
- EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Index rowIndexByOuterInner(Index outer, Index inner) {
+ EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE constexpr Index rowIndexByOuterInner(Index outer, Index inner) {
typedef typename DstEvaluatorType::ExpressionTraits Traits;
return int(Traits::RowsAtCompileTime) == 1 ? 0
: int(Traits::ColsAtCompileTime) == 1 ? inner
@@ -656,7 +688,7 @@ class generic_dense_assignment_kernel {
: inner;
}
- EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Index colIndexByOuterInner(Index outer, Index inner) {
+ EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE constexpr Index colIndexByOuterInner(Index outer, Index inner) {
typedef typename DstEvaluatorType::ExpressionTraits Traits;
return int(Traits::ColsAtCompileTime) == 1 ? 0
: int(Traits::RowsAtCompileTime) == 1 ? inner
@@ -708,8 +740,8 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize_if_allowed(DstXprType& dst, co
}
template <typename DstXprType, typename SrcXprType, typename T1, typename T2>
-EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize_if_allowed(DstXprType& dst, const SrcXprType& src,
- const internal::assign_op<T1, T2>& /*func*/) {
+EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void resize_if_allowed(DstXprType& dst, const SrcXprType& src,
+ const internal::assign_op<T1, T2>& /*func*/) {
Index dstRows = src.rows();
Index dstCols = src.cols();
if (((dst.rows() != dstRows) || (dst.cols() != dstCols))) dst.resize(dstRows, dstCols);
@@ -790,7 +822,7 @@ struct Assignment;
// not has to bother about these annoying details.
template <typename Dst, typename Src>
-EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void call_assignment(Dst& dst, const Src& src) {
+EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void call_assignment(Dst& dst, const Src& src) {
call_assignment(dst, src, internal::assign_op<typename Dst::Scalar, typename Src::Scalar>());
}
template <typename Dst, typename Src>
@@ -807,7 +839,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE EIGEN_CONSTEXPR void call_assignment(
}
template <typename Dst, typename Src, typename Func>
-EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void call_assignment(
+EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void call_assignment(
Dst& dst, const Src& src, const Func& func, std::enable_if_t<!evaluator_assume_aliasing<Src>::value, void*> = 0) {
call_assignment_no_alias(dst, src, func);
}
@@ -891,9 +923,12 @@ EIGEN_DEVICE_FUNC void check_for_aliasing(const Dst& dst, const Src& src);
// both partial specialization+SFINAE without ambiguous specialization
template <typename DstXprType, typename SrcXprType, typename Functor, typename Weak>
struct Assignment<DstXprType, SrcXprType, Functor, Dense2Dense, Weak> {
- EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src, const Functor& func) {
+ EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE constexpr void run(DstXprType& dst, const SrcXprType& src,
+ const Functor& func) {
#ifndef EIGEN_NO_DEBUG
- internal::check_for_aliasing(dst, src);
+ if (!internal::is_constant_evaluated()) {
+ internal::check_for_aliasing(dst, src);
+ }
#endif
call_dense_assignment_loop(dst, src, func);
diff --git a/Eigen/src/Core/EigenBase.h b/Eigen/src/Core/EigenBase.h
index 6d167006a094181fa3693b19f6b9daeb6f2afb79..894bfc13b15eb994abd90f100da15de5bd8b22b7 100644
--- a/Eigen/src/Core/EigenBase.h
+++ b/Eigen/src/Core/EigenBase.h
@@ -50,7 +50,7 @@ struct EigenBase {
/** \returns a const reference to the derived object */
EIGEN_DEVICE_FUNC constexpr const Derived& derived() const { return *static_cast<const Derived*>(this); }
- EIGEN_DEVICE_FUNC inline Derived& const_cast_derived() const {
+ EIGEN_DEVICE_FUNC inline constexpr Derived& const_cast_derived() const {
return *static_cast<Derived*>(const_cast<EigenBase*>(this));
}
EIGEN_DEVICE_FUNC inline const Derived& const_derived() const { return *static_cast<const Derived*>(this); }
diff --git a/Eigen/src/Core/functors/AssignmentFunctors.h b/Eigen/src/Core/functors/AssignmentFunctors.h
index 09d1da8ca2bcb41384520f46e2b793ba8b28a798..3687bb20db4dfe1a2f6cf1342b4fcbd8f91f1f68 100644
--- a/Eigen/src/Core/functors/AssignmentFunctors.h
+++ b/Eigen/src/Core/functors/AssignmentFunctors.h
@@ -23,7 +23,7 @@ namespace internal {
*/
template <typename DstScalar, typename SrcScalar>
struct assign_op {
- EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void assignCoeff(DstScalar& a, const SrcScalar& b) const { a = b; }
+ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void assignCoeff(DstScalar& a, const SrcScalar& b) const { a = b; }
template <int Alignment, typename Packet>
EIGEN_STRONG_INLINE void assignPacket(DstScalar* a, const Packet& b) const {

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
from upstream_utils import Lib
def crlf_to_lf():
for root, _, files in os.walk("."):
if ".git" in root:
continue
for fname in files:
filename = os.path.join(root, fname)
print(f"Converting CRLF -> LF for {filename}")
with open(filename, "rb") as f:
content = f.read()
content = content.replace(b"\r\n", b"\n")
with open(filename, "wb") as f:
f.write(content)
subprocess.check_call(["git", "add", "-A"])
subprocess.check_call(["git", "commit", "-m", "Fix line endings"])
def copy_upstream_src(wpilib_root):
wpiutil = os.path.join(wpilib_root, "wpiutil")
shutil.copy(
os.path.join("Main", "StackWalker", "StackWalker.h"),
os.path.join(wpiutil, "src/main/native/windows/StackWalker.h"),
)
shutil.copy(
os.path.join("Main", "StackWalker", "StackWalker.cpp"),
os.path.join(wpiutil, "src/main/native/windows/StackWalker.cpp"),
)
def main():
name = "stack_walker"
url = "https://github.com/JochenKalmbach/StackWalker"
tag = "5b0df7a4db8896f6b6dc45d36e383c52577e3c6b"
patch_options = {
"ignore_whitespace": True,
}
stack_walker = Lib(
name,
url,
tag,
copy_upstream_src,
patch_options,
pre_patch_hook=crlf_to_lf,
pre_patch_commits=1,
)
stack_walker.main()
if __name__ == "__main__":
main()

View File

@@ -1,21 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Thad House <thadhouse1@gmail.com>
Date: Sat, 22 Jul 2023 13:03:13 -0700
Subject: [PATCH] Add advapi pragma
---
Main/StackWalker/StackWalker.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/Main/StackWalker/StackWalker.cpp b/Main/StackWalker/StackWalker.cpp
index 89545f8612d0d099d48fcf4184a2f2a30cf8577f..b2bcbaa447c5db1a3bcc155fb317ebc8a8050e79 100644
--- a/Main/StackWalker/StackWalker.cpp
+++ b/Main/StackWalker/StackWalker.cpp
@@ -91,6 +91,7 @@
#include <new>
#pragma comment(lib, "version.lib") // for "VerQueryValue"
+#pragma comment(lib, "Advapi32.lib") // for "GetUserName"
#pragma warning(disable : 4826)
#if _MSC_VER >= 1900

View File

@@ -107,7 +107,7 @@ public class Command{{ ConsoleName }}Controller extends CommandGenericHID {
{% endfor -%}
{% for stick in sticks %}
/**
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller.
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller. {{ stick.PositiveDirection }} is positive.
*
* @return The axis value.
*/

View File

@@ -71,7 +71,7 @@ class Command{{ ConsoleName }}Controller : public CommandGenericHID {
{% endfor -%}
{% for stick in sticks %}
/**
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller.
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller. {{ stick.PositiveDirection }} is positive.
*
* @return The axis value.
*/

View File

@@ -348,7 +348,7 @@ public class CommandPS4Controller extends CommandGenericHID {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -357,7 +357,7 @@ public class CommandPS4Controller extends CommandGenericHID {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -366,7 +366,7 @@ public class CommandPS4Controller extends CommandGenericHID {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -375,7 +375,7 @@ public class CommandPS4Controller extends CommandGenericHID {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -348,7 +348,7 @@ public class CommandPS5Controller extends CommandGenericHID {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -357,7 +357,7 @@ public class CommandPS5Controller extends CommandGenericHID {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -366,7 +366,7 @@ public class CommandPS5Controller extends CommandGenericHID {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -375,7 +375,7 @@ public class CommandPS5Controller extends CommandGenericHID {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -370,7 +370,7 @@ public class CommandStadiaController extends CommandGenericHID {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -379,7 +379,7 @@ public class CommandStadiaController extends CommandGenericHID {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -388,7 +388,7 @@ public class CommandStadiaController extends CommandGenericHID {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -397,7 +397,7 @@ public class CommandStadiaController extends CommandGenericHID {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -338,7 +338,7 @@ public class CommandXboxController extends CommandGenericHID {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -347,7 +347,7 @@ public class CommandXboxController extends CommandGenericHID {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -356,7 +356,7 @@ public class CommandXboxController extends CommandGenericHID {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -365,7 +365,7 @@ public class CommandXboxController extends CommandGenericHID {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -204,28 +204,28 @@ class CommandPS4Controller : public CommandGenericHID {
.GetDefaultButtonLoop()) const;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetLeftX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
double GetLeftY() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -204,28 +204,28 @@ class CommandPS5Controller : public CommandGenericHID {
.GetDefaultButtonLoop()) const;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetLeftX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
double GetLeftY() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -216,28 +216,28 @@ class CommandStadiaController : public CommandGenericHID {
.GetDefaultButtonLoop()) const;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetLeftX() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
double GetLeftY() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -190,28 +190,28 @@ class CommandXboxController : public CommandGenericHID {
.GetDefaultButtonLoop()) const;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetLeftX() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
double GetLeftY() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -173,6 +173,9 @@ public class CommandJoystick extends CommandGenericHID {
/**
* Get the x position of the HID.
*
* <p>This depends on the mapping of the joystick connected to the current port. On most
* joysticks, positive is to the right.
*
* @return the x position
*/
public double getX() {
@@ -182,6 +185,9 @@ public class CommandJoystick extends CommandGenericHID {
/**
* Get the y position of the HID.
*
* <p>This depends on the mapping of the joystick connected to the current port. On most
* joysticks, positive is to the back.
*
* @return the y position
*/
public double getY() {
@@ -218,8 +224,8 @@ public class CommandJoystick extends CommandGenericHID {
}
/**
* Get the magnitude of the direction vector formed by the joystick's current position relative to
* its origin.
* Get the magnitude of the vector formed by the joystick's current position relative to its
* origin.
*
* @return The magnitude of the direction vector
*/
@@ -228,16 +234,26 @@ public class CommandJoystick extends CommandGenericHID {
}
/**
* Get the direction of the vector formed by the joystick and its origin in radians.
* Get the direction of the vector formed by the joystick and its origin in radians. 0 is forward
* and clockwise is positive. (Straight right is π/2.)
*
* @return The direction of the vector in radians
*/
public double getDirectionRadians() {
// https://docs.wpilib.org/en/stable/docs/software/basic-programming/coordinate-system.html#joystick-and-controller-coordinate-system
// A positive rotation around the X axis moves the joystick right, and a
// positive rotation around the Y axis moves the joystick backward. When
// treating them as translations, 0 radians is measured from the right
// direction, and angle increases clockwise.
//
// It's rotated 90 degrees CCW (y is negated and the arguments are reversed)
// so that 0 radians is forward.
return m_hid.getDirectionRadians();
}
/**
* Get the direction of the vector formed by the joystick and its origin in degrees.
* Get the direction of the vector formed by the joystick and its origin in degrees. 0 is forward
* and clockwise is positive. (Straight right is 90.)
*
* @return The direction of the vector in degrees
*/

View File

@@ -26,5 +26,13 @@ double CommandJoystick::GetMagnitude() const {
}
units::radian_t CommandJoystick::GetDirection() const {
// https://docs.wpilib.org/en/stable/docs/software/basic-programming/coordinate-system.html#joystick-and-controller-coordinate-system
// A positive rotation around the X axis moves the joystick right, and a
// positive rotation around the Y axis moves the joystick backward. When
// treating them as translations, 0 radians is measured from the right
// direction, and angle increases clockwise.
//
// It's rotated 90 degrees CCW (y is negated and the arguments are reversed)
// so that 0 radians is forward.
return m_hid.GetDirection();
}

View File

@@ -56,7 +56,7 @@ class CommandJoystick : public CommandGenericHID {
class Trigger Top(frc::EventLoop* loop = CommandScheduler::GetInstance()
.GetDefaultButtonLoop()) const;
/**
* Get the magnitude of the direction vector formed by the joystick's
* Get the magnitude of the vector formed by the joystick's
* current position relative to its origin.
*
* @return The magnitude of the direction vector
@@ -64,7 +64,9 @@ class CommandJoystick : public CommandGenericHID {
double GetMagnitude() const;
/**
* Get the direction of the vector formed by the joystick and its origin.
* Get the direction of the vector formed by the joystick and its origin. 0 is
* forward and clockwise is positive. (Straight right is π/2 radians or 90
* degrees.)
*
* @return The direction of the vector.
*/

View File

@@ -47,7 +47,7 @@ class {{ ConsoleName }}Controller : public GenericHID,
{{ ConsoleName }}Controller& operator=({{ ConsoleName }}Controller&&) = default;
{% for stick in sticks %}
/**
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller.
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller. {{ stick.PositiveDirection }} is positive.
*
* @return the axis value.
*/

View File

@@ -45,28 +45,28 @@ class PS4Controller : public GenericHID,
PS4Controller& operator=(PS4Controller&&) = default;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetLeftX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return the axis value.
*/
double GetLeftY() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return the axis value.
*/

View File

@@ -45,28 +45,28 @@ class PS5Controller : public GenericHID,
PS5Controller& operator=(PS5Controller&&) = default;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetLeftX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return the axis value.
*/
double GetLeftY() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return the axis value.
*/

View File

@@ -45,28 +45,28 @@ class StadiaController : public GenericHID,
StadiaController& operator=(StadiaController&&) = default;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetLeftX() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return the axis value.
*/
double GetLeftY() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return the axis value.
*/

View File

@@ -45,28 +45,28 @@ class XboxController : public GenericHID,
XboxController& operator=(XboxController&&) = default;
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetLeftX() const;
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return the axis value.
*/
double GetRightX() const;
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return the axis value.
*/
double GetLeftY() const;
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return the axis value.
*/

View File

@@ -18,6 +18,7 @@
#include <numbers>
#include <utility>
#include <fmt/format.h>
#include <hal/HAL.h>
#include <wpi/sendable/SendableBuilder.h>
#include <wpi/sendable/SendableRegistry.h>
@@ -374,7 +375,8 @@ bool ADIS16448_IMU::SwitchToStandardSPI() {
// Validate the product ID
uint16_t prod_id = ReadRegister(PROD_ID);
if (prod_id != 16448) {
REPORT_ERROR("Could not find ADIS16448!");
REPORT_ERROR(
fmt::format("Could not find ADIS16448; got product ID {}", prod_id));
Close();
return false;
}

View File

@@ -17,6 +17,7 @@
#include <numbers>
#include <utility>
#include <fmt/format.h>
#include <hal/HAL.h>
#include <wpi/sendable/SendableBuilder.h>
#include <wpi/sendable/SendableRegistry.h>
@@ -355,7 +356,8 @@ bool ADIS16470_IMU::SwitchToStandardSPI() {
// Validate the product ID
uint16_t prod_id = ReadRegister(PROD_ID);
if (prod_id != 16982 && prod_id != 16470) {
REPORT_ERROR("Could not find ADIS16470!");
REPORT_ERROR(
fmt::format("Could not find ADIS16470; got product ID {}", prod_id));
Close();
return false;
}

View File

@@ -35,6 +35,13 @@ AddressableLED::AddressableLED(int port) : m_port{port} {
HAL_Report(HALUsageReporting::kResourceType_AddressableLEDs, port + 1);
}
void AddressableLED::SetColorOrder(AddressableLED::ColorOrder order) {
int32_t status = 0;
HAL_SetAddressableLEDColorOrder(
m_handle, static_cast<HAL_AddressableLEDColorOrder>(order), &status);
FRC_CheckErrorStatus(status, "Port {} Color order {}", m_port, order);
}
void AddressableLED::SetLength(int length) {
int32_t status = 0;
HAL_SetAddressableLEDLength(m_handle, length, &status);

View File

@@ -82,17 +82,18 @@ class Alert::SendableAlerts : public nt::NTSendable,
* @return the SendableAlerts for the group
*/
static SendableAlerts& ForGroup(std::string_view group) {
// Force initialization of SendableRegistry before our magic static to
// prevent incorrect destruction order.
wpi::SendableRegistry::EnsureInitialized();
static wpi::StringMap<Alert::SendableAlerts> groups;
auto [iter, exists] = groups.try_emplace(group);
SendableAlerts& sendable = iter->second;
if (!exists) {
frc::SmartDashboard::PutData(group, &iter->second);
SendableAlerts* salert = nullptr;
try {
auto* sendable = frc::SmartDashboard::GetData(group);
salert = dynamic_cast<SendableAlerts*>(sendable);
} catch (frc::RuntimeError&) {
}
return sendable;
if (!salert) {
// this leaks if ResetSmartDashboardInstance is called, but that's fine
salert = new Alert::SendableAlerts;
frc::SmartDashboard::PutData(group, salert);
}
return *salert;
}
private:

View File

@@ -119,5 +119,13 @@ double Joystick::GetMagnitude() const {
}
units::radian_t Joystick::GetDirection() const {
// https://docs.wpilib.org/en/stable/docs/software/basic-programming/coordinate-system.html#joystick-and-controller-coordinate-system
// A positive rotation around the X axis moves the joystick right, and a
// positive rotation around the Y axis moves the joystick backward. When
// treating them as translations, 0 radians is measured from the right
// direction, and angle increases clockwise.
//
// It's rotated 90 degrees CCW (y is negated and the arguments are reversed)
// so that 0 radians is forward.
return units::radian_t{std::atan2(GetX(), -GetY())};
}

View File

@@ -19,10 +19,13 @@
namespace frc {
/**
* A class for driving addressable LEDs, such as WS2812Bs and NeoPixels.
* A class for driving addressable LEDs, such as WS2812B, WS2815, and NeoPixels.
*
* By default, the timing supports WS2812B LEDs, but is configurable using
* SetBitTiming()
* By default, the timing supports WS2812B and WS2815 LEDs, but is configurable
* using SetBitTiming()
*
* Some LEDs use a different color order than the default GRB. The color order
* is configurable using SetColorOrder().
*
* <p>Only 1 LED driver is currently supported by the roboRIO. However,
* multiple LED strips can be connected in series and controlled from the
@@ -30,6 +33,18 @@ namespace frc {
*/
class AddressableLED {
public:
/**
* Order that color data is sent over the wire.
*/
enum ColorOrder {
kRGB = HAL_ALED_RGB, ///< RGB order
kRBG = HAL_ALED_RBG, ///< RBG order
kBGR = HAL_ALED_BGR, ///< BGR order
kBRG = HAL_ALED_BRG, ///< BRG order
kGBR = HAL_ALED_GBR, ///< GBR order
kGRB = HAL_ALED_GRB ///< GRB order. This is the default order.
};
class LEDData : public HAL_AddressableLEDData {
public:
LEDData() : LEDData(0, 0, 0) {}
@@ -95,6 +110,15 @@ class AddressableLED {
AddressableLED(AddressableLED&&) = default;
AddressableLED& operator=(AddressableLED&&) = default;
/**
* Sets the color order for this AddressableLED. The default order is GRB.
*
* This will take effect on the next call to SetData().
*
* @param order the color order
*/
void SetColorOrder(ColorOrder order);
/**
* Sets the length of the LED strip.
*
@@ -130,8 +154,8 @@ class AddressableLED {
/**
* Sets the bit timing.
*
* <p>By default, the driver is set up to drive WS2812Bs, so nothing needs to
* be set for those.
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing
* needs to be set for those.
*
* @param highTime0 high time for 0 bit (default 400ns)
* @param lowTime0 low time for 0 bit (default 900ns)
@@ -146,7 +170,7 @@ class AddressableLED {
* Sets the sync time.
*
* <p>The sync time is the time to hold output so LEDs enable. Default set for
* WS2812B.
* WS2812B and WS2815.
*
* @param syncTime the sync time (default 280us)
*/
@@ -169,4 +193,9 @@ class AddressableLED {
hal::Handle<HAL_AddressableLEDHandle, HAL_FreeAddressableLED> m_handle;
int m_port;
};
constexpr auto format_as(AddressableLED::ColorOrder order) {
return static_cast<int32_t>(order);
}
} // namespace frc

View File

@@ -148,6 +148,7 @@ class Joystick : public GenericHID {
* Get the X value of the current joystick.
*
* This depends on the mapping of the joystick connected to the current port.
* On most joysticks, positive is to the right.
*/
double GetX() const;
@@ -155,6 +156,7 @@ class Joystick : public GenericHID {
* Get the Y value of the current joystick.
*
* This depends on the mapping of the joystick connected to the current port.
* On most joysticks, positive is to the back.
*/
double GetY() const;
@@ -244,7 +246,7 @@ class Joystick : public GenericHID {
BooleanEvent Top(EventLoop* loop) const;
/**
* Get the magnitude of the direction vector formed by the joystick's
* Get the magnitude of the vector formed by the joystick's
* current position relative to its origin.
*
* @return The magnitude of the direction vector
@@ -252,7 +254,9 @@ class Joystick : public GenericHID {
double GetMagnitude() const;
/**
* Get the direction of the vector formed by the joystick and its origin.
* Get the direction of the vector formed by the joystick and its origin. 0 is
* forward and clockwise is positive. (Straight right is π/2 radians or 90
* degrees.)
*
* @return The direction of the vector.
*/

View File

@@ -52,11 +52,11 @@ class LinearSystemSim {
* @param dt The time between updates.
*/
void Update(units::second_t dt) {
// Update x. By default, this is the linear system dynamics x_k+1 = Ax_k +
// Bu_k
// Update x. By default, this is the linear system dynamics xₖ₊₁ = Ax +
// Buₖ.
m_x = UpdateX(m_x, m_u, dt);
// y = Cx + Du
// y = Cx + Du
m_y = m_plant.CalculateY(m_x, m_u);
// Add noise. If the user did not pass a noise vector to the
@@ -115,7 +115,14 @@ class LinearSystemSim {
*
* @param state The new state.
*/
void SetState(const Vectord<States>& state) { m_x = state; }
void SetState(const Vectord<States>& state) {
m_x = state;
// Update the output to reflect the new state.
//
// yₖ = Cxₖ + Duₖ
m_y = m_plant.CalculateY(m_x, m_u);
}
protected:
/**

View File

@@ -33,8 +33,7 @@ class AlertsTest : public ::testing::Test {
std::string GetGroupName() {
const ::testing::TestInfo* testInfo =
::testing::UnitTest::GetInstance()->current_test_info();
return fmt::format("{}_{}", testInfo->test_suite_name(),
testInfo->test_case_name());
return fmt::format("{}_{}", testInfo->test_suite_name(), testInfo->name());
}
template <typename... Args>
@@ -80,7 +79,16 @@ class AlertsTest : public ::testing::Test {
#define EXPECT_STATE(type, ...) \
EXPECT_EQ(GetActiveAlerts(type), (std::vector<std::string>{__VA_ARGS__}))
TEST_F(AlertsTest, SetUnset) {
TEST_F(AlertsTest, SetUnsetSingle) {
auto one = MakeAlert("one", kInfo);
EXPECT_FALSE(IsAlertActive("one", kInfo));
one.Set(true);
EXPECT_TRUE(IsAlertActive("one", kInfo));
one.Set(false);
EXPECT_FALSE(IsAlertActive("one", kInfo));
}
TEST_F(AlertsTest, SetUnsetMultiple) {
auto one = MakeAlert("one", kError);
auto two = MakeAlert("two", kInfo);
EXPECT_FALSE(IsAlertActive("one", kError));

View File

@@ -44,6 +44,15 @@ TEST(ElevatorSimTest, StateSpaceSim) {
EXPECT_NEAR(controller.GetSetpoint(), sim.GetPosition().value(), 0.2);
}
TEST(ElevatorSimTest, InitialState) {
constexpr auto startingHeight = 0.5_m;
frc::sim::ElevatorSim sim(frc::DCMotor::KrakenX60(2), 20, 8_kg, 0.1_m, 0_m,
1_m, true, startingHeight, {0.01, 0.0});
EXPECT_DOUBLE_EQ(startingHeight.value(), sim.GetPosition().value());
EXPECT_DOUBLE_EQ(0, sim.GetVelocity().value());
}
TEST(ElevatorSimTest, MinMax) {
frc::sim::ElevatorSim sim(frc::DCMotor::Vex775Pro(4), 14.67, 8_kg, 0.75_in,
0_m, 1_m, true, 0_m, {0.01});

View File

@@ -21,3 +21,12 @@ TEST(SingleJointedArmTest, Disabled) {
// The arm should swing down.
EXPECT_NEAR(sim.GetAngle().value(), -std::numbers::pi / 2, 0.01);
}
TEST(SingleJointedArmTest, InitialState) {
constexpr auto startingAngle = 45_deg;
frc::sim::SingleJointedArmSim sim(frc::DCMotor::KrakenX60(2), 125, 3_kg_sq_m,
30_in, 0_deg, 90_deg, true, startingAngle);
EXPECT_EQ(startingAngle, sim.GetAngle());
EXPECT_DOUBLE_EQ(0, sim.GetVelocity().value());
}

View File

@@ -60,28 +60,32 @@
"left",
"X"
],
"value": 0
"value": 0,
"PositiveDirection": "Right"
},
{
"NameParts": [
"right",
"X"
],
"value": 4
"value": 4,
"PositiveDirection": "Right"
},
{
"NameParts": [
"left",
"Y"
],
"value": 1
"value": 1,
"PositiveDirection": "Back"
},
{
"NameParts": [
"right",
"Y"
],
"value": 5
"value": 5,
"PositiveDirection": "Back"
}
],
"triggers": [
@@ -175,28 +179,32 @@
"left",
"X"
],
"value": 0
"value": 0,
"PositiveDirection": "Right"
},
{
"NameParts": [
"left",
"Y"
],
"value": 1
"value": 1,
"PositiveDirection": "Back"
},
{
"NameParts": [
"right",
"X"
],
"value": 2
"value": 2,
"PositiveDirection": "Right"
},
{
"NameParts": [
"right",
"Y"
],
"value": 5
"value": 5,
"PositiveDirection": "Back"
}
],
"triggers": [
@@ -290,28 +298,32 @@
"left",
"X"
],
"value": 0
"value": 0,
"PositiveDirection": "Right"
},
{
"NameParts": [
"left",
"Y"
],
"value": 1
"value": 1,
"PositiveDirection": "Back"
},
{
"NameParts": [
"right",
"X"
],
"value": 2
"value": 2,
"PositiveDirection": "Right"
},
{
"NameParts": [
"right",
"Y"
],
"value": 5
"value": 5,
"PositiveDirection": "Back"
}
],
"triggers": [
@@ -412,28 +424,32 @@
"left",
"X"
],
"value": 0
"value": 0,
"PositiveDirection": "Right"
},
{
"NameParts": [
"right",
"X"
],
"value": 3
"value": 3,
"PositiveDirection": "Right"
},
{
"NameParts": [
"left",
"Y"
],
"value": 1
"value": 1,
"PositiveDirection": "Back"
},
{
"NameParts": [
"right",
"Y"
],
"value": 4
"value": 4,
"PositiveDirection": "Back"
}
]
}

View File

@@ -66,7 +66,8 @@
"type": "object",
"required": [
"NameParts",
"value"
"value",
"PositiveDirection"
],
"properties": {
"NameParts": {
@@ -80,6 +81,10 @@
"value": {
"description": "The axis value",
"type": "integer"
},
"PositiveDirection": {
"description": "The positive direction of the axis.",
"type": "string"
}
}
}

View File

@@ -103,7 +103,7 @@ public class {{ ConsoleName }}Controller extends GenericHID implements Sendable
}
{% for stick in sticks %}
/**
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller.
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller. {{ stick.PositiveDirection }} is positive.
*
* @return The axis value.
*/

View File

@@ -129,7 +129,7 @@ public class PS4Controller extends GenericHID implements Sendable {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -138,7 +138,7 @@ public class PS4Controller extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -147,7 +147,7 @@ public class PS4Controller extends GenericHID implements Sendable {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -156,7 +156,7 @@ public class PS4Controller extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -129,7 +129,7 @@ public class PS5Controller extends GenericHID implements Sendable {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -138,7 +138,7 @@ public class PS5Controller extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -147,7 +147,7 @@ public class PS5Controller extends GenericHID implements Sendable {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -156,7 +156,7 @@ public class PS5Controller extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -127,7 +127,7 @@ public class StadiaController extends GenericHID implements Sendable {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -136,7 +136,7 @@ public class StadiaController extends GenericHID implements Sendable {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -145,7 +145,7 @@ public class StadiaController extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -154,7 +154,7 @@ public class StadiaController extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -121,7 +121,7 @@ public class XboxController extends GenericHID implements Sendable {
}
/**
* Get the X axis value of left side of the controller.
* Get the X axis value of left side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -130,7 +130,7 @@ public class XboxController extends GenericHID implements Sendable {
}
/**
* Get the X axis value of right side of the controller.
* Get the X axis value of right side of the controller. Right is positive.
*
* @return The axis value.
*/
@@ -139,7 +139,7 @@ public class XboxController extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of left side of the controller.
* Get the Y axis value of left side of the controller. Back is positive.
*
* @return The axis value.
*/
@@ -148,7 +148,7 @@ public class XboxController extends GenericHID implements Sendable {
}
/**
* Get the Y axis value of right side of the controller.
* Get the Y axis value of right side of the controller. Back is positive.
*
* @return The axis value.
*/

View File

@@ -425,8 +425,9 @@ public class ADIS16448_IMU implements AutoCloseable, Sendable {
}
readRegister(PROD_ID); // Dummy read
// Validate the product ID
if (readRegister(PROD_ID) != 16448) {
DriverStation.reportError("Could not find ADIS16448", false);
int prodId = readRegister(PROD_ID);
if (prodId != 16448) {
DriverStation.reportError("Could not find ADIS16448; got product ID " + prodId, false);
close();
return false;
}

View File

@@ -483,8 +483,9 @@ public class ADIS16470_IMU implements AutoCloseable, Sendable {
}
readRegister(PROD_ID); // Dummy read
// Validate the product ID
if (readRegister(PROD_ID) != 16982) {
DriverStation.reportError("Could not find an ADIS16470", false);
int prodId = readRegister(PROD_ID);
if (prodId != 16982 && prodId != 16470) {
DriverStation.reportError("Could not find an ADIS16470; got product ID " + prodId, false);
close();
return false;
}

Some files were not shown because too many files have changed in this diff Show More