Merge "Add support for parsing JSON into a FlatBufferBuilder"
diff --git a/WORKSPACE b/WORKSPACE
index 5f29700..d0d6347 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -50,6 +50,10 @@
"//debian:python_gtk.bzl",
python_gtk_debs = "files",
)
+load(
+ "//debian:opencv_armhf.bzl",
+ opencv_armhf_debs = "files",
+)
load("//debian:packages.bzl", "generate_repositories_for_debs")
generate_repositories_for_debs(python_debs)
@@ -76,6 +80,8 @@
generate_repositories_for_debs(python_gtk_debs)
+generate_repositories_for_debs(opencv_armhf_debs)
+
http_archive(
name = "python_repo",
build_file = "@//debian:python.BUILD",
@@ -144,6 +150,17 @@
url = "http://frc971.org/Build-Dependencies/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz",
)
+# The main partition from https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-09-30/2019-09-26-raspbian-buster-lite.zip.
+# The following folders are removed to make bazel happy with it:
+# usr/share/ca-certificates
+# This copy command to make clang happy: `cp usr/lib/arm-linux-gnueabihf/*.o usr/lib`
+http_archive(
+ name = "armhf_debian_rootfs",
+ build_file = "@//:compilers/armhf_debian_rootfs.BUILD",
+ sha256 = "8c827bdb79615046ee3e13e85664e5d01286ca1721f7169341667a634e599eb6",
+ url = "http://frc971.org/Build-Dependencies/2019-09-26-raspbian-buster-lite_rootfs.tar.bz2",
+)
+
new_git_repository(
name = "python_gflags_repo",
build_file = "@//debian:gflags.BUILD",
@@ -560,3 +577,11 @@
sha256 = "91c98edee0c90a19992792c711dde4a6743af2d6d7e45b5079ec228fdf51ff11",
urls = ["http://www.frc971.org/Build-Dependencies/small_sample_logfile.fbs"],
)
+
+# OpenCV armhf (for raspberry pi)
+http_archive(
+ name = "opencv_armhf",
+ build_file = "@//debian:opencv.BUILD",
+ sha256 = "1dd496ad0947ed6ce5d89cbefcfa55ea15ccb5bf70fa6ad7701c62cf2fcdd657",
+ url = "http://www.frc971.org/Build-Dependencies/opencv_armhf_v3.tar.gz",
+)
diff --git a/aos/controls/polytope.h b/aos/controls/polytope.h
index c63166f..28f011e 100644
--- a/aos/controls/polytope.h
+++ b/aos/controls/polytope.h
@@ -109,13 +109,15 @@
#ifdef __linux__
+
template <int number_of_dimensions>
class HPolytope : public Polytope<number_of_dimensions> {
public:
// Constructs a polytope given the H and k matrices.
- HPolytope(Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic,
- number_of_dimensions>> H,
- Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k)
+ HPolytope(
+ Eigen::Ref<
+ const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>> H,
+ Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k)
: H_(H), k_(k), vertices_(CalculateVertices(H, k)) {}
// This is an initialization function shared across all instantiations of this
@@ -133,6 +135,8 @@
return k_;
}
+ // NOTE: If you are getting bizarre errors that you are tracing back to the
+ // vertex matrix being funky, please check that you correctly called Init().
Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> Vertices()
const override {
return vertices_;
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 3652db2..39c3d17 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -32,6 +32,7 @@
CHECK(config->has_nodes()) << ": Config must have nodes to transfer.";
flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(1);
flatbuffers::Offset<Node> node_offset = CopyFlatBuffer<Node>(my_node, &fbb);
const std::string_view node_name = my_node->name()->string_view();
@@ -106,6 +107,7 @@
aos::FlatbufferDetachedBuffer<aos::logger::MessageHeader>
MakeMessageHeaderReply() {
flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(1);
logger::MessageHeader::Builder message_header_builder(fbb);
message_header_builder.add_channel_index(0);
message_header_builder.add_monotonic_sent_time(0);
@@ -121,6 +123,7 @@
const std::vector<std::string_view> &source_node_names,
const Configuration *configuration) {
flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(1);
std::vector<flatbuffers::Offset<ClientConnection>> connection_offsets;
for (const std::string_view node_name : source_node_names) {
diff --git a/compilers/armhf_debian_rootfs.BUILD b/compilers/armhf_debian_rootfs.BUILD
new file mode 100644
index 0000000..004b82a
--- /dev/null
+++ b/compilers/armhf_debian_rootfs.BUILD
@@ -0,0 +1,15 @@
+filegroup(
+ name = "sysroot_files",
+ srcs = glob(
+ include = [
+ "include/**",
+ "lib/**",
+ "usr/include/**",
+ "usr/lib/**",
+ ],
+ exclude = [
+ "usr/share/**",
+ ],
+ ),
+ visibility = ["//visibility:public"],
+)
diff --git a/compilers/linaro_linux_gcc.BUILD b/compilers/linaro_linux_gcc.BUILD
index aea261d..f2fae98 100644
--- a/compilers/linaro_linux_gcc.BUILD
+++ b/compilers/linaro_linux_gcc.BUILD
@@ -58,14 +58,15 @@
filegroup(
name = "compiler_pieces",
- srcs = glob([
- "arm-linux-gnueabihf/**",
- "libexec/**",
- "lib/gcc/arm-linux-gnueabihf/**",
- "include/**",
- ], exclude=["arm-linux-gnueabihf/libc/usr/include/linux/sctp.h"]) +
- [
- "@org_frc971//third_party/linux:sctp",
+ srcs = glob(
+ include = [
+ "arm-linux-gnueabihf/**",
+ "libexec/**",
+ "lib/gcc/arm-linux-gnueabihf/**",
+ "include/**",
+ ],
+ ) + [
+ "@armhf_debian_rootfs//:sysroot_files",
],
)
diff --git a/debian/BUILD b/debian/BUILD
index 5c9d814..11f9349 100644
--- a/debian/BUILD
+++ b/debian/BUILD
@@ -48,6 +48,10 @@
":python_gtk.bzl",
python_gtk_debs = "files",
)
+load(
+ ":opencv_armhf.bzl",
+ opencv_armhf_debs = "files",
+)
load("//debian:packages.bzl", "download_packages", "generate_deb_tarball")
filegroup(
@@ -282,6 +286,13 @@
files = python_gtk_debs,
)
+# This list was generated with download_packages.py on armhf and then
+# hand-tweaked to get everything it needs.
+generate_deb_tarball(
+ name = "opencv_armhf_v3",
+ files = opencv_armhf_debs,
+)
+
exports_files([
"ssh_wrapper.sh",
])
diff --git a/debian/opencv.BUILD b/debian/opencv.BUILD
new file mode 100644
index 0000000..1915d25
--- /dev/null
+++ b/debian/opencv.BUILD
@@ -0,0 +1,258 @@
+load("@//tools/build_rules:select.bzl", "cpu_select")
+
+cc_library(
+ name = "opencv",
+ srcs = cpu_select({
+ "amd64": [
+ ],
+ "roborio": [
+ ],
+ "armhf": [
+ "usr/lib/arm-linux-gnueabihf/libopencv_core.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_features2d.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_imgproc.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_flann.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_highgui.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_videoio.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_imgcodecs.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libopencv_ml.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libtbb.so.2",
+ "usr/lib/arm-linux-gnueabihf/libgtk-3.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgdk-3.so.0",
+ "usr/lib/arm-linux-gnueabihf/libpangocairo-1.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libpango-1.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libatk-1.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgdcmDICT.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgdcmCommon.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgdcmIOD.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgdcmMSFF.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libavutil.so.56",
+ "usr/lib/arm-linux-gnueabihf/libswscale.so.5",
+ "usr/lib/arm-linux-gnueabihf/libavresample.so.4",
+ "usr/lib/arm-linux-gnueabihf/libcairo-gobject.so.2",
+ "usr/lib/arm-linux-gnueabihf/libcairo.so.2",
+ "usr/lib/arm-linux-gnueabihf/libgdk_pixbuf-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgio-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgobject-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libglib-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgthread-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libdc1394.so.22",
+ "usr/lib/arm-linux-gnueabihf/libgphoto2.so.6",
+ "usr/lib/arm-linux-gnueabihf/libgphoto2_port.so.12",
+ "usr/lib/arm-linux-gnueabihf/libavcodec.so.58",
+ "usr/lib/arm-linux-gnueabihf/libavformat.so.58",
+ "usr/lib/arm-linux-gnueabihf/libjpeg.so.62",
+ "usr/lib/arm-linux-gnueabihf/libwebp.so.6",
+ "usr/lib/arm-linux-gnueabihf/libpng16.so.16",
+ "usr/lib/arm-linux-gnueabihf/libtiff.so.5",
+ "usr/lib/arm-linux-gnueabihf/libImath-2_2.so.23",
+ "usr/lib/arm-linux-gnueabihf/libIlmImf-2_2.so.23",
+ "usr/lib/arm-linux-gnueabihf/libIex-2_2.so.23",
+ "usr/lib/arm-linux-gnueabihf/libHalf.so.23",
+ "usr/lib/arm-linux-gnueabihf/libIlmThread-2_2.so.23",
+ "usr/lib/libgdal.so.20",
+ "usr/lib/arm-linux-gnueabihf/libgdcmDSED.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgmodule-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libX11.so.6",
+ "usr/lib/arm-linux-gnueabihf/libXi.so.6",
+ "usr/lib/arm-linux-gnueabihf/libXcomposite.so.1",
+ "usr/lib/arm-linux-gnueabihf/libXdamage.so.1",
+ "usr/lib/arm-linux-gnueabihf/libXfixes.so.3",
+ "usr/lib/arm-linux-gnueabihf/libatk-bridge-2.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libxkbcommon.so.0",
+ "usr/lib/arm-linux-gnueabihf/libwayland-cursor.so.0",
+ "usr/lib/arm-linux-gnueabihf/libwayland-egl.so.1",
+ "usr/lib/arm-linux-gnueabihf/libwayland-client.so.0",
+ "usr/lib/arm-linux-gnueabihf/libepoxy.so.0",
+ "usr/lib/arm-linux-gnueabihf/libharfbuzz.so.0",
+ "usr/lib/arm-linux-gnueabihf/libpangoft2-1.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libfontconfig.so.1",
+ "usr/lib/arm-linux-gnueabihf/libfreetype.so.6",
+ "usr/lib/arm-linux-gnueabihf/libXinerama.so.1",
+ "usr/lib/arm-linux-gnueabihf/libXrandr.so.2",
+ "usr/lib/arm-linux-gnueabihf/libXcursor.so.1",
+ "usr/lib/arm-linux-gnueabihf/libXext.so.6",
+ "usr/lib/arm-linux-gnueabihf/libthai.so.0",
+ "usr/lib/arm-linux-gnueabihf/libfribidi.so.0",
+ "usr/lib/arm-linux-gnueabihf/libcrypto.so.1.1",
+ "lib/arm-linux-gnueabihf/libexpat.so.1",
+ "usr/lib/arm-linux-gnueabihf/libgdcmjpeg8.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgdcmjpeg12.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libgdcmjpeg16.so.2.8",
+ "usr/lib/arm-linux-gnueabihf/libopenjp2.so.7",
+ "usr/lib/arm-linux-gnueabihf/libCharLS.so.2",
+ "lib/arm-linux-gnueabihf/libuuid.so.1",
+ "usr/lib/arm-linux-gnueabihf/libjson-c.so.3",
+ "usr/lib/arm-linux-gnueabihf/libva-drm.so.2",
+ "usr/lib/arm-linux-gnueabihf/libva.so.2",
+ "usr/lib/arm-linux-gnueabihf/libva-x11.so.2",
+ "usr/lib/arm-linux-gnueabihf/libvdpau.so.1",
+ "usr/lib/arm-linux-gnueabihf/libdrm.so.2",
+ "usr/lib/arm-linux-gnueabihf/libpixman-1.so.0",
+ "usr/lib/arm-linux-gnueabihf/libxcb-shm.so.0",
+ "usr/lib/arm-linux-gnueabihf/libxcb.so.1",
+ "usr/lib/arm-linux-gnueabihf/libxcb-render.so.0",
+ "usr/lib/arm-linux-gnueabihf/libXrender.so.1",
+ "lib/arm-linux-gnueabihf/libmount.so.1",
+ "usr/lib/arm-linux-gnueabihf/libffi.so.6",
+ "usr/lib/arm-linux-gnueabihf/libraw1394.so.11",
+ "lib/arm-linux-gnueabihf/libusb-1.0.so.0",
+ "usr/lib/arm-linux-gnueabihf/libltdl.so.7",
+ "usr/lib/arm-linux-gnueabihf/libexif.so.12",
+ "usr/lib/arm-linux-gnueabihf/libswresample.so.3",
+ "usr/lib/arm-linux-gnueabihf/libvpx.so.5",
+ "usr/lib/arm-linux-gnueabihf/libwebpmux.so.3",
+ "opt/vc/lib/libmmal_core.so",
+ "opt/vc/lib/libmmal_util.so",
+ "opt/vc/lib/libmmal_vc_client.so",
+ "opt/vc/lib/libbcm_host.so",
+ "usr/lib/arm-linux-gnueabihf/librsvg-2.so.2",
+ "usr/lib/arm-linux-gnueabihf/libzvbi.so.0",
+ "usr/lib/arm-linux-gnueabihf/libsnappy.so.1",
+ "usr/lib/arm-linux-gnueabihf/libaom.so.0",
+ "usr/lib/arm-linux-gnueabihf/libcodec2.so.0.8.1",
+ "usr/lib/arm-linux-gnueabihf/libgsm.so.1",
+ "usr/lib/arm-linux-gnueabihf/libmp3lame.so.0",
+ "usr/lib/arm-linux-gnueabihf/libopus.so.0",
+ "usr/lib/arm-linux-gnueabihf/libshine.so.3",
+ "usr/lib/arm-linux-gnueabihf/libspeex.so.1",
+ "usr/lib/arm-linux-gnueabihf/libtheoraenc.so.1",
+ "usr/lib/arm-linux-gnueabihf/libtheoradec.so.1",
+ "usr/lib/arm-linux-gnueabihf/libtwolame.so.0",
+ "usr/lib/arm-linux-gnueabihf/libvorbis.so.0",
+ "usr/lib/arm-linux-gnueabihf/libvorbisenc.so.2",
+ "usr/lib/arm-linux-gnueabihf/libwavpack.so.1",
+ "usr/lib/arm-linux-gnueabihf/libx264.so.155",
+ "usr/lib/arm-linux-gnueabihf/libx265.so.165",
+ "usr/lib/arm-linux-gnueabihf/libxvidcore.so.4",
+ "usr/lib/arm-linux-gnueabihf/libxml2.so.2",
+ "usr/lib/arm-linux-gnueabihf/libgme.so.0",
+ "usr/lib/arm-linux-gnueabihf/libopenmpt.so.0",
+ "usr/lib/arm-linux-gnueabihf/libchromaprint.so.1",
+ "usr/lib/arm-linux-gnueabihf/libbluray.so.2",
+ "usr/lib/arm-linux-gnueabihf/libgnutls.so.30",
+ "usr/lib/arm-linux-gnueabihf/libssh-gcrypt.so.4",
+ "usr/lib/arm-linux-gnueabihf/libzstd.so.1",
+ "usr/lib/arm-linux-gnueabihf/libjbig.so.0",
+ "usr/lib/libarmadillo.so.9",
+ "usr/lib/arm-linux-gnueabihf/libproj.so.13",
+ "usr/lib/arm-linux-gnueabihf/libpoppler.so.82",
+ "usr/lib/arm-linux-gnueabihf/libfreexl.so.1",
+ "usr/lib/arm-linux-gnueabihf/libqhull.so.7",
+ "usr/lib/arm-linux-gnueabihf/libgeos_c.so.1",
+ "usr/lib/arm-linux-gnueabihf/libepsilon.so.1",
+ "usr/lib/arm-linux-gnueabihf/libodbc.so.2",
+ "usr/lib/arm-linux-gnueabihf/libodbcinst.so.2",
+ "usr/lib/arm-linux-gnueabihf/libkmlbase.so.1",
+ "usr/lib/arm-linux-gnueabihf/libkmldom.so.1",
+ "usr/lib/arm-linux-gnueabihf/libkmlengine.so.1",
+ "usr/lib/arm-linux-gnueabihf/libkmlxsd.so.1",
+ "usr/lib/arm-linux-gnueabihf/libkmlregionator.so.1",
+ "usr/lib/arm-linux-gnueabihf/libxerces-c-3.2.so",
+ "usr/lib/arm-linux-gnueabihf/libnetcdf.so.13",
+ "usr/lib/arm-linux-gnueabihf/libhdf5_serial_hl.so.100",
+ "usr/lib/arm-linux-gnueabihf/libsz.so.2",
+ "usr/lib/arm-linux-gnueabihf/libhdf5_serial.so.103",
+ "usr/lib/libmfhdfalt.so.0",
+ "usr/lib/libdfalt.so.0",
+ "usr/lib/libogdi.so.3.2",
+ "usr/lib/arm-linux-gnueabihf/libgif.so.7",
+ "usr/lib/arm-linux-gnueabihf/libgeotiff.so.2",
+ "usr/lib/arm-linux-gnueabihf/libpq.so.5",
+ "usr/lib/arm-linux-gnueabihf/libdapclient.so.6",
+ "usr/lib/arm-linux-gnueabihf/libdapserver.so.7",
+ "usr/lib/arm-linux-gnueabihf/libdap.so.25",
+ "usr/lib/arm-linux-gnueabihf/libspatialite.so.7",
+ "usr/lib/arm-linux-gnueabihf/libcurl-gnutls.so.4",
+ "usr/lib/arm-linux-gnueabihf/libfyba.so.0",
+ "usr/lib/arm-linux-gnueabihf/libfygm.so.0",
+ "usr/lib/arm-linux-gnueabihf/libfyut.so.0",
+ "usr/lib/arm-linux-gnueabihf/libmariadb.so.3",
+ "lib/arm-linux-gnueabihf/libdbus-1.so.3",
+ "usr/lib/arm-linux-gnueabihf/libatspi.so.0",
+ "usr/lib/arm-linux-gnueabihf/libgraphite2.so.3",
+ "usr/lib/arm-linux-gnueabihf/libdatrie.so.1",
+ "usr/lib/arm-linux-gnueabihf/libXau.so.6",
+ "usr/lib/arm-linux-gnueabihf/libXdmcp.so.6",
+ "lib/arm-linux-gnueabihf/libblkid.so.1",
+ "lib/arm-linux-gnueabihf/libudev.so.1",
+ "usr/lib/arm-linux-gnueabihf/libsoxr.so.0",
+ "opt/vc/lib/libvcos.so",
+ "opt/vc/lib/libvchiq_arm.so",
+ "opt/vc/lib/libvcsm.so",
+ "usr/lib/arm-linux-gnueabihf/libcroco-0.6.so.3",
+ "usr/lib/arm-linux-gnueabihf/libogg.so.0",
+ "usr/lib/arm-linux-gnueabihf/libicui18n.so.63",
+ "usr/lib/arm-linux-gnueabihf/libicuuc.so.63",
+ "usr/lib/arm-linux-gnueabihf/libicudata.so.63",
+ "usr/lib/arm-linux-gnueabihf/libmpg123.so.0",
+ "usr/lib/arm-linux-gnueabihf/libvorbisfile.so.3",
+ "usr/lib/arm-linux-gnueabihf/libp11-kit.so.0",
+ "usr/lib/arm-linux-gnueabihf/libidn2.so.0",
+ "usr/lib/arm-linux-gnueabihf/libunistring.so.2",
+ "usr/lib/arm-linux-gnueabihf/libtasn1.so.6",
+ "usr/lib/arm-linux-gnueabihf/libnettle.so.6",
+ "usr/lib/arm-linux-gnueabihf/libhogweed.so.4",
+ "usr/lib/arm-linux-gnueabihf/libgmp.so.10",
+ "lib/arm-linux-gnueabihf/libgcrypt.so.20",
+ "usr/lib/arm-linux-gnueabihf/libgssapi_krb5.so.2",
+ "usr/lib/arm-linux-gnueabihf/blas/libblas.so.3",
+ "usr/lib/arm-linux-gnueabihf/lapack/liblapack.so.3",
+ "usr/lib/arm-linux-gnueabihf/libarpack.so.2",
+ "usr/lib/arm-linux-gnueabihf/libsuperlu.so.5",
+ "usr/lib/arm-linux-gnueabihf/libnss3.so",
+ "usr/lib/arm-linux-gnueabihf/libsmime3.so",
+ "usr/lib/arm-linux-gnueabihf/libnspr4.so",
+ "usr/lib/arm-linux-gnueabihf/liblcms2.so.2",
+ "usr/lib/arm-linux-gnueabihf/libgeos-3.7.1.so",
+ "usr/lib/arm-linux-gnueabihf/libpopt.so.0",
+ "usr/lib/arm-linux-gnueabihf/libminizip.so.1",
+ "usr/lib/arm-linux-gnueabihf/liburiparser.so.1",
+ "usr/lib/arm-linux-gnueabihf/libkmlconvenience.so.1",
+ "usr/lib/arm-linux-gnueabihf/libaec.so.0",
+ "usr/lib/arm-linux-gnueabihf/libssl.so.1.1",
+ "usr/lib/arm-linux-gnueabihf/libldap_r-2.4.so.2",
+ "usr/lib/arm-linux-gnueabihf/libsqlite3.so.0",
+ "usr/lib/arm-linux-gnueabihf/libnghttp2.so.14",
+ "usr/lib/arm-linux-gnueabihf/librtmp.so.1",
+ "usr/lib/arm-linux-gnueabihf/libssh2.so.1",
+ "usr/lib/arm-linux-gnueabihf/libpsl.so.5",
+ "usr/lib/arm-linux-gnueabihf/libkrb5.so.3",
+ "usr/lib/arm-linux-gnueabihf/libk5crypto.so.3",
+ "lib/arm-linux-gnueabihf/libcom_err.so.2",
+ "usr/lib/arm-linux-gnueabihf/liblber-2.4.so.2",
+ "lib/arm-linux-gnueabihf/libsystemd.so.0",
+ "usr/lib/arm-linux-gnueabihf/libbsd.so.0",
+ "lib/arm-linux-gnueabihf/libgpg-error.so.0",
+ "usr/lib/arm-linux-gnueabihf/libkrb5support.so.0",
+ "lib/arm-linux-gnueabihf/libkeyutils.so.1",
+ "usr/lib/arm-linux-gnueabihf/libgfortran.so.5",
+ "usr/lib/arm-linux-gnueabihf/libnssutil3.so",
+ "usr/lib/arm-linux-gnueabihf/libplc4.so",
+ "usr/lib/arm-linux-gnueabihf/libplds4.so",
+ "usr/lib/arm-linux-gnueabihf/libsasl2.so.2",
+ "usr/lib/arm-linux-gnueabihf/liblz4.so.1",
+ "lib/arm-linux-gnueabihf/libz.so.1",
+ "usr/lib/arm-linux-gnueabihf/libatomic.so.1",
+ "lib/arm-linux-gnueabihf/libselinux.so.1",
+ "lib/arm-linux-gnueabihf/libpcre.so.3",
+ "lib/arm-linux-gnueabihf/liblzma.so.5",
+ "lib/arm-linux-gnueabihf/libbz2.so.1.0",
+ "usr/lib/arm-linux-gnueabihf/libgomp.so.1",
+ ],
+ "cortex-m": [],
+ }),
+ hdrs = glob([
+ "usr/include/opencv/**",
+ "usr/include/opencv2/**",
+ ]),
+ includes = [
+ "usr/include",
+ ],
+ linkopts = [
+ "-ldl",
+ "-lnsl",
+ "-lresolv",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/debian/opencv_armhf.bzl b/debian/opencv_armhf.bzl
new file mode 100644
index 0000000..d48ee39
--- /dev/null
+++ b/debian/opencv_armhf.bzl
@@ -0,0 +1,380 @@
+files = {
+ "adduser_3.118_all.deb": "bd71dd1ab8dcd6005390708f23741d07f1913877affb7604dfd55f85d009aa2b",
+ "adwaita-icon-theme_3.30.1-1_all.deb": "698b3f0fa337bb36ea4fe072a37a32a1c81875db13042368677490bb087ccb93",
+ "coreutils_8.30-3_armhf.deb": "6a578920fe016ce628065f4c7a2639a6ffc3d52637e4b4f20a46ea76fcc05539",
+ "dconf-gsettings-backend_0.30.1-2_armhf.deb": "61bd02ba2da9e549e245ab2f5152baa2f27ea40fd0b0cde5873c63048feaa708",
+ "dconf-service_0.30.1-2_armhf.deb": "2ddc0eddff21e18afda15379d6414ffea5ea21a10e958c2ddc85625feab5cf70",
+ "fontconfig-config_2.13.1-2_all.deb": "9f5d34ba20eb156ef62d8126866a376be985c6a83fdcfb33f12cd83acac480c2",
+ "fontconfig_2.13.1-2_armhf.deb": "f2d17a9588f37d149e2777bdeb6acbc8bad203b814c8983b34ddee24ce316421",
+ "fonts-dejavu-core_2.37-1_all.deb": "58d21a255606191e6512cca51f32c4480e7a798945cc980623377696acfa3cfc",
+ "fonts-liberation_1.07.4-9_all.deb": "c936aebbfd0af7851399ae5ab08bb01744f5e3381f7678fb87cc77114f95ef53",
+ "gdal-data_2.4.0+dfsg-1_all.deb": "6e0fce32cf2e85ad2539482087d712bf2258d05e1838f3586a17ad2dc6bb7410",
+ "glib-networking-common_2.58.0-2_all.deb": "79831fd09fc96dc5729e8ed360563b05100d6bff70b03f3badf4e0f4759bb7ec",
+ "glib-networking-services_2.58.0-2_armhf.deb": "f901428286b7d10acac92159d6e0b9c3e09691dbe7d5ec4848f962491f0805d6",
+ "glib-networking_2.58.0-2_armhf.deb": "713ec6741147cc75468f8c16cda12185aa5b11ec79cfcc5786790febf1664aaf",
+ "gsettings-desktop-schemas_3.28.1-1_all.deb": "a75aed8781a781c4b819b2d1e952791b123580b1a02a4bb35fdbbba2e3ab8310",
+ "gtk-update-icon-cache_3.24.5-1+rpt2_armhf.deb": "8ed36c27190370354987249416dd4d19545a9e82e6020f271499bf72db067e8b",
+ "hicolor-icon-theme_0.17-2_all.deb": "20304d34b85a734ec1e4830badf3a3a70a5dc5f9c1afc0b2230ecd760c81b5e0",
+ "libaec0_1.0.2-1_armhf.deb": "6a8107a0253577259ccadd5c274664bf7cb7f0d6e67a694d7ff0da7af850a7e9",
+ "libaom0_1.0.0-3_armhf.deb": "eca4cfebdc6f8afcdf141e17097c37c4094da61bb220c5c6fdf7faf2ef9badd6",
+ "libarmadillo9_9.200.7+dfsg-1_armhf.deb": "30e835a8de5c42bcede50b98f10ac2292d2677de12fbb44349b4611cc8803ad8",
+ "libarpack2_3.7.0-2_armhf.deb": "a1a466a360902651a8722539a39521787afe10df067a47b8d6b070e3cdc35b60",
+ "libatk-bridge2.0-0_2.30.0-5_armhf.deb": "7c7900d671e6a04cada9f36162a7a2e411763b19b08a2cd6b81ec9c249d24445",
+ "libatk1.0-0_2.30.0-2_armhf.deb": "c2c32c8784a995894da3b76d9c5e9269f64cb2cf3c28971a1cbede53190605c2",
+ "libatk1.0-data_2.30.0-2_all.deb": "cf0c94611ff2245ae31d12a5a43971eb4ca628f42e93b0e003fd2c4c0de5e533",
+ "libatomic1_8.3.0-6+rpi1_armhf.deb": "f0c29af98f8358dc7d38a25f12a1f82ee8f876336ca459c25c08f42754b03865",
+ "libatspi2.0-0_2.30.0-7_armhf.deb": "05980a3e666d895433b8bd24306d67d2362ead58199253ce2e699f9dc4e8fa5d",
+ "libaudit-common_2.8.4-3_all.deb": "4e51dc247cde083528d410f525c6157b08be8b69511891cf972bc87025311371",
+ "libaudit1_2.8.4-3_armhf.deb": "25378f4115b0c71b352ea91095e85d5ddced7ceb7b46448abb8cb53a0bc02da9",
+ "libavahi-client3_0.7-4+b1_armhf.deb": "8555f041940308d4bb24558d8eed6c506287d95ea151814c9fb5572ef68b9797",
+ "libavahi-common-data_0.7-4+b1_armhf.deb": "064992922f2ff006f0dea327fb5c38e1328fe58e17eb55a6c2ceac4dc531c46d",
+ "libavahi-common3_0.7-4+b1_armhf.deb": "6d40047dc95d3d24c75763288367eb651cac1e93ad167d9c4cae6eb6ffc7fa59",
+ "libavcodec-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "3dfe147c25d8a852edd763722afc3e46c83a4c87092624af59f89cb8fcde20cc",
+ "libavcodec58_4.1.4-1+rpt1~deb10u1_armhf.deb": "38010fa402aad07d8d68a93f2f9518ba7ac997371e2ef424b8d7d083687a4ad4",
+ "libavformat-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "1d43679f4c7bd834ded1da3e82d1d09380dc8dcba169b57abdcee931e5fb5d23",
+ "libavformat58_4.1.4-1+rpt1~deb10u1_armhf.deb": "6e61fd1b2c724214def3c50eda0dfcd6ef4982dbf69fa8be33d91c77b25a39e8",
+ "libavresample-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "f733261644f240dc73c8fbcb85bec57797183df2f83a8a30245b2d8c80a2b8f9",
+ "libavresample4_4.1.4-1+rpt1~deb10u1_armhf.deb": "4d06883a40682cd2348d7bfbb657340bcb23f57bea5151554483ca99704d6d1e",
+ "libavutil-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "b2d034461daa7e3ab80bcfb5e0975ac243dc16dfe2c1526a3d658cc1f8aaf0b2",
+ "libavutil56_4.1.4-1+rpt1~deb10u1_armhf.deb": "e17b9be1e6ea97fe7ce2bcebc66e4d975501bb3f5408ebbd7b922ad739f22445",
+ "libblas3_3.8.0-2_armhf.deb": "4231b6d249eb60cb13e9fc65e4378bc9e7908407a3b49a6fcdd4e88eb5df9f3d",
+ "libblkid1_2.33.1-0.1_armhf.deb": "92801e35c3dbe24f2cb45d76e0837bc3928ecf2c1016ab07e827097634afa2c0",
+ "libbluray2_1.1.0-1_armhf.deb": "670a11be9d786fa07e472928893f78ba57140cd7caecfb1e396802d3ef8863dd",
+ "libbsd0_0.9.1-2_armhf.deb": "49164a38e2aa95b45e154dafa9974765554bad662c6ee865244a1b115d568b94",
+ "libcairo-gobject2_1.16.0-4+rpt1_armhf.deb": "e3fdc6667bb647e0804cbad1eb369949d7caa8a711592786b158d0bc62c576cf",
+ "libcairo2_1.16.0-4+rpt1_armhf.deb": "1f651a306f87337b4b493ba248a09813a6da8acf60bea8ad3669a06d9d522c9f",
+ "libcap-ng0_0.7.9-2_armhf.deb": "5218a1d5d264620e027391df53c66ddc3cb5715e0aa6065238118fa3515b5e7b",
+ "libcharls2_2.0.0+dfsg-1_armhf.deb": "fa95753cbe407167cf4959f9ac02ac5db804fbaad1aaf5e04fc0bc2839502ee4",
+ "libchromaprint1_1.4.3-3_armhf.deb": "ae8106e0e758a423a89443fd1abb180d5ba3e6b208c93fc4f4a619390331abd1",
+ "libcodec2-0.8.1_0.8.1-2_armhf.deb": "21b065c1587dfa8ca1d8b9cbf0c797c56fa19738b2f697886091305cbdd891e6",
+ "libcolord2_1.4.3-4_armhf.deb": "4eec3912d5e1df92ef8d71a9c72aad2d7cc31c05edde8b583f3ee94c0181fe25",
+ "libcom-err2_1.44.5-1+deb10u2_armhf.deb": "2bc8807d701b7a41f0aed2c6ae869e26424bb66709fa5f217bb93aef8d87dcd6",
+ "libcroco3_0.6.12-3_armhf.deb": "45940fc83e016ab6921a17001125b4fd0743ff37a87d2cf503c97a9b73e50f3b",
+ "libcups2_2.2.10-6+deb10u1_armhf.deb": "f9e40ab35364c65998d4bfb217a1f1db2d1a0520a9c18d089e09b87c444aaa11",
+ "libcurl3-gnutls_7.64.0-4_armhf.deb": "1f52639539ccc0b358c4bac4d4044fe95db30acbaa2d27765c7132632a11047e",
+ "libdap25_3.20.3-1_armhf.deb": "e4a7a8502e233d355eadbcfad793d23a6e2b5dfbfda813aef3b76e91da6178f6",
+ "libdapclient6v5_3.20.3-1_armhf.deb": "beee7cb7642fcfd2d550908ae019a833195eb46ae5e5fac3732ab8208e0626a9",
+ "libdapserver7v5_3.20.3-1_armhf.deb": "cb4b57096e13161c5368db9d2ae868ba377a177a17dbb47503bfc1d022019b6e",
+ "libdatrie1_0.2.12-2_armhf.deb": "8e57fcfce1f6cad89e48332931d3ac3e7d65838ef36108dcb8cb9b8689268704",
+ "libdb5.3_5.3.28+dfsg1-0.5_armhf.deb": "e9bfd3904dfbdab095f24f4e3d2736c1cabd0fc0a13c06239fc795dc3fd394fa",
+ "libdbus-1-3_1.12.16-1_armhf.deb": "8956d26ed5e36da1827b295af1db20c4c3326c2fb6286df0611af1fddadfe148",
+ "libdc1394-22-dev_2.2.5-1_armhf.deb": "e1181d30983e2388f30168a06e347af77f63dadd472d7f10a06c0453c9854492",
+ "libdc1394-22_2.2.5-1_armhf.deb": "dc9d04ccaaf4c9d8e789a81b5eee65f3b7dbc16dedd492106d5494a14cc024f6",
+ "libdconf1_0.30.1-2_armhf.deb": "6ede031759943492bbc180e8d370b68d22279158a73ed692501b0e2347491cde",
+ "libdpkg-perl_1.19.7_all.deb": "1cb272a8168138e9b8334e87cc26388259f232b74667b3a7f3856f227adcc4ba",
+ "libdrm-amdgpu1_2.4.99-1~bpo10~1_armhf.deb": "f432b784cb753da0a763ba3f6bed0f5e74578cc6c1a27de617a27f25ad89391e",
+ "libdrm-common_2.4.99-1~bpo10~1_all.deb": "e48b70787ace4ee4874df9078f28353119af80792c85c76db45aed26a2bbd813",
+ "libdrm-nouveau2_2.4.99-1~bpo10~1_armhf.deb": "11bb0b958997810924afa5eba00dbce4ce5f036409a86b309823c40a32420581",
+ "libdrm-radeon1_2.4.99-1~bpo10~1_armhf.deb": "79d4d288276a852b18e6b02e409c589a6cdca5f9bc712accdc062e60db500b4a",
+ "libdrm2_2.4.99-1~bpo10~1_armhf.deb": "ff95d213633ecaa8450cb9f77b04b34cc5017459c32e42f6ec90138436faafd8",
+ "libedit2_3.1-20181209-1_armhf.deb": "0d35fbdf5952df7a78dbc4776b89361a2fef8006342a94ab9d602483ad3578da",
+ "libelf1_0.176-1.1_armhf.deb": "013c168e64666e33e431701f7b1d683d2234f39caa2d801e389ef69266e88167",
+ "libepoxy0_1.5.3-0.1_armhf.deb": "1c09ff3084a59f3fbb519ca43b3d5d2bd32eb2835ec1b8f5703d02ddfefef2fc",
+ "libepsilon1_0.9.2+dfsg-4_armhf.deb": "22394acdfe3159dbb6a17aca4fa4edc641c1d6f04c5eed09802b10f0cbd24a29",
+ "libevent-2.1-6_2.1.8-stable-4_armhf.deb": "b8bca67f980502877981d8891e751fa0bd879e785c63e2dd25b61ef629442adc",
+ "libevent-core-2.1-6_2.1.8-stable-4_armhf.deb": "24cd3b8e29650bd0e4b4efe6c1d770b1e75df9682c07eb3276fa22eb55276c44",
+ "libevent-pthreads-2.1-6_2.1.8-stable-4_armhf.deb": "cb009ff0d23de8d1de1972b712c210899fd5e4380027d9ac6846d5bb3b7e8c25",
+ "libexif-dev_0.6.21-5.1_armhf.deb": "bc9c03655e49f038b308228d92998677429b63f3aa1e97ef336dbdd0fe6cdbf0",
+ "libexif12_0.6.21-5.1_armhf.deb": "9105590ee18cb2113e8dee17f270ce81d9cdb2199904886278b99c44c661cbfc",
+ "libexpat1_2.2.6-2+deb10u1_armhf.deb": "869f0de1b5548c13e395554f454dcd17398479e12b5b1e7079fd8e5246113365",
+ "libffi6_3.2.1-9_armhf.deb": "dd24f5e9fa15e16c80c8a13869d63f1a1fbef153b63c628d09f9bc4ed513112e",
+ "libfontconfig1_2.13.1-2_armhf.deb": "3c9b6ab7c53742599ba2d43f67181b01b77442c0bd48539466e3a117c555e094",
+ "libfreetype6_2.9.1-3+deb10u1_armhf.deb": "84a520466752a39ac67acd32403fd00b18f41bf5477044e8475d643fdfaefd78",
+ "libfreexl1_1.0.5-3_armhf.deb": "42a5ad5b00b79271a9651cd0fa220e488bc700b691e3e9789b7b0d0c27219a5e",
+ "libfribidi0_1.0.5-3.1+deb10u1_armhf.deb": "c1fd57da1608f48bd699d853f0149e47bb21baa4d7328be5f87fb0f908a5ed54",
+ "libfyba0_4.1.1-6_armhf.deb": "331d150a88b29e2cc16139dc2ba3c1c77ab0fd577be4f2f08de603bbaec0e59b",
+ "libgcrypt20_1.8.4-5_armhf.deb": "19ec0ba3e4d133ade463dedc1ca4f2b37344eab058213cc384ea14488a7272d5",
+ "libgd3_2.2.5-5.2_armhf.deb": "77e8999b903a4b576ae05f3c3776f69a0277a8200857aba6fa3bc8fb290c874c",
+ "libgdal20_2.4.0+dfsg-1+b2_armhf.deb": "7b4f71a576320aecbeadd11dbd507c5a6f7c9c519606bd30efa5e192183112c4",
+ "libgdcm2-dev_2.8.8-9_armhf.deb": "8b27b3ab3e2c265bd92c78aa39bc485f692da499bcf5ed0e2d9ff6b52d5d6eff",
+ "libgdcm2.8_2.8.8-9_armhf.deb": "e07feea0e5724c4ea9ab24af5dae2ba5bc0d3be20d6c9596b09f7067dd037768",
+ "libgdk-pixbuf2.0-0_2.38.1+dfsg-1_armhf.deb": "68dc44a106ef642247847657567890d7f36a4eeed16d2b7d1e7e733a0442a265",
+ "libgdk-pixbuf2.0-common_2.38.1+dfsg-1_all.deb": "1310e3f0258866eb4d0e95f140d5d9025cf6be1e3e2c375f4a426ccc2e78cf68",
+ "libgeos-3.7.1_3.7.1-1_armhf.deb": "2fd2fc54180965df1f3921ced9186de9e97204bc08f05558a48de4fcfcec69e3",
+ "libgeos-c1v5_3.7.1-1_armhf.deb": "6efa1978880f24e97214163972ff29f652ffcb8a2cebff3d17235704e204f57b",
+ "libgeotiff2_1.4.3-1_armhf.deb": "044798114f725f781ec3f2415bdf12bba85c4e762e6a2d93fff0508ab8fa2346",
+ "libgfortran5_8.3.0-6+rpi1_armhf.deb": "a5f5a383d8e617a11316ec335f83ee5bafade9cc7de5c9d83dc79f5c5858f9ad",
+ "libgif7_5.1.4-3_armhf.deb": "b88a0b203bf0f88264dd932ee13a52d28b1e92eb76bfbc7e62a124eae71f9de5",
+ "libgl1-mesa-dri_19.2.0~rc1-1~bpo10+1~rpt3_armhf.deb": "f7291de6f9dfc56a412330f306ae89f809ed7c7d021b7da15347784803a2111f",
+ "libgl1-mesa-glx_19.2.0~rc1-1~bpo10+1~rpt3_armhf.deb": "261e3a9fef4f4f7912c6cf61a09fed47c3c1eeef1d563ae757b879284a0d084e",
+ "libgl1_1.1.0-1_armhf.deb": "7243594e50a2f898a023fc2ec4b17d59e2e71fd415afeafe6dc8af104268c9fe",
+ "libgl2ps1.4_1.4.0+dfsg1-2_armhf.deb": "61aa9ebb7c7205365cf6adb2318b4d8376e30f6dbed9271bdda63135e6d57c37",
+ "libglapi-mesa_19.2.0~rc1-1~bpo10+1~rpt3_armhf.deb": "03ce3b9a80c6eadb3f5dd5ac421c4a45bb6beacb7748f244931343025abd6173",
+ "libglib2.0-0_2.58.3-2+deb10u2_armhf.deb": "6c9edefc08726bc9e63b31e487c022db6b55dd710fe3e022e1a414a17f33328f",
+ "libglu1-mesa_9.0.0-2.1_armhf.deb": "1a3aaf79151e412a4af3316873e8ae5f73a8e78ef7e361b37e49aed186470e91",
+ "libglvnd0_1.1.0-1_armhf.deb": "c449f17f2c86dc60b49161a84414a7c355bd329a49979e4a5ba92e7a1dd37927",
+ "libglx-mesa0_19.2.0~rc1-1~bpo10+1~rpt3_armhf.deb": "787521cf777871c9aa59ddfd563ac428bec27454d91f85248df21d96c559f32f",
+ "libglx0_1.1.0-1_armhf.deb": "c2495916884ddb8d0360fcba429f42463ec03d7935eef24a2f3548b57bb80aa7",
+ "libgme0_0.6.2-1_armhf.deb": "09a4d473e20c1cbd76119c45a42d00fe953ea7a58cda45abaf65897bea82e21d",
+ "libgmp10_6.1.2+dfsg-4_armhf.deb": "ca3cd65e915de80716dd976fd9e6b9588342e39117ec07ac5a00e60bcb1a27df",
+ "libgnutls30_3.6.7-4_armhf.deb": "439dbc35caa9e3baacc7ddf200c60385e69c262c37b20294295c3c2071606812",
+ "libgomp1_8.3.0-6+rpi1_armhf.deb": "243f49f947c8a992ecb8c38a301288661254bc10099d27c98eafd2e05fe88100",
+ "libgpg-error0_1.35-1_armhf.deb": "6549092b313862bf3959fa4a0903a885ff81a777bed2b4925ab85df03588eee2",
+ "libgphoto2-6_2.5.22-3_armhf.deb": "b20dd5e04ecce954151680f4aa35e6f5a2c8c7f09a8bbfc5a769345d99481861",
+ "libgphoto2-dev_2.5.22-3_armhf.deb": "30b98ae9bb8fa42ce16e2231f509bf68c425d1bc81003ef66ed8fda1774fa135",
+ "libgphoto2-port12_2.5.22-3_armhf.deb": "0eae07c8307af1d629d475c9a50570e032941c6ef065b2551684b03dbc0e7c46",
+ "libgraphite2-3_1.3.13-7_armhf.deb": "461cc0fec95f74dae2c031e7c7123774877e8bb4f0341b607d163ee0e58d1186",
+ "libgsm1_1.0.18-2_armhf.deb": "148dd82999418b9d1e70b412b5fe2d2e1d4de7407cc23ec7e2c485a7fd73ef57",
+ "libgssapi-krb5-2_1.17-3_armhf.deb": "63a06b5943840f841aacc34032974f228a3c0023fca05d9b4b6329650390361a",
+ "libgtk-3-0_3.24.5-1+rpt2_armhf.deb": "3ee5f878748e4a6301a631a1fc1e5afad3a0bc07cf5d2485e12511545609732b",
+ "libgtk-3-common_3.24.5-1+rpt2_all.deb": "eb65d4610b98e5927a23ce4f36bfd76e2d53e1303b94c6d2a0c634d8fe4506fd",
+ "libharfbuzz0b_2.3.1-1_armhf.deb": "cb57cfe0e2c3e36a9cdbf032eed11269eeda8ae5c66203fb95c19cc8c2fa1ed0",
+ "libhdf4-0-alt_4.2.13-4+b1_armhf.deb": "df376e0f0413e52cd59ddc937a4c9fde565cc4d5cf56cdc63f9c32c709ac8053",
+ "libhdf5-103_1.10.4+repack-10_armhf.deb": "ebe9eff643cb5e5fb0f78038ee81ae8a7ee29bd2e1d34eeb92452c3c172291ff",
+ "libhdf5-openmpi-103_1.10.4+repack-10_armhf.deb": "eb53ab0db447b08d50f3c8a5d22f8c643f65075ace448efe86d6bea5e194352a",
+ "libhogweed4_3.4.1-1_armhf.deb": "9eafecd38110191e285293a770eb13ef7a291cea2e0ff18cf511c6cf21b947b6",
+ "libhwloc-plugins_1.11.12-3+rpi1_armhf.deb": "5703cbe54214331b879aa8bc07577dc7e4e3c748df6a9c8f89af9e6e6e5cb20d",
+ "libhwloc5_1.11.12-3+rpi1_armhf.deb": "a9c20eeaa0f5abff444a3f12639ccb8554fae05d97cef1840e5de54c7d3c394b",
+ "libibverbs1_22.1-1_armhf.deb": "37aebd2d0c1cffe2b9a8678bbde786ae57b9e04ca8977fce5baa334378e661f7",
+ "libice6_1.0.9-2_armhf.deb": "92374e7e8076ad0062638c7438c886d0c23000b1a8a9b361a057d0625dc20a65",
+ "libicu63_63.1-6_armhf.deb": "94010cc7c8ce2de49ad2dcdf2d70eccb32b890a8d5e9b30ec5ba3ce72f681fdc",
+ "libidn2-0_2.0.5-1_armhf.deb": "4652f117181607de335c7ded31109310934bc40051c6afb7bbdedb9fbbb2b28c",
+ "libilmbase-dev_2.2.1-2_armhf.deb": "bcd411d9f601549cbbb343b672e6ce0be2704c701f2cc6cdbc254cc8a8b61bce",
+ "libilmbase23_2.2.1-2_armhf.deb": "7d8995d3db7cfe4ff6705553d00679682f55cd4021436e7bd2e83bb56d23d8c2",
+ "libjbig-dev_2.1-3.1+b2_armhf.deb": "8324e57714c0e44ed47235ef3510cd4f1acc8b098eb2140b7773935cfdd4a7e6",
+ "libjbig0_2.1-3.1+b2_armhf.deb": "b50783fe5974f648125b6ce2487ba05f99e4f11929f5b75bdc5baa94890a563f",
+ "libjpeg-dev_1.5.2-2_all.deb": "71b42025bdeb9fcc30054b54c84c4306da59466fbd419f46471f15ec54d435aa",
+ "libjpeg62-turbo-dev_1.5.2-2+b1_armhf.deb": "c8b85c158cff2deb164da3e67eba70fa13cfddc40ef7e721eaa4bf0c770f9194",
+ "libjpeg62-turbo_1.5.2-2+b1_armhf.deb": "bc28dbc5b68fe0268aa7692562bb0a39908e1bd0901be1990affd585fec773b3",
+ "libjson-c3_0.12.1+ds-2_armhf.deb": "ca3de6f029fb22f0efb576734f27a97583ebd9b9137b1c7cfd0f6228fae44423",
+ "libjson-glib-1.0-0_1.4.4-2_armhf.deb": "a790c43ed7957d646337df29628b17e812869b1e087a59002f5b1b97a42b400f",
+ "libjson-glib-1.0-common_1.4.4-2_all.deb": "c27dbb0cf9c73e2a09d5c774fb46ecf6d2b634facaf3b37b20a4654d9c549187",
+ "libjsoncpp1_1.7.4-3_armhf.deb": "25674de33c2105228048b9380b71045faf0716e63c3f901f4d9bc67ed4579c8a",
+ "libk5crypto3_1.17-3_armhf.deb": "abcc38ec1ec6f0c84feb2cb14b8a96517957cbcbdc20f6183e7fe3c0e133975c",
+ "libkeyutils1_1.6-6_armhf.deb": "ee0948ea16c2520d5a8612ba74c95c820966ed8dba78334729aef37571161d58",
+ "libkmlbase1_1.3.0-7_armhf.deb": "7ffa17e6e3487fd5745d32416ff82dba541b926b9eaab2e16ac7811a38de2486",
+ "libkmlconvenience1_1.3.0-7_armhf.deb": "4bfcc0187e12a3eef08372c3b8be8205d4eecddaaf4d7467ce29585466bc2365",
+ "libkmldom1_1.3.0-7_armhf.deb": "168b96f0e36b863517afc16ea6a37f00acb20dac80a40ffe2a6039412db0630d",
+ "libkmlengine1_1.3.0-7_armhf.deb": "d1d5df02935b20105d94e9ea8d4d1b186d3592f9197d9bea36d69b2cc2952d80",
+ "libkmlregionator1_1.3.0-7_armhf.deb": "3eba2098651bd33e7a51e7c54f9996ac11f0167c133ce59ddea4415ad7f5cecc",
+ "libkmlxsd1_1.3.0-7_armhf.deb": "820b7705568f69c54b7ac30feb9bc36935aecbbcaac55a801b6675f1bfe1a599",
+ "libkrb5-3_1.17-3_armhf.deb": "eb91711bd2f1606354c27216c89cef3c85d78407902b750ee228018f9134f8a1",
+ "libkrb5support0_1.17-3_armhf.deb": "5b0d26f4a7f8a0991087b917b2a9d93d353c4c9cc18f6a345db45e1c06391564",
+ "liblapack3_3.8.0-2_armhf.deb": "b6b2d62fe5f607efbb383d5b39edffa414a1bdad89cb886a60e0b5ee55c8ecbd",
+ "liblcms2-2_2.9-3_armhf.deb": "6d771698dd7b90af8f53d744775ad0f8a669be7a5ee8bf2c285f7bced0c64822",
+ "libldap-2.4-2_2.4.47+dfsg-3+rpi1+deb10u1_armhf.deb": "b61e759ffe122e843dd2b5117a421fcd344deac94c75b1892e338ab6042ce4a9",
+ "libldap-common_2.4.47+dfsg-3+rpi1+deb10u1_all.deb": "16f2cc9f5faaf9a539697d8adf05c0f460d274d785497aa8027dca6b0e9236d0",
+ "liblept5_1.76.0-1_armhf.deb": "9eb19fa5d74b861bdca63d195e9f23c90f359e6702ab2140df36804d7098f495",
+ "libllvm8_8-3+rpi1_armhf.deb": "2e3a98a357ebccfcc630cdb8a7177ec1561d31c11c5ffaceb5c449a14f817660",
+ "libltdl7_2.4.6-9_armhf.deb": "0109cd8ee5f2216584d21dcbb8b85786d5d38cd3d88fa8602c756088c38ba29a",
+ "liblz4-1_1.8.3-1_armhf.deb": "99661a8b347d55fc0212b8176f691eaee1e13e2ee75aa316c549ac669fe77925",
+ "liblzma-dev_5.2.4-1_armhf.deb": "94c1b419a70af792590eb26582f3ab5fd6908ee0f045ee649c65523503290bd4",
+ "libmariadb3_10.3.17-0+deb10u1_armhf.deb": "d141db7c82826cead85bbc3c8808e9d2148029f87c41b676c425f21cd8397639",
+ "libminizip1_1.1-8+b1_armhf.deb": "7ac58a7fb21b641d00d5485c0068ab4aca024f795ee220eec5ac1501cbfe6b7c",
+ "libmount1_2.33.1-0.1_armhf.deb": "9443056463d7ddedde9bf28e1f2b6486198b68143fa0b7a2688e3edf823d566d",
+ "libmp3lame0_3.100-2+b1_armhf.deb": "1b5334f976afe0a16c0faa29832ff35e6d442beca23062b9f385079a120d4017",
+ "libmpg123-0_1.25.10-2_armhf.deb": "e552789597110f8cc7300ca34578a2e93700db189ee4732c2adce39a339ad617",
+ "libncursesw6_6.1+20181013-2+deb10u2_armhf.deb": "2435c3c7d6f27d907584a36583da629927eec4c2d8e2deff7bc8d814ff2b97b6",
+ "libnetcdf-c++4_4.2-11_armhf.deb": "ddbc876f3a37f78386f7d4611ad9ef095dce27a8dffa95f65a64c10381324d27",
+ "libnetcdf13_4.6.2-1+b1_armhf.deb": "0323f376ec2d0be39683adfdebaba1a0ee062d4387a4b1cd5946b389b6fd0409",
+ "libnettle6_3.4.1-1_armhf.deb": "49010bb7544c086eb20d5330fd1b1bce61bf29f66f0bfe7da5381f1ddcc6abf0",
+ "libnghttp2-14_1.36.0-2+deb10u1_armhf.deb": "3e47c770b48f555decbb31bc38f38b985c8d6009f39f7923c0fc7973bac99348",
+ "libnl-3-200_3.4.0-1_armhf.deb": "61c07f733be04122faa5f86e50138f27b639b10852fa19c5109b63ce7b4f1d8d",
+ "libnl-route-3-200_3.4.0-1_armhf.deb": "3761f4d6c6b255873b5ddf9c615ff9c396f00b212bde9d81cf83a86373316b44",
+ "libnspr4_4.20-1_armhf.deb": "1a5c311c0b2d3de1d53bb8bed8034c475dcd8293319e69f4bea2d765f00c87ee",
+ "libnss3_3.42.1-1+deb10u2_armhf.deb": "bf52021aac6e4c10183f10155fb554286831c75639e766c19c0f4946cde76718",
+ "libodbc1_2.3.6-0.1_armhf.deb": "07ce132f8fc2dab2e11f6988896cfdaf2e865b81da96456f42fde8f5e1e1708f",
+ "libogdi3.2_3.2.1+ds-4_armhf.deb": "f2089377ed36ef36327e8a982ea3fdde736806fd7288be67da19b69a7d1f6bb1",
+ "libogg0_1.3.2-1+b2_armhf.deb": "2518b3214e3c709eb0df6bb71127d1b9e24fc642513f6a8a9e729de98f789d50",
+ "libopencv-calib3d-dev_3.2.0+dfsg-6_armhf.deb": "8c7f3ca52bf148f39483a6e1ed864c36107f5461fed78934b9ae3cecf027e48a",
+ "libopencv-calib3d3.2_3.2.0+dfsg-6_armhf.deb": "a7ce99efec8b00d8db399352a7099ab78153d5aec72fb1f53d3570b6213d1c4c",
+ "libopencv-contrib-dev_3.2.0+dfsg-6_armhf.deb": "b733091fe4bafb84f40b1221af788ec5df7f64108bd15c700575054a9846d1b0",
+ "libopencv-contrib3.2_3.2.0+dfsg-6_armhf.deb": "41ce0e6af765c46d2fbfd9d9b8ecca678537e53ac637dbc28775f26b22b18d49",
+ "libopencv-core-dev_3.2.0+dfsg-6_armhf.deb": "a83d0f59ce1e23cfa2f7c400e669f12b905acbbc9349998b9eab451c78ce91ca",
+ "libopencv-core3.2_3.2.0+dfsg-6_armhf.deb": "d2b7ecda65da3ba6610711dc9ab95f7bc8f90a6dead77ad06f93a082b5ae36f2",
+ "libopencv-dev_3.2.0+dfsg-6_armhf.deb": "aa519c3e572b655f039803117c737bc7c0f0638fbc86ff8989294b5df294a8fb",
+ "libopencv-features2d-dev_3.2.0+dfsg-6_armhf.deb": "b614de275538bcb5a12ed7bc9cf7dc644572b1792cab7f5c821d329fbf05ae54",
+ "libopencv-features2d3.2_3.2.0+dfsg-6_armhf.deb": "2acd7864a39c01528f87b8d34fb5b620004e04cc287200c74ca44e0712a161b2",
+ "libopencv-flann-dev_3.2.0+dfsg-6_armhf.deb": "b81522782181f3d39d48a6b61b3b61fa45388c305ab7952d98fd7b6084314e5d",
+ "libopencv-flann3.2_3.2.0+dfsg-6_armhf.deb": "097da5fec0d3828e7e2de1bd1d38cccf86c3e0f866a94e9ae0f116fe69afdace",
+ "libopencv-highgui-dev_3.2.0+dfsg-6_armhf.deb": "20c31e48c84f7ac8ff74e603aa91453671fe0f13292f5c05370ce7c984eaeb76",
+ "libopencv-highgui3.2_3.2.0+dfsg-6_armhf.deb": "f52232ca0db2aec76bf55605f7268eb3f5969524d0dd4627a5e0c75900655b38",
+ "libopencv-imgcodecs-dev_3.2.0+dfsg-6_armhf.deb": "055d2f33c8b1b20edc0d989a6ff047d0b9eea0347237febbcf27f9380fc1b843",
+ "libopencv-imgcodecs3.2_3.2.0+dfsg-6_armhf.deb": "3a87d0dd4a0d534242394de932526365a9e080f9fe783a1caaaeb72b909762c0",
+ "libopencv-imgproc-dev_3.2.0+dfsg-6_armhf.deb": "0069eb4e75c2133ca12b94f979e5c2ec1c4be31b7ca42e39a90200bbb287f4c8",
+ "libopencv-imgproc3.2_3.2.0+dfsg-6_armhf.deb": "2561604f98264ade28e5ecb31b1e5590fab4c861bb170de41e8533d09dd868c6",
+ "libopencv-ml-dev_3.2.0+dfsg-6_armhf.deb": "f576a5bd460c64914e6091d5229ecf5a021a9541319d5dae472105f2d3c6e3aa",
+ "libopencv-ml3.2_3.2.0+dfsg-6_armhf.deb": "f187fa8c4cb52c982a19c5199b4df4d311bb19a561744e9086a46b4f40de69f9",
+ "libopencv-objdetect-dev_3.2.0+dfsg-6_armhf.deb": "5a1e49091efe0af562cbbc3fcd5035c4770d076503f4266360528b3aacd170ba",
+ "libopencv-objdetect3.2_3.2.0+dfsg-6_armhf.deb": "a908f56acfd787d76ab91bf3e10374c6c34070fe73acf0b58683d15d845bd5ce",
+ "libopencv-photo-dev_3.2.0+dfsg-6_armhf.deb": "6f93654b78007bb54b6294a6294d2ee1b2c8c34f9557f7fb6f51caf8a996680c",
+ "libopencv-photo3.2_3.2.0+dfsg-6_armhf.deb": "2dbb93becd211a9543711c99c32a9f6e7480f554c068a945bd76c20111756207",
+ "libopencv-shape-dev_3.2.0+dfsg-6_armhf.deb": "a56b63c6706af19b3f6e556ee7cac51dd80af48d948f7226bc5d60d4d57dda16",
+ "libopencv-shape3.2_3.2.0+dfsg-6_armhf.deb": "a6906df95067c61b18ebdbe36e30e836752c2a4c1b6a4160b1dc991b347cbe34",
+ "libopencv-stitching-dev_3.2.0+dfsg-6_armhf.deb": "d343a82f4190e0cf10ba72846d01fd38a132f7e22024f8e8ba19171c0a65feab",
+ "libopencv-stitching3.2_3.2.0+dfsg-6_armhf.deb": "e8abf81b53d28ee5e68ae5d6e423a1278e0619b02920efbc247665b5b7bbe497",
+ "libopencv-superres-dev_3.2.0+dfsg-6_armhf.deb": "24511a36a510be5943842931933577334b4e8410ded4be3fe6ad634831b5baba",
+ "libopencv-superres3.2_3.2.0+dfsg-6_armhf.deb": "81e8882fbeeae7d07665c6c07e7885b5f43fdd7d536b7007e298cdeea4acc510",
+ "libopencv-ts-dev_3.2.0+dfsg-6_armhf.deb": "2e9520d3c83ac2ef3af690ff4241c68a8cad067c0cc50d3ec9395ac7d75e29dc",
+ "libopencv-video-dev_3.2.0+dfsg-6_armhf.deb": "c7ae7ced16c7cf6aa1a1647758b0c1cf01e38bf84444dbd3baf568d4328c6f86",
+ "libopencv-video3.2_3.2.0+dfsg-6_armhf.deb": "e6d079903ce88b25558046b4ce94261eafd2fb5de0617e7fcffbbef02521ac59",
+ "libopencv-videoio-dev_3.2.0+dfsg-6_armhf.deb": "f712aa3626fd0efc42240c1b500e05a37f8e05bfab459046a6a1cf2364541127",
+ "libopencv-videoio3.2_3.2.0+dfsg-6_armhf.deb": "ed38ad4aded75bc4b5ee1a2e7acc67fc0a7a0484d9d5fe46e56f5a9edaafeb57",
+ "libopencv-videostab-dev_3.2.0+dfsg-6_armhf.deb": "942519168d7be208736394024612285bacc9c41f66edcce5a33eb86aedae6dfc",
+ "libopencv-videostab3.2_3.2.0+dfsg-6_armhf.deb": "a4dae91092fe9e9e60b2c185d610a15452f7f9df9b9b635e8deaa3b0aa93cbbf",
+ "libopencv-viz-dev_3.2.0+dfsg-6_armhf.deb": "64a9b47eb603860c60fe62f4024b3f23a4df23fe7a0e185090ba730a32ec7fc2",
+ "libopencv-viz3.2_3.2.0+dfsg-6_armhf.deb": "e3a859dc1426c7eddfb181f0e37c8c20bebced557fabf5161de795195c12c9b4",
+ "libopencv3.2-java_3.2.0+dfsg-6_all.deb": "6a177762d8dbe7e2a54cfc03aa523802848e0567ded674314d1919652b07f81b",
+ "libopencv3.2-jni_3.2.0+dfsg-6_armhf.deb": "4fae611a082c059c2344d4f39ad60d3c00c79e243b3e218d6ac5fa5a829c63bb",
+ "libopenexr-dev_2.2.1-4.1_armhf.deb": "fb634227cc4fb67662fd45a8a5e741dd9609a2872a841856cd21c20aa7f4d0e8",
+ "libopenexr23_2.2.1-4.1_armhf.deb": "437f125bc53e5749d32de0625f8aaa4eb3c06df096ce147889cf9bd380876dde",
+ "libopenjp2-7_2.3.0-2_armhf.deb": "0bc6c631f13494dda21f9df8ae87ccd35f7aa3c8945b2ee5014d410633dd7c58",
+ "libopenmpi3_3.1.3-11+rpi1_armhf.deb": "b6d19977698ae4860690574ce43dd947462e41ab96498f6cc557c4a122ad2cb7",
+ "libopenmpt0_0.4.3-1_armhf.deb": "85eb98a60a45992c9345583c5869a124a71e6d9179737bc7ad5597c615b08530",
+ "libopus0_1.3-1_armhf.deb": "69cd56d03aaa51a4d62ad8f98d2ff487ea062bbdfe13e983afcefa99cb0c010e",
+ "libp11-kit0_0.23.15-2_armhf.deb": "56de64f62447b20b4f24f3c1d5cf2036f0971f22e1e820e25ff16b8cf59a7490",
+ "libpam-modules-bin_1.3.1-5+rpt1_armhf.deb": "d10f1ff2fa6e1b486e2d1443793ee86eecaa15db9487f553e659696d4a9c7e01",
+ "libpam-modules_1.3.1-5+rpt1_armhf.deb": "a7294f87afe55e0972ed7bba8269f62226b53824a6e0f25a8348776173be0907",
+ "libpam0g_1.3.1-5+rpt1_armhf.deb": "3f85873f6bda68084c597ccc7ec985cb5406b5335eaf0fd225ecce892d7c24dc",
+ "libpango-1.0-0_1.42.4-7~deb10u1_armhf.deb": "23d2b3f5e3ba20bc858adcd1e1718e1794ab34e7d50050d8af0f22c64d4c2afd",
+ "libpangocairo-1.0-0_1.42.4-7~deb10u1_armhf.deb": "a66aa6ac56c5d0f62d90c3015f2c9d8b6d40bbe00d0e2edc3f7ee14b030ae400",
+ "libpangoft2-1.0-0_1.42.4-7~deb10u1_armhf.deb": "40cd486567b4207f2fe367d704a9ad6224c3e032129df5d6cb625bd3435a3bb8",
+ "libpciaccess0_0.14-1_armhf.deb": "37f01b81f204bfd7ab1ffbd3e4f2ef1355dd0f65167e8081ac3639bf12af912b",
+ "libpixman-1-0_0.36.0-1+rpt1_armhf.deb": "e24b5249c31dcccc246a88df767cc1b05ad47c98d484773f9e18982e1b3c2739",
+ "libpmix2_3.1.2-3_armhf.deb": "dc28717bcaffa242bc81a4e55d37819fdc73d6e204303555cf836f85973ab1e4",
+ "libpng-dev_1.6.36-6_armhf.deb": "91d8e235856389d40018e6a1568cf23c7f22c8a8fb076e9d9515ffec7159a676",
+ "libpng16-16_1.6.36-6_armhf.deb": "e5d547ed5bcc30045e8812602324c41a8e59536bed73d2af0405cbe3b596eb44",
+ "libpoppler82_0.71.0-5_armhf.deb": "78add7ce54ba679fcba6a87545ff99ed4a586c506642982caad8b529f60a6cb6",
+ "libpopt0_1.16-12_armhf.deb": "260b2ba983c6489f86cbfa590316b6e4fa3ba7501bfe9475f00c46fbf3ee76e4",
+ "libpq5_11.5-1+deb10u1_armhf.deb": "6ab0c723546189d39f793a79d7042b2b58d8ee0f349c64e196c29d563122ca68",
+ "libproj13_5.2.0-1_armhf.deb": "01226abbfaf179ce9f19397ee085bced5a29ee89e8af012b817196b8d173a857",
+ "libproxy1v5_0.4.15-5_armhf.deb": "6786d3190e0ab7069b207679d93b9d2f204aeb091aa87cbf0b899902521c7407",
+ "libpsl5_0.20.2-2_armhf.deb": "e4d0c0fc1b232cc3aee36351a474d55e56c45c587edbb4e3b4ce58ef399bdc3e",
+ "libpython2.7-minimal_2.7.16-2+deb10u1_armhf.deb": "e84407a0d58e7dff3adac497db1519dbdeba952a7caabd4f7ea2a14abb0e346d",
+ "libpython2.7-stdlib_2.7.16-2+deb10u1_armhf.deb": "dd2479f925a3da9b605b8dfb5a14ff58642e450252d7d4d99f05ca249c0d0280",
+ "libpython2.7_2.7.16-2+deb10u1_armhf.deb": "d5617bddfb0927d53471aee0ce553f22786fa488725ed09c22c13ffd8d97d884",
+ "libqhull7_2015.2-4_armhf.deb": "498f825e3c31489dc47fb9312110333ebf8bad5f1e1fd850a312fff4694f6a92",
+ "libraspberrypi0_1.20190925+1-1_armhf.deb": "9bd18328b4a040a5da558093e6d458f046372bb278decb28920d21095f7432f7",
+ "libraw1394-11_2.1.2-1+b1_armhf.deb": "e7691347dfdc9096a69068f22a2f88a81f132e1cb0d1619cce89177a79fd02aa",
+ "libraw1394-dev_2.1.2-1+b1_armhf.deb": "23620ea90abf64a75431beb2939a129473fb9de4ab1f6b6fe9a414f85abc7b53",
+ "libreadline7_7.0-5_armhf.deb": "f655bfd17328343631ea6dd3fc7d885184a518fa397881f4d32f2a30b1e8fcb5",
+ "librest-0.7-0_0.8.1-1_armhf.deb": "ac4b777c967ae0f31b6d1ff51c32c8098c9d17e742ebdd2cbaa152b6f375e820",
+ "librsvg2-2_2.44.10-2.1+rpi1_armhf.deb": "9bfade393582432caa8f96868cd2a67b974ff04b9dc94a266e1bf578d14b124b",
+ "librsvg2-common_2.44.10-2.1+rpi1_armhf.deb": "02d96caf56f77643744d9d902c0d413b50224ad1a95757da65ddc2471dbb6cd0",
+ "librtmp1_2.4+20151223.gitfa8646d.1-2_armhf.deb": "c4adf4780f3e19b55fba417a0edc8d0d3b40be6d61c996a23d1a60cc3d1a3980",
+ "libsasl2-2_2.1.27+dfsg-1+b1_armhf.deb": "02b64b1ad87e6ae75095f7cef2b38e7b59888569bed25951830aad2b5aa319e9",
+ "libsasl2-modules-db_2.1.27+dfsg-1+b1_armhf.deb": "c54eee01f398398cb1e8af9c801750f00fbfa0e8c53cba819992e0ac39c4bbcc",
+ "libsemanage-common_2.8-2_all.deb": "fa3c50e11afa9250f823218898084bdefea73c7cd1995ef5ed5e7c12e7b46331",
+ "libsemanage1_2.8-2_armhf.deb": "7f403dccd00375eb91786db9fbea41496833cb9391f78bd6ea1136d83203b325",
+ "libsensors-config_3.5.0-3_all.deb": "a064dbafa1590562e979852aca9802fc10ecfb6fda5403369c903fb38fa9802a",
+ "libsensors5_3.5.0-3_armhf.deb": "695f500a247e8a7762fe044c6fd9081db2e9806818eb4cd0593075de53ad5f5f",
+ "libsepol1_2.8-1_armhf.deb": "9941f76c1d378ed0322cb413e0340455fe812f6b7451cf86a78065b2e5db69ef",
+ "libshine3_3.1.1-2+b1_armhf.deb": "7853acc136660422b7d3423caaf2ed5cb526001bb5064f932dfedf702fb5a35b",
+ "libsm6_1.2.3-1_armhf.deb": "92eccccb771f738c18bec33b38c2bf95a281e216608e91341a7e2dbb1f8703fd",
+ "libsnappy1v5_1.1.7-1_armhf.deb": "8b87dfb35872804edd1fd002383762e593e54e3860123d0089a5b7bb26b8aef9",
+ "libsocket++1_1.12.13-10_armhf.deb": "64af2171d010a98d3cb8ca200ce24738a8fb92ef057f6b4d0c059885bf233531",
+ "libsoup-gnome2.4-1_2.64.2-2_armhf.deb": "f36e6e41e88d1ea5e203fcd1a833ef0198693a2156a6abc4a29baae817746073",
+ "libsoup2.4-1_2.64.2-2_armhf.deb": "6ef422515aa22db398887e4b0efaaeeb980a3e0d27ec1dbe3199a186d6ac19fc",
+ "libsoxr0_0.1.2-3_armhf.deb": "82c3a5098221423a3213cb7754758034e24ff114ca4e8562bf37038efc7e8afd",
+ "libspatialite7_4.3.0a-5+b2_armhf.deb": "26db41b6b1f2fee9a39673e624fe74172cc4a12b4324737dd7c066b2ae205098",
+ "libspeex1_1.2~rc1.2-1+b2_armhf.deb": "a05502ef24e63edcb3410bce0fb654c3d5a8d3129df7cfe60e0e2a330ddbc114",
+ "libsqlite3-0_3.27.2-3_armhf.deb": "09efeaead3ce02fe3b390952a30c2207be518acdcf0210715595c486212dbe53",
+ "libssh-gcrypt-4_0.8.7-1_armhf.deb": "6e8ee452c5c3fede30ee89ab80f95532f63614a76afe3d138213e33986df768b",
+ "libssh2-1_1.8.0-2.1_armhf.deb": "ab4159f8bbd8491349d75231d09bc2fdca61f91756abaa2bac95210cfc21d310",
+ "libssl1.1_1.1.1d-0+deb10u2_armhf.deb": "893e3bfdfa84cfcc48d870a1d26341de408d71cbae0fc74788a9917bb5187910",
+ "libstdc++6_8.3.0-6+rpi1_armhf.deb": "bfc0533cc7d6a4d8adfb62205b39a79ee6df7c2f7c48a1dc6ff15f5af519aed4",
+ "libsuperlu5_5.2.1+dfsg1-4_armhf.deb": "f4d797c904bbedb0ea341bd7667661137004403a86a2a8b3e7d1c2365d08dc35",
+ "libswresample-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "78691ab4c0df8a0d4810f5388958007463c9e1df9e43b218cd70ef5315688ecd",
+ "libswresample3_4.1.4-1+rpt1~deb10u1_armhf.deb": "4bee948872147a2b53ee073d5816a377c827c1f0f23eeaba1d9422fdf920f815",
+ "libswscale-dev_4.1.4-1+rpt1~deb10u1_armhf.deb": "7d294825bd34e57e3831b524a1454bba2402f8b6361e83f89294f7dcfeccfc63",
+ "libswscale5_4.1.4-1+rpt1~deb10u1_armhf.deb": "07ad95a40cd28eb1dfb537c8bd45b45dfe40c35e7aa6f3ab9784d2e3913c76b6",
+ "libsystemd0_241-7~deb10u2+rpi1_armhf.deb": "b05e56f47b281fd84054f8ed6babdb67467f1d4a32c98d79334821d841988cab",
+ "libsz2_1.0.2-1_armhf.deb": "ce5347b6d722e01899fc49a39073da7a16985ceadcf8238985e8109617a2a11b",
+ "libtasn1-6_4.13-3_armhf.deb": "594f82946858a332bfbe55ddb2b10247a52486b8b183fd818231fef8a70ff682",
+ "libtbb-dev_2018~U6-4_armhf.deb": "ff7b27eae8c89056677a0479667448c0a2d8e20f75ed84862ccc183d9739ae7c",
+ "libtbb2_2018~U6-4_armhf.deb": "4ed379b2c64bdc16b6cf1cff7b0b859c125bfc311ebfa933f17c8f6efb8f65af",
+ "libtcl8.6_8.6.9+dfsg-2_armhf.deb": "b0f0b25f4bdbb95020ed1476fbc9a84e9a22b3d5278c0dd3df4a5963b5daf3f1",
+ "libtesseract4_4.0.0-2_armhf.deb": "1f46f21a995d76aa42c83ea6272876292520d04a51936fbd4752811ea5e73be1",
+ "libthai-data_0.1.28-2_all.deb": "267d6b251f77c17fb1415ac0727675cb978c895cc1c77d7540e7133125614366",
+ "libthai0_0.1.28-2_armhf.deb": "dad127d817507db95d10a5010db28cef882b51567d5fae58da97fc7bed55f7ae",
+ "libtheora0_1.1.1+dfsg.1-15_armhf.deb": "92f9de0685e30d293e113072b429651a6b2f783c23ffdbdc430da851e9f48236",
+ "libtiff-dev_4.0.10-4_armhf.deb": "0119ae4eb3003f5aa842daa83044a56629f36970ddbf3ff27eaea1a556657720",
+ "libtiff5_4.0.10-4_armhf.deb": "93bb72344e7663b74d0d4fe19ac216a8386f235d5bd3e39ecda17f4468c489a8",
+ "libtiffxx5_4.0.10-4_armhf.deb": "7cd65c17d98224aae86c800c82ab5a197906b4a195089e5948ff3f58ba4626c4",
+ "libtinfo6_6.1+20181013-2+deb10u2_armhf.deb": "48f25a4a8c6629126aa77d9665030b83867f520e50cf8317249e22d8ec204963",
+ "libtk8.6_8.6.9-2_armhf.deb": "d15d84339d668d91cc78e66122265fbccbb56f2ab5b37f2792f3112e44b9dded",
+ "libtwolame0_0.3.13-4_armhf.deb": "2fc0bb23e5ba08b77fce5651d9c3b536478eebfd00ff8078633187538b8bdb4a",
+ "libudev1_241-7~deb10u2+rpi1_armhf.deb": "a10d8ac4dc6b4fe4296f6e2df732a3e4e1f53fd10179fe73b8ab28182a6628f8",
+ "libunistring2_0.9.10-1_armhf.deb": "7e9a8046fde4a3471e9f5477bdcecd079e423aff2b916791e0d4a224e5a6c999",
+ "liburiparser1_0.9.1-1_armhf.deb": "ed680831b4a4236a27707cd50d4649fd812876eccf1f1bfec772bb9255f65cba",
+ "libusb-1.0-0_1.0.22-2_armhf.deb": "11df519acc304a52083bbcdf018bc842510fa9f6621ac957c0e3e994dc6a1843",
+ "libuuid1_2.33.1-0.1_armhf.deb": "31dd55f3044d29370d22f956aa86965b085a201f133771aed5a44631bf989791",
+ "libva-drm2_2.4.0-1_armhf.deb": "2475f97e6e91b6c5afb81ffa0ec00e57727ab44fcbc0eb6947d4ae3dabecd397",
+ "libva-x11-2_2.4.0-1_armhf.deb": "96a84184a734f4795ff0553b1ccb31c29641024b2967327c121f46dc794d9dd1",
+ "libva2_2.4.0-1_armhf.deb": "f4a11116c295ff059b74f2aab5b0156b6e5de493595ede9ccdca21dd2a0b6d24",
+ "libvdpau1_1.1.1-10_armhf.deb": "174cc3df89c9cce18253b832f896dfe4189b765d7122f3dfe8efc48d4b9f2528",
+ "libvorbis0a_1.3.6-2_armhf.deb": "10c7ef81708ea3382fa08dd9185d7f633788362e08e9d5e7549604d6c54bc33c",
+ "libvorbisenc2_1.3.6-2_armhf.deb": "5274a1593ea161d8a4511e4f771eaf83234cc40a383857209d8f38637dee2489",
+ "libvorbisfile3_1.3.6-2_armhf.deb": "22803a4d65a855683ce59f4d95663b786a75a35c2fff78574bdcd70d113318b5",
+ "libvpx5_1.7.0-3+deb10u1_armhf.deb": "44339d7f9ee6a467524aca298a71009092680ff17af4c50b654a0e4ea081f12b",
+ "libvtk6.3_6.3.0+dfsg2-2+b6_armhf.deb": "6f0a4ea94d410d4543fa1f3345b0481960bae5969405c177212c179a177ccf15",
+ "libwavpack1_5.1.0-6_armhf.deb": "d5f7a739bd2ec74e224d205ef2dd331ced7044f687636922c0c3da6250af94a0",
+ "libwayland-client0_1.16.0-1_armhf.deb": "384c3b3288e9a1ecd1014cdb62aece060b47383cb564a001a056bb78f66b2c09",
+ "libwayland-cursor0_1.16.0-1_armhf.deb": "384fd0dbcd9760d62348b5426f3d3072e582a99fd83218ac9d4a91d1758fd40c",
+ "libwayland-egl1_1.16.0-1_armhf.deb": "6270413558873bd434d112e789796d6cba5e0d8703ae19903db0234db2c71924",
+ "libwebp6_0.6.1-2_armhf.deb": "979fc61f16f7887e4ad602a7df402ed8f12d461fda376fde31de90873920494f",
+ "libwebpmux3_0.6.1-2_armhf.deb": "6237227b67a31609eeaa20c164028447c8db0f07c6aba29da0c0d08d2f758375",
+ "libx11-6_1.6.7-1_armhf.deb": "40450a640133af52c6ca90c150cbb6ff549d3ad0e81c80f8916bc57f6af5d918",
+ "libx11-data_1.6.7-1_all.deb": "eb9e373fa57bf61fe3a3ecb2e869deb639aab5c7a53c90144ce903da255f7431",
+ "libx11-xcb1_1.6.7-1_armhf.deb": "13085f3f991abfab2fd17176c0cd97c9ade0856cd864cdb1d560451ee903b967",
+ "libx264-155_0.155.2917+git0a84d98-2+rpi1_armhf.deb": "307de7bd1053117095523c7b4cfa3ca3843490a6f10023beb77c7201143691ab",
+ "libx265-165_2.9-4_armhf.deb": "aeb74dbd170aee841a1908444e6d6997c81da92fc532c41f3908595ea86dd090",
+ "libxau6_1.0.8-1+b2_armhf.deb": "1d1c083edfc29fa3a0c7539565357fcf2f90488dee919444a155afee59ca85eb",
+ "libxcb-dri2-0_1.13.1-2_armhf.deb": "dd81a9718fec85632b80fbac71f2b03972c1c588ed570f4a6c26b7de15ba0914",
+ "libxcb-dri3-0_1.13.1-2_armhf.deb": "7760da9fec785977eea7a1dad02601d7db1841ee36bdba1d05ee8dfd5c65a11a",
+ "libxcb-glx0_1.13.1-2_armhf.deb": "d787c79efcad262895de9fa662cf7646448c1c447b4c8603daa5ac2e49d56aaa",
+ "libxcb-present0_1.13.1-2_armhf.deb": "00d64156b4710ff5621fa95c33a95d608fb59c22cb293dee26c0a09e701b80b2",
+ "libxcb-render0_1.13.1-2_armhf.deb": "842d08da35fd84d9c52d189bb412fc238ada6391da803f4e8a3bc8f9dddeded0",
+ "libxcb-shm0_1.13.1-2_armhf.deb": "d6d35c9e57153832d88a521eb22acb19639e80003de7f3d9c834162fe8e4b5da",
+ "libxcb-sync1_1.13.1-2_armhf.deb": "3a150594eb919886708a37a3c4ad13383ad798780db9175632fd442510fc436b",
+ "libxcb1_1.13.1-2_armhf.deb": "9be3930e901f475e377dd0b3fb598d785826699be1e0e4cb1b4c24ed0ad3a46d",
+ "libxcomposite1_0.4.4-2_armhf.deb": "8550a66e62a33368988efbf9c77008e3b030a03a21421a96b595584481b15331",
+ "libxcursor1_1.1.15-2_armhf.deb": "c7ac382c659528b58c053a0c552d5cc9f26aded0caf2e2e3fcd602d978380fe4",
+ "libxdamage1_1.1.4-3+b3_armhf.deb": "51339efb637c4a3bf800ed0e605158e330732cd01c9ff6b8de94f2edc5bc9b29",
+ "libxdmcp6_1.1.2-3_armhf.deb": "c871d428ca79b15b31967a8e2f19507f06c4af10bcc29429a903a175634af6e4",
+ "libxerces-c3.2_3.2.2+debian-1+b1_armhf.deb": "df1a22c853bf85b6e9afa79751860c57280406d8b40a098ac3bc8f66eceb3255",
+ "libxext6_1.3.3-1+b2_armhf.deb": "4cff4cba6aae865ca4d5e72061d51c16c87985de0232751afce0d05909c755cc",
+ "libxfixes3_5.0.3-1_armhf.deb": "92ee46160bc288442c8e8cd7e0eb2a4dd24e69904333f49371b703af8a9e1b94",
+ "libxft2_2.3.2-2_armhf.deb": "502631a6a91f4a8fccbde895aeedcb518a54e11987f97d20866c325b2eeef220",
+ "libxi6_1.7.9-1_armhf.deb": "f03478e7a8bcf4c144e46d416fb01e74352bddb57a737f3ce78da308784f9639",
+ "libxinerama1_1.1.4-2_armhf.deb": "fb715bf6feefd3695dbaf963191673864a8f73976aa3f52f1197a551af66010e",
+ "libxkbcommon0_0.8.2-1_armhf.deb": "6a45884e50e7e7438e58b6c8387dfeed5f571b79cc8a3e9dc373ffcd6f4a76de",
+ "libxml2_2.9.4+dfsg1-7+b3_armhf.deb": "2629f83a6a430149ed091098e25e22884fb163df01a1f1a3a19765bd205b1a8b",
+ "libxpm4_3.5.12-1_armhf.deb": "f1a677cb3ef3b45e2262e326af71d939ff67dcd0fa3c7a6741706836900612fd",
+ "libxrandr2_1.5.1-1_armhf.deb": "5668f1bf32b9c1d3fe13a90ffb0a15aa5b6445029d24d1718865c08b08581d8a",
+ "libxrender1_0.9.10-1_armhf.deb": "82343e14e073be48577ae1c2c5f95886bc2dddf9a1966b77ba76a827a8e62e44",
+ "libxshmfence1_1.3-1_armhf.deb": "4c9c872c366037d4535e2b5749f34bae782e19171efec6eaaf8c14c9f2486225",
+ "libxss1_1.2.3-1_armhf.deb": "8ce41b86c573c234016450b188551001f7c7da606f090d865adde9c326e1cbc1",
+ "libxt6_1.1.5-1+b3_armhf.deb": "20e1bfa25f403a7014bb3c096a2140b5a6b4db0d370b30774965fc23bb7db122",
+ "libxvidcore4_1.3.5-1_armhf.deb": "caf1801fb13ee60bdc12235f5cd4138a5479b3769be598d29e1864dd7ffd5160",
+ "libxxf86vm1_1.1.4-1+b2_armhf.deb": "cbe30a767f4decb6203bc09661e877579a8adff99ccf73459c698ad0de8efce7",
+ "libzstd1_1.3.8+dfsg-3+rpi1_armhf.deb": "250e609240c682a90b85f2d90024acc63bd0b3f586699929246c1a5d4ba0458c",
+ "libzvbi-common_0.2.35-16_all.deb": "5eea3f86857626ce4371a8a70ba9ce89f1abfc47ed033d1de12ebc0c7d1dd3ea",
+ "libzvbi0_0.2.35-16_armhf.deb": "b8e412ce669fde535a3250714eda0a446c6791771bb6360f93f676efa3d6376d",
+ "lsb-base_10.2019051400+rpi1_all.deb": "b3e203037786d00dd83a5fa9412c8395090921d373e914cb166b395ee2aedaa4",
+ "mariadb-common_10.3.17-0+deb10u1_all.deb": "43edeb3274f132675447d7375f39dd324495a430b0cddcf608875c22cbd4e9c3",
+ "mime-support_3.62_all.deb": "776efd686af26fa26325450280e3305463b1faef75d82b383bb00da61893d8ca",
+ "mysql-common_5.8+1.0.5_all.deb": "340c68aaf03b9c4372467a907575b6a7c980c6d31f90f1d6abc6707a0630608a",
+ "ocl-icd-libopencl1_2.2.12-2_armhf.deb": "634dd778eb0a073609a773b4af463999be6c77b7a757b270ba2759d52e28f16d",
+ "odbcinst1debian2_2.3.6-0.1_armhf.deb": "a2fa334961f985d37602f2eb8ec2a69338897a8e0cba6438b55d365e06624f4c",
+ "odbcinst_2.3.6-0.1_armhf.deb": "81f2678332309805a18b7120dca0c0d76e29ba4e67cca1a629c100893d65a19c",
+ "passwd_4.5-1.1_armhf.deb": "beae91f59bddfe2ca8bf99a70131263d120ada1bdee6d1b3bb46cf96093c44b3",
+ "perl_5.28.1-6_armhf.deb": "464d3c3c46d40e18ebb233106d83a1855931b01b02bd761e72217b161e87ec48",
+ "pkg-config_0.29-6_armhf.deb": "cd1b397b846e4a8b815be6a8e1edbf9a3f509b924030a008c07f2fa3ddd20911",
+ "proj-data_5.2.0-1_all.deb": "fa7126aa00742ccf75f0e9867b54ea70f733436b97f600bec39408c5d3c54bd2",
+ "raspberrypi-bootloader_1.20190925+1-1_armhf.deb": "f43da527cde12548ac439ca496305bd8d818e0981d1506adb5dd7dea943f7673",
+ "readline-common_7.0-5_all.deb": "153d8a5ddb04044d10f877a8955d944612ec9035f4c73eec99d85a92c3816712",
+ "sensible-utils_0.0.12_all.deb": "2043859f8bf39a20d075bf52206549f90dcabd66665bb9d6837273494fc6a598",
+ "shared-mime-info_1.10-1_armhf.deb": "9cc1069b361b8c229b4e2afa4c5b7014e0258cca867204f2b9d4735cb7941e68",
+ "ttf-bitstream-vera_1.10-8_all.deb": "328def7f581bf94b3b06d21e641f3e5df9a9b2e84e93b4206bc952fe8e80f38a",
+ "tzdata_2019c-0+deb10u1_all.deb": "59b295b0e669af26d494ed2803eb9011408f768a77395db2573e67c388e8a509",
+ "ucf_3.0038+nmu1_all.deb": "d02a82455faab988a52121f37d97c528a4f967ed75e9398e1d8db571398c12f9",
+ "x11-common_7.7+19_all.deb": "221b2e71e0e98b8cafa4fbc674b3fbe293db031c51d35570a3c8cdfb02a5a155",
+ "xkb-data_2.26-2_all.deb": "17d21564c940dd8d89e0a1b69d6fea0144d057e4698902378f5c83500612b779",
+ "zlib1g-dev_1.2.11.dfsg-1_armhf.deb": "51561e557bd16f56e1e28b276184f0a6d82617afce987fc8d5322369ab0da478",
+ "zlib1g_1.1.2.11.dfsg-1_armhf.deb": "408be89eee242d4836aa0fa5ef1bedeae68f6ddb3b13b792a4df0363b09320c4",
+ "libselinux1_2.8-1+b1_armhf.deb": "cc2fee966330b3d362358434ae60fa08dd7dcec81b606f4ac94ce83dd6097a39",
+ "libpcre3_2.8.39-12_armhf.deb": "394b0ce553f25fe1bcca1ab367ac86e374c30688c213f95c50f62d0c9101a9df",
+ "liblzma5_5.2.4-1_armhf.deb": "825babb4ce905472493d6f26a5ec6dfa055447f3a9f4b3418cec9e0d56681f03",
+ "libbz2-1.0_1.0.6-9.2~deb10u1_armhf.deb": "22840e43aa2b48fb0ebe441d7cef8b33380063f722769fe1382574052e214d0e",
+}
diff --git a/frc971/analysis/plot.py b/frc971/analysis/plot.py
index 40feea3..d325bd7 100644
--- a/frc971/analysis/plot.py
+++ b/frc971/analysis/plot.py
@@ -11,6 +11,7 @@
from frc971.analysis.plot_config_pb2 import PlotConfig, Signal
from google.protobuf import text_format
+import numpy as np
import matplotlib
from matplotlib import pyplot as plt
@@ -42,6 +43,39 @@
parsed_json = json.loads(valid_json)
self.data[channel.alias].append((message[0], message[1],
parsed_json))
+ self.calculate_signals()
+
+ def calculate_imu_signals(self):
+ if 'IMU' in self.data:
+ entries = []
+ for entry in self.data['IMU']:
+ accel_x = 'accelerometer_x'
+ accel_y = 'accelerometer_y'
+ accel_z = 'accelerometer_z'
+ msg = entry[2]
+ new_msg = {}
+ if accel_x in msg and accel_y in msg and accel_x in msg:
+ total_acceleration = np.sqrt(
+ msg[accel_x]**2 + msg[accel_y]**2 + msg[accel_z]**2)
+ new_msg['total_acceleration'] = total_acceleration
+ entries.append((entry[0], entry[1], new_msg))
+ if 'CalcIMU' in self.data:
+ raise RuntimeError('CalcIMU is already a member of data.')
+ self.data['CalcIMU'] = entries
+
+ def calculate_signals(self):
+ """Calculate any derived signals for plotting.
+
+ See calculate_imu_signals for an example, but the basic idea is that
+ for any data that is read in from the logfile, we may want to calculate
+ some derived signals--possibly as simple as doing unit conversions,
+ or more complicated version where we do some filtering or the such.
+ The way this will work is that the calculate_* functions will, if the
+ raw data is available, calculate the derived signals and place them into
+ fake "messages" with an alias of "Calc*". E.g., we currently calculate
+ an overall magnitude for the accelerometer readings, which is helpful
+ to understanding how some internal filters work."""
+ self.calculate_imu_signals()
def plot_signal(self, axes: matplotlib.axes.Axes, signal: Signal):
if not signal.channel in self.data:
@@ -56,10 +90,10 @@
# If the value wasn't populated in a given message, fill in
# NaN rather than crashing.
if name in value:
- value = value[name]
+ value = value[name]
else:
- value = float("nan")
- break
+ value = float("nan")
+ break
# Catch NaNs and convert them to floats.
value = float(value)
signal_data.append(value)
@@ -72,7 +106,8 @@
fig = plt.figure()
num_subplots = len(figure_config.axes)
for ii in range(num_subplots):
- axes = fig.add_subplot(num_subplots, 1, ii + 1, sharex=shared_axis)
+ axes = fig.add_subplot(
+ num_subplots, 1, ii + 1, sharex=shared_axis)
shared_axis = shared_axis or axes
axes_config = figure_config.axes[ii]
for signal in axes_config.signal:
diff --git a/frc971/analysis/plot_configs/gyro.pb b/frc971/analysis/plot_configs/gyro.pb
index e398a2e..f246835 100644
--- a/frc971/analysis/plot_configs/gyro.pb
+++ b/frc971/analysis/plot_configs/gyro.pb
@@ -22,6 +22,10 @@
}
axes {
signal {
+ channel: "CalcIMU"
+ field: "total_acceleration"
+ }
+ signal {
channel: "IMU"
field: "accelerometer_x"
}
diff --git a/frc971/control_loops/BUILD b/frc971/control_loops/BUILD
index 17613ab..7d3b915 100644
--- a/frc971/control_loops/BUILD
+++ b/frc971/control_loops/BUILD
@@ -142,6 +142,22 @@
],
)
+cc_test(
+ name = "coerce_goal_test",
+ srcs = [
+ "coerce_goal_test.cc",
+ ],
+ linkopts = [
+ "-lm",
+ ],
+ deps = [
+ ":coerce_goal",
+ "//aos/controls:polytope",
+ "//aos/testing:googletest",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
# TODO(austin): Select isn't working right. We should be able to remove
# logging conditionally with select and have CPU constraints work correctly.
cc_library(
diff --git a/frc971/control_loops/coerce_goal.cc b/frc971/control_loops/coerce_goal.cc
index 311d882..6fdc70f 100644
--- a/frc971/control_loops/coerce_goal.cc
+++ b/frc971/control_loops/coerce_goal.cc
@@ -1,11 +1,8 @@
#include "frc971/control_loops/coerce_goal.h"
#include "Eigen/Dense"
-
#include "aos/controls/polytope.h"
namespace frc971 {
-namespace control_loops {
-
-} // namespace control_loops
+namespace control_loops {} // namespace control_loops
} // namespace frc971
diff --git a/frc971/control_loops/coerce_goal.h b/frc971/control_loops/coerce_goal.h
index 6e927b0..b682b85 100644
--- a/frc971/control_loops/coerce_goal.h
+++ b/frc971/control_loops/coerce_goal.h
@@ -35,14 +35,34 @@
if (is_inside) *is_inside = true;
return R;
}
+ const Scalar norm_K = K.norm();
Eigen::Matrix<Scalar, 2, 1> parallel_vector;
Eigen::Matrix<Scalar, 2, 1> perpendicular_vector;
- perpendicular_vector = K.transpose().normalized();
+ // Calculate a vector that is perpendicular to the line defined by K * x = w.
+ perpendicular_vector = K.transpose() / norm_K;
+ // Calculate a vector that is parallel to the line defined by K * x = w.
parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0);
- Eigen::Matrix<Scalar, 4, 1> projectedh = region.static_H() * parallel_vector;
- Eigen::Matrix<Scalar, 4, 1> projectedk =
- region.static_k() - region.static_H() * perpendicular_vector * w;
+ // Calculate the location along the K x = w line where each boundary of the
+ // polytope would intersect.
+ // I.e., we want to calculate the distance along the K x = w line, as
+ // represented by
+ // parallel_vector * dist + perpendicular_vector * w / norm(K),
+ // such that it intersects with H_i * x = k_i.
+ // This gives us H_i * (parallel * dist + perp * w / norm(K)) = k_i.
+ // dist = (k_i - H_i * perp * w / norm(K)) / (H_i * parallel)
+ // projectedh is the numerator, projectedk is the denominator.
+ // The case where H_i * parallel is zero indicates a scenario where the given
+ // boundary of the polytope is parallel to the line, and so there is no
+ // meaningful value of dist to return.
+ // Note that the sign of projectedh will also indicate whether the distance is
+ // an upper or lower bound. If since valid points satisfy H_i * x < k_i, then
+ // if H_i * parallel is less than zero, then dist will be a lower bound and
+ // if it is greater than zero, then dist will be an upper bound.
+ const Eigen::Matrix<Scalar, 4, 1> projectedh =
+ region.static_H() * parallel_vector;
+ const Eigen::Matrix<Scalar, 4, 1> projectedk =
+ region.static_k() - region.static_H() * perpendicular_vector * w / norm_K;
Scalar min_boundary = ::std::numeric_limits<Scalar>::lowest();
Scalar max_boundary = ::std::numeric_limits<Scalar>::max();
@@ -50,7 +70,7 @@
if (projectedh(i, 0) > 0) {
max_boundary =
::std::min(max_boundary, projectedk(i, 0) / projectedh(i, 0));
- } else {
+ } else if (projectedh(i, 0) != 0) {
min_boundary =
::std::max(min_boundary, projectedk(i, 0) / projectedh(i, 0));
}
@@ -60,11 +80,16 @@
vertices << max_boundary, min_boundary;
if (max_boundary > min_boundary) {
+ // The line goes through the region (if the line just intersects a single
+ // vertex, then we fall through to the next clause), but R is not
+ // inside the region. The returned point will be one of the two points where
+ // the line intersects the edge of the region.
Scalar min_distance_sqr = 0;
Eigen::Matrix<Scalar, 2, 1> closest_point;
for (int i = 0; i < vertices.innerSize(); i++) {
Eigen::Matrix<Scalar, 2, 1> point;
- point = parallel_vector * vertices(0, i) + perpendicular_vector * w;
+ point =
+ parallel_vector * vertices(0, i) + perpendicular_vector * w / norm_K;
const Scalar length = (R - point).squaredNorm();
if (i == 0 || length < min_distance_sqr) {
closest_point = point;
@@ -74,6 +99,8 @@
if (is_inside) *is_inside = true;
return closest_point;
} else {
+ // The line does not pass through the region; identify the vertex closest to
+ // the line.
Eigen::Matrix<Scalar, 2, 4> region_vertices = region.StaticVertices();
#ifdef __linux__
CHECK_GT(reinterpret_cast<ssize_t>(region_vertices.outerSize()), 0);
@@ -83,8 +110,11 @@
Scalar min_distance = INFINITY;
int closest_i = 0;
for (int i = 0; i < region_vertices.outerSize(); i++) {
+ // Calculate the distance of the vertex from the line. The closest vertex
+ // will be the one to return.
const Scalar length = ::std::abs(
- (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0));
+ (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0) -
+ w);
if (i == 0 || length < min_distance) {
closest_i = i;
min_distance = length;
diff --git a/frc971/control_loops/coerce_goal_test.cc b/frc971/control_loops/coerce_goal_test.cc
new file mode 100644
index 0000000..ae04d6c
--- /dev/null
+++ b/frc971/control_loops/coerce_goal_test.cc
@@ -0,0 +1,217 @@
+#include "frc971/control_loops/coerce_goal.h"
+
+#include <unistd.h>
+
+#include "aos/controls/polytope.h"
+#include "gtest/gtest.h"
+
+namespace frc971 {
+namespace control_loops {
+
+namespace {
+
+aos::controls::HVPolytope<2, 4, 4> MakeBox(double x1_min, double x1_max,
+ double x2_min, double x2_max) {
+ Eigen::Matrix<double, 4, 2> box_H;
+ box_H << /*[[*/ 1.0, 0.0 /*]*/,
+ /*[*/ -1.0, 0.0 /*]*/,
+ /*[*/ 0.0, 1.0 /*]*/,
+ /*[*/ 0.0, -1.0 /*]]*/;
+ Eigen::Matrix<double, 4, 1> box_k;
+ box_k << /*[[*/ x1_max /*]*/,
+ /*[*/ -x1_min /*]*/,
+ /*[*/ x2_max /*]*/,
+ /*[*/ -x2_min /*]]*/;
+ aos::controls::HPolytope<2> t_poly(box_H, box_k);
+ return aos::controls::HVPolytope<2, 4, 4>(t_poly.H(), t_poly.k(),
+ t_poly.Vertices());
+}
+} // namespace
+
+class CoerceGoalTest : public ::testing::Test {
+ public:
+ void SetUp() override { aos::controls::HPolytope<2>::Init(); }
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+
+TEST_F(CoerceGoalTest, Inside) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << /*[[*/ 1, -1 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << /*[[*/ 1.5, 1.5 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(R(0, 0), output(0, 0));
+ EXPECT_EQ(R(1, 0), output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, LineOutside) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ // Make a line equivalent to y = -x, which does not pass through the box and
+ // is nearest the box at (1, 1).
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << /*[[*/ 0.0, 0.0 /*]]*/;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+
+ // Test the same line, but on the other side of the box, where the (2, 2)
+ // vertex will be closest.
+ output = frc971::control_loops::CoerceGoal<double>(box, K, 5, R);
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, GoalOutsideLineInsideThroughOrigin) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, GoalOutsideLineNotThroughOrigin) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 0, 3;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 3, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_DOUBLE_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, GoalOutsideLineThroughVertex) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 1, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, LineAndGoalOutside) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(3, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, -1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(3.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, LineThroughEdgeOfBox) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(0, 4, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << -1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(2.0, output(0, 0));
+ EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, PerpendicularLine) {
+ aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
+
+ Eigen::Matrix<double, 1, 2> K;
+ K << 1, 1;
+
+ Eigen::Matrix<double, 2, 1> R;
+ R << 5, 5;
+
+ Eigen::Matrix<double, 2, 1> output =
+ frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
+
+ EXPECT_EQ(1.0, output(0, 0));
+ EXPECT_EQ(1.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, WithinRegion) {
+ const auto upoly = MakeBox(-12.0, 12.0, -12.0, 12.0);
+ Eigen::Matrix<double, 1, 2> k;
+ k << 2, 2;
+
+ Eigen::Matrix<double, 2, 1> goal;
+ goal << -2, 2;
+
+ auto result = CoerceGoal<double>(upoly, k, 0, goal);
+
+ EXPECT_EQ(result(0, 0), goal(0, 0));
+ EXPECT_EQ(result(1, 0), goal(1, 0));
+}
+
+TEST_F(CoerceGoalTest, VerticalLine) {
+ const auto upoly = MakeBox(-12.0, 12.0, -12.0, 12.0);
+ Eigen::Matrix<double, 1, 2> k;
+ k << 2, 0;
+
+ Eigen::Matrix<double, 2, 1> goal;
+ goal << 0, 13;
+
+ auto result = CoerceGoal<double>(upoly, k, 0, goal);
+
+ EXPECT_EQ(result(0, 0), 0);
+ EXPECT_EQ(result(1, 0), 12);
+}
+
+TEST_F(CoerceGoalTest, HorizontalLine) {
+ const auto upoly = MakeBox(-12.0, 12.0, -12.0, 12.0);
+ Eigen::Matrix<double, 1, 2> k;
+ k << 0, 2;
+
+ Eigen::Matrix<double, 2, 1> goal;
+ goal << 13, 2;
+
+ auto result = CoerceGoal<double>(upoly, k, 0, goal);
+
+ EXPECT_EQ(result(0, 0), 12);
+ EXPECT_EQ(result(1, 0), 0);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/drivetrain/BUILD b/frc971/control_loops/drivetrain/BUILD
index f263d14..6f46965 100644
--- a/frc971/control_loops/drivetrain/BUILD
+++ b/frc971/control_loops/drivetrain/BUILD
@@ -642,3 +642,31 @@
"arm": [],
}),
)
+
+cc_library(
+ name = "improved_down_estimator",
+ srcs = [
+ "improved_down_estimator.cc",
+ ],
+ hdrs = [
+ "improved_down_estimator.h",
+ ],
+ deps = [
+ "//frc971/control_loops:runge_kutta",
+ "@com_github_google_glog//:glog",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
+cc_test(
+ name = "improved_down_estimator_test",
+ srcs = [
+ "improved_down_estimator_test.cc",
+ ],
+ deps = [
+ "//aos/testing:googletest",
+ "//aos/testing:random_seed",
+ "//frc971/control_loops/drivetrain:improved_down_estimator",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
diff --git a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
index 3b668dd..a289fed 100644
--- a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
+++ b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -1414,109 +1414,6 @@
EXPECT_EQ(1.0, localizer_.theta());
}
-::aos::controls::HVPolytope<2, 4, 4> MakeBox(double x1_min, double x1_max,
- double x2_min, double x2_max) {
- Eigen::Matrix<double, 4, 2> box_H;
- box_H << /*[[*/ 1.0, 0.0 /*]*/,
- /*[*/ -1.0, 0.0 /*]*/,
- /*[*/ 0.0, 1.0 /*]*/,
- /*[*/ 0.0, -1.0 /*]]*/;
- Eigen::Matrix<double, 4, 1> box_k;
- box_k << /*[[*/ x1_max /*]*/,
- /*[*/ -x1_min /*]*/,
- /*[*/ x2_max /*]*/,
- /*[*/ -x2_min /*]]*/;
- ::aos::controls::HPolytope<2> t_poly(box_H, box_k);
- return ::aos::controls::HVPolytope<2, 4, 4>(t_poly.H(), t_poly.k(),
- t_poly.Vertices());
-}
-
-class CoerceGoalTest : public ::testing::Test {
- public:
- EIGEN_MAKE_ALIGNED_OPERATOR_NEW
-};
-
-// WHOOOHH!
-TEST_F(CoerceGoalTest, Inside) {
- ::aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
-
- Eigen::Matrix<double, 1, 2> K;
- K << /*[[*/ 1, -1 /*]]*/;
-
- Eigen::Matrix<double, 2, 1> R;
- R << /*[[*/ 1.5, 1.5 /*]]*/;
-
- Eigen::Matrix<double, 2, 1> output =
- ::frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
-
- EXPECT_EQ(R(0, 0), output(0, 0));
- EXPECT_EQ(R(1, 0), output(1, 0));
-}
-
-TEST_F(CoerceGoalTest, Outside_Inside_Intersect) {
- ::aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
-
- Eigen::Matrix<double, 1, 2> K;
- K << 1, -1;
-
- Eigen::Matrix<double, 2, 1> R;
- R << 5, 5;
-
- Eigen::Matrix<double, 2, 1> output =
- ::frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
-
- EXPECT_EQ(2.0, output(0, 0));
- EXPECT_EQ(2.0, output(1, 0));
-}
-
-TEST_F(CoerceGoalTest, Outside_Inside_no_Intersect) {
- ::aos::controls::HVPolytope<2, 4, 4> box = MakeBox(3, 4, 1, 2);
-
- Eigen::Matrix<double, 1, 2> K;
- K << 1, -1;
-
- Eigen::Matrix<double, 2, 1> R;
- R << 5, 5;
-
- Eigen::Matrix<double, 2, 1> output =
- ::frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
-
- EXPECT_EQ(3.0, output(0, 0));
- EXPECT_EQ(2.0, output(1, 0));
-}
-
-TEST_F(CoerceGoalTest, Middle_Of_Edge) {
- ::aos::controls::HVPolytope<2, 4, 4> box = MakeBox(0, 4, 1, 2);
-
- Eigen::Matrix<double, 1, 2> K;
- K << -1, 1;
-
- Eigen::Matrix<double, 2, 1> R;
- R << 5, 5;
-
- Eigen::Matrix<double, 2, 1> output =
- ::frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
-
- EXPECT_EQ(2.0, output(0, 0));
- EXPECT_EQ(2.0, output(1, 0));
-}
-
-TEST_F(CoerceGoalTest, PerpendicularLine) {
- ::aos::controls::HVPolytope<2, 4, 4> box = MakeBox(1, 2, 1, 2);
-
- Eigen::Matrix<double, 1, 2> K;
- K << 1, 1;
-
- Eigen::Matrix<double, 2, 1> R;
- R << 5, 5;
-
- Eigen::Matrix<double, 2, 1> output =
- ::frc971::control_loops::CoerceGoal<double>(box, K, 0, R);
-
- EXPECT_EQ(1.0, output(0, 0));
- EXPECT_EQ(1.0, output(1, 0));
-}
-
// TODO(austin): Make sure the profile reset code when we disable works.
} // namespace testing
diff --git a/frc971/control_loops/drivetrain/improved_down_estimator.cc b/frc971/control_loops/drivetrain/improved_down_estimator.cc
new file mode 100644
index 0000000..40dc8e3
--- /dev/null
+++ b/frc971/control_loops/drivetrain/improved_down_estimator.cc
@@ -0,0 +1,287 @@
+#include "frc971/control_loops/drivetrain/improved_down_estimator.h"
+
+#include "Eigen/Dense"
+#include "Eigen/Geometry"
+
+namespace frc971 {
+namespace control_loops {
+namespace drivetrain {
+
+Eigen::Matrix<double, 4, 1> ToQuaternionFromRotationVector(
+ const Eigen::Matrix<double, 3, 1> &X, const double max_angle_cap) {
+ const double unclipped_angle = X.norm();
+ const double angle_scalar =
+ (unclipped_angle > max_angle_cap) ? max_angle_cap / unclipped_angle : 1.0;
+ const double angle = unclipped_angle * angle_scalar;
+ const double half_angle = angle * 0.5;
+
+ const double half_angle_squared = half_angle * half_angle;
+
+ // sin(x)/x = 1
+ double sinx_x = 1.0;
+
+ // - x^2/3!
+ double value = half_angle_squared / 6.0;
+ sinx_x -= value;
+
+ // + x^4/5!
+ value = value * half_angle_squared / 20.0;
+ sinx_x += value;
+
+ // - x^6/7!
+ value = value * half_angle_squared / (6.0 * 7.0);
+ sinx_x -= value;
+
+ // + x^8/9!
+ value = value * half_angle_squared / (8.0 * 9.0);
+ sinx_x += value;
+
+ // - x^10/11!
+ value = value * half_angle_squared / (10.0 * 11.0);
+ sinx_x -= value;
+
+ // + x^12/13!
+ value = value * half_angle_squared / (12.0 * 13.0);
+ sinx_x += value;
+
+ // - x^14/15!
+ value = value * half_angle_squared / (14.0 * 15.0);
+ sinx_x -= value;
+
+ // + x^16/17!
+ value = value * half_angle_squared / (16.0 * 17.0);
+ sinx_x += value;
+
+ // To plot the residual in matplotlib, run:
+ // import numpy
+ // import scipy
+ // from matplotlib import pyplot
+ // x = numpy.arange(-numpy.pi, numpy.pi, 0.01)
+ // pyplot.plot(x, 1 - x**2 / scipy.misc.factorial(3) +
+ // x**4 / scipy.misc.factorial(5) -
+ // x**6 / scipy.misc.factorial(7) +
+ // x**8 / scipy.misc.factorial(9) -
+ // x ** 10 / scipy.misc.factorial(11) +
+ // x ** 12 / scipy.misc.factorial(13) -
+ // x ** 14 / scipy.misc.factorial(15) +
+ // x ** 16 / scipy.misc.factorial(17) -
+ // numpy.sin(x) / x)
+
+ const double scalar = sinx_x * 0.5;
+
+ Eigen::Matrix<double, 4, 1> result;
+ result.block<3, 1>(0, 0) = X * scalar * angle_scalar;
+ result(3, 0) = std::cos(half_angle);
+ return result;
+}
+
+inline Eigen::Matrix<double, 4, 1> MaybeFlipX(
+ const Eigen::Matrix<double, 4, 1> &X) {
+ if (X(3, 0) < 0.0) {
+ return -X;
+ } else {
+ return X;
+ }
+}
+
+Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
+ const Eigen::Matrix<double, 4, 1> &X) {
+ // TODO(austin): Verify we still need it.
+ const Eigen::Matrix<double, 4, 1> corrected_X = MaybeFlipX(X);
+ const double half_angle =
+ std::atan2(corrected_X.block<3, 1>(0, 0).norm(), corrected_X(3, 0));
+
+ const double half_angle_squared = half_angle * half_angle;
+
+ // TODO(austin): We are doing a division at the end of this. Do the taylor
+ // series expansion of x/sin(x) instead to avoid this.
+
+ // sin(x)/x = 1
+ double sinx_x = 1.0;
+
+ // - x^2/3!
+ double value = half_angle_squared / 6.0;
+ sinx_x -= value;
+
+ // + x^4/5!
+ value = value * half_angle_squared / 20.0;
+ sinx_x += value;
+
+ // - x^6/7!
+ value = value * half_angle_squared / (6.0 * 7.0);
+ sinx_x -= value;
+
+ // + x^8/9!
+ value = value * half_angle_squared / (8.0 * 9.0);
+ sinx_x += value;
+
+ // - x^10/11!
+ value = value * half_angle_squared / (10.0 * 11.0);
+ sinx_x -= value;
+
+ // + x^12/13!
+ value = value * half_angle_squared / (12.0 * 13.0);
+ sinx_x += value;
+
+ // - x^14/15!
+ value = value * half_angle_squared / (14.0 * 15.0);
+ sinx_x -= value;
+
+ // + x^16/17!
+ value = value * half_angle_squared / (16.0 * 17.0);
+ sinx_x += value;
+
+ const double scalar = 2.0 / sinx_x;
+
+ return corrected_X.block<3, 1>(0, 0) * scalar;
+}
+
+// States are X_hat_bar (position estimate) and P (Covariance)
+
+void QuaternionUkf::Predict(const Eigen::Matrix<double, 3, 1> &U,
+ const Eigen::Matrix<double, 3, 1> &measurement) {
+ // Compute the sigma points.
+ // Our system is pretty linear. The traditional way of dealing with process
+ // noise is to augment your state vector with the mean of the process noise,
+ // and augment your covariance matrix with the covariance of your process
+ // noise. Sigma points are then computed. These points are then propegated
+ // through the model. This ends up effectively meaning that perturbations
+ // from the unaugmented state with covariance P are propegated through the
+ // model, and points which are at the mean but with perturbations to simulated
+ // process noise are propegated through the system. The covariance is then
+ // calculated from this set of points, and works out to have a covariance of
+ // essentially P + Q.
+ //
+ // Since our noise is just additive, and quaternian rotation preserves
+ // distance, we can add our noise first and it'll be a good representation of
+ // our distance. This will reduce the number of math operations we need to
+ // do. If we break this assumption in the future by adding a nonlinear model
+ // somewhere in this system, we'll have to revisit this assumption.
+
+ // Now, compute the actual sigma points using the columns of S as the
+ // pertubation vectors. The last point is the original mean.
+ const Eigen::Matrix<double, 4, 3 * 2 + 1> X =
+ GenerateSigmaPoints(X_hat_, P_ + Q_);
+
+ // Now, compute Y, the sigma points which have been propegated forwards by the
+ // model.
+ Eigen::Matrix<double, 4, 3 * 2 + 1> Y;
+ for (int i = 0; i < Y.cols(); ++i) {
+ // Y = Transformed sigma points
+ Y.col(i) = A(X.col(i), U);
+ }
+
+ // We now have the sigma points after the model update.
+ // Compute the mean of the transformed sigma point
+ X_hat_ = Eigen::Quaternion<double>(QuaternionMean(Y));
+
+ // And the covariance.
+ Eigen::Matrix<double, 3, 2 * 3 + 1> Wprime;
+ Eigen::Matrix<double, 3, 3> P_prior =
+ ComputeQuaternionCovariance(X_hat_, Y, &Wprime);
+
+ // If the only obvious acceleration is that due to gravity, then accept the
+ // measurement.
+ constexpr double kUseAccelThreshold = 0.1;
+ if (std::abs(measurement.squaredNorm() - 1.0) < kUseAccelThreshold) {
+ P_ = P_prior;
+ return;
+ }
+
+ // TODO(austin): Maybe re-calculate the sigma points here before transforming
+ // them? Otherwise we can't cleanly decouple the model and measurement updates.
+
+ // Apply the measurement transform to all the sigma points to get a
+ // representation of the distribution of the measurement.
+ Eigen::Matrix<double, kNumMeasurements, 3 * 2 + 1> Z;
+ Z_hat_.setZero();
+ for (int i = 0; i < Z.cols(); ++i) {
+ Z.col(i) = H(Y.col(i));
+
+ // Compute the mean in addition.
+ Z_hat_ += Z.col(i) / Z.cols();
+ }
+
+ // Now compute the measurement covariance.
+ Eigen::Matrix<double, 3, 3> P_zz;
+ P_zz.setZero();
+ Eigen::Matrix<double, 3, 2 * 3 + 1> Zprime;
+ for (int i = 0; i < 7; ++i) {
+ // Compute the error vector for each sigma point.
+ Eigen::Matrix<double, 3, 1> Zprimei = Z.col(i) - Z_hat_;
+
+ // Now, compute the contribution of this sigma point to P_zz.
+ P_zz += 1.0 / 12.0 * Zprimei * Zprimei.transpose();
+ // Save the error for the cross-correlation matrix.
+ Zprime.col(i) = Zprimei;
+ }
+
+ // Compute the measurement error and innovation uncertanty.
+ const Eigen::Matrix<double, kNumMeasurements, kNumMeasurements> P_vv =
+ P_zz + R_;
+
+ // Now compute the cross correlation matrix P_xz.
+ Eigen::Matrix<double, 3, 3> P_xz;
+ P_xz.setZero();
+ for (int i = 0; i < 7; ++i) {
+ // Now, compute the contribution of this sigma point to P_prior.
+ P_xz += 1.0 / 12.0 * Wprime.col(i) * Zprime.col(i).transpose();
+ }
+
+ // Compute the kalman gain.
+ const Eigen::Matrix<double, 3, kNumMeasurements> K =
+ P_xz * P_vv.inverse();
+
+ // Update X_hat and the covariance P
+ X_hat_ = X_hat_ * Eigen::Quaternion<double>(ToQuaternionFromRotationVector(
+ K * (measurement - Z_hat_)));
+ P_ = P_prior - K * P_vv * K.transpose();
+}
+
+Eigen::Matrix<double, 3, 3> ComputeQuaternionCovariance(
+ const Eigen::Quaternion<double> &mean,
+ const Eigen::Matrix<double, 4, 7> &points,
+ Eigen::Matrix<double, 3, 7> *residual) {
+ Eigen::Matrix<double, 3, 3> P_prior;
+ P_prior.setZero();
+
+ for (int i = 0; i < 7; ++i) {
+ // Compute the error vector for each sigma point.
+ Eigen::Matrix<double, 3, 1> Wprimei = ToRotationVectorFromQuaternion(
+ Eigen::Quaternion<double>(mean).conjugate() *
+ Eigen::Quaternion<double>(points.col(i)));
+ // Now, compute the contribution of this sigma point to P_prior.
+ P_prior += 1.0 / 6.0 * (Wprimei * Wprimei.transpose());
+ // Save the error for the cross-correlation matrix.
+ residual->col(i) = Wprimei;
+ }
+ return P_prior / 2.0;
+}
+
+Eigen::Matrix<double, 4, 3 * 2 + 1> GenerateSigmaPoints(
+ const Eigen::Quaternion<double> &mean,
+ const Eigen::Matrix<double, 3, 3> &covariance) {
+ // Take the matrix square root.
+ Eigen::Matrix<double, 3, 3> S = covariance.llt().matrixL();
+
+ S *= std::sqrt(2.0 * 3.0);
+ // TODO(austin): Make sure the sigma points aren't outside +- PI/2.0.
+ // Otherwise they wrap on themselves and we get a mess.
+
+ // Now, compute the actual sigma points using the columns of S as the
+ // pertubation vectors. The last point is the original mean.
+ Eigen::Matrix<double, 4, 3 * 2 + 1> X;
+ for (int i = 0; i < 3; ++i) {
+ Eigen::Quaternion<double> perturbation(
+ ToQuaternionFromRotationVector(S.col(i), M_PI_2));
+
+ X.col(i * 2) = (mean * perturbation).coeffs();
+ X.col(i * 2 + 1) = (mean * perturbation.conjugate()).coeffs();
+ }
+ X.col(6) = mean.coeffs();
+ return X;
+}
+
+} // namespace drivetrain
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/drivetrain/improved_down_estimator.h b/frc971/control_loops/drivetrain/improved_down_estimator.h
new file mode 100644
index 0000000..2b47f9e
--- /dev/null
+++ b/frc971/control_loops/drivetrain/improved_down_estimator.h
@@ -0,0 +1,219 @@
+#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_IMPROVED_DOWN_ESTIMATOR_H_
+#define FRC971_CONTROL_LOOPS_DRIVETRAIN_IMPROVED_DOWN_ESTIMATOR_H_
+
+#include "Eigen/Dense"
+#include "Eigen/Geometry"
+
+#include "frc971/control_loops/runge_kutta.h"
+#include "glog/logging.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace drivetrain {
+
+// Function to compute the quaternion average of a bunch of quaternions. Each
+// column in the input matrix is a quaternion (optionally scaled by it's
+// weight).
+template <int SM>
+Eigen::Matrix<double, 4, 1> QuaternionMean(
+ Eigen::Matrix<double, 4, SM> input) {
+ // Algorithm to compute the average of a bunch of quaternions:
+ // http://www.acsu.buffalo.edu/~johnc/ave_quat07.pdf
+
+ Eigen::Matrix<double, 4, 4> m = input * input.transpose();
+
+ Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>> solver;
+ solver.compute(m);
+
+ Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvectorsType
+ eigenvectors = solver.eigenvectors();
+ Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvalueType eigenvalues =
+ solver.eigenvalues();
+
+ int max_index = 0;
+ double max_eigenvalue = 0.0;
+ for (int i = 0; i < 4; ++i) {
+ const double eigenvalue = std::abs(eigenvalues(i, 0));
+ if (eigenvalue > max_eigenvalue) {
+ max_eigenvalue = eigenvalue;
+ max_index = i;
+ }
+ }
+
+ // Assume that there shouldn't be any imaginary components to the eigenvector.
+ // I can't prove this is true, but everyone else seems to assume it...
+ // TODO(james): Handle this more rigorously.
+ for (int i = 0; i < 4; ++i) {
+ CHECK_LT(eigenvectors(i, max_index).imag(), 1e-4)
+ << eigenvectors(i, max_index);
+ }
+ return eigenvectors.col(max_index).real().normalized();
+}
+
+// Converts from a quaternion to a rotation vector, where the rotation vector's
+// direction represents the axis to rotate around and its magnitude represents
+// the number of radians to rotate.
+Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
+ const Eigen::Matrix<double, 4, 1> &X);
+
+inline Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
+ const Eigen::Quaternion<double> &X) {
+ return ToRotationVectorFromQuaternion(X.coeffs());
+};
+
+// Converts from a rotation vector to a quaternion. If you supply max_angle_cap,
+// then the rotation vector's magnitude will be clipped to be no more than
+// max_angle_cap before being converted to a quaternion.
+Eigen::Matrix<double, 4, 1> ToQuaternionFromRotationVector(
+ const Eigen::Matrix<double, 3, 1> &X,
+ const double max_angle_cap = std::numeric_limits<double>::infinity());
+
+// Generates the sigma points to use in the UKF given the current estimate and
+// covariance.
+Eigen::Matrix<double, 4, 3 * 2 + 1> GenerateSigmaPoints(
+ const Eigen::Quaternion<double> &mean,
+ const Eigen::Matrix<double, 3, 3> &covariance);
+
+// Computes the covariance of the noise given the mean and the transformed sigma
+// points. The residual corresponds with the W' variable from the original
+// paper.
+Eigen::Matrix<double, 3, 3> ComputeQuaternionCovariance(
+ const Eigen::Quaternion<double> &mean,
+ const Eigen::Matrix<double, 4, 7> &points,
+ Eigen::Matrix<double, 3, 7> *residual);
+
+// This class provides a quaternion-based Kalman filter for estimating
+// orientation using a 3-axis gyro and 3-axis accelerometer. It does leave open
+// the option of overridding the system process model and the function used to
+// calculate the expected measurement (which is relevant if, e.g., the IMU is
+// not mounted horizontally in the robot).
+class QuaternionUkf {
+ public:
+ // The state is just a quaternion representing the current robot orientaiton.
+ // The zero/identity quaternion (1, 0, 0, 0) implies that the robot is
+ // position flat on the ground with a heading of zero (which, in our normal
+ // field coordinates, means pointed straight away from our driver's station
+ // wall).
+ constexpr static int kNumStates = 4;
+ // Inputs to the system--we use the (x, y, z) gyro measurements as the inputs
+ // to the system.
+ constexpr static int kNumInputs = 3;
+ // Measurements to use for correcting the estimated system state. These
+ // correspond to (x, y, z) measurements from the accelerometer.
+ constexpr static int kNumMeasurements = 3;
+ QuaternionUkf() {
+ // TODO(james): Tune the process/measurement noises.
+ R_.setIdentity();
+ R_ /= 100.0;
+
+ Q_.setIdentity();
+ Q_ /= 10000.0;
+
+ // Assume that the robot starts flat on the ground pointed straight forward.
+ X_hat_ = Eigen::Quaternion<double>(1.0, 0.0, 0.0, 0.0);
+
+ // TODO(james): Determine an appropriate starting noise estimate. Probably
+ // not too critical.
+ P_.setIdentity();
+ P_ /= 1000.0;
+ }
+
+ // Handles updating the state of the UKF, given the gyro and accelerometer
+ // measurements.
+ void Predict(const Eigen::Matrix<double, kNumInputs, 1> &U,
+ const Eigen::Matrix<double, 3, 1> &measurement);
+
+ // Returns the updated state for X after one time step, given the current
+ // state and gyro measurements.
+ virtual Eigen::Matrix<double, 4, 1> A(
+ const Eigen::Matrix<double, 4, 1> &X,
+ const Eigen::Matrix<double, kNumInputs, 1> &U) const = 0;
+
+ // Returns the current expected accelerometer measurements given the current
+ // state.
+ virtual Eigen::Matrix<double, 3, 1> H(
+ const Eigen::Matrix<double, 4, 1> &X) const = 0;
+
+ // Returns the current estimate of the robot's orientation. Note that this
+ // filter does not have anything other than the gyro with which to estimate
+ // the robot's yaw heading, and so it may need to be corrected for by upstream
+ // filters.
+ const Eigen::Quaternion<double> &X_hat() const { return X_hat_; }
+
+ Eigen::Matrix<double, 3, 1> Z_hat() const { return Z_hat_; };
+
+ private:
+ // Measurement Noise (Uncertainty)
+ Eigen::Matrix<double, kNumInputs, kNumInputs> R_;
+ // Model noise. Note that both this and P are 3 x 3 matrices, despite the
+ // state having 4 dimensions.
+ Eigen::Matrix<double, 3, 3> Q_;
+ // Current estimate covariance.
+ Eigen::Matrix<double, 3, 3> P_;
+
+ // Current state estimate.
+ Eigen::Quaternion<double> X_hat_;
+
+ // Current expected accelerometer measurement.
+ Eigen::Matrix<double, 3, 1> Z_hat_;
+};
+
+class DrivetrainUkf : public QuaternionUkf {
+ public:
+ constexpr static double kDt = 0.00505;
+
+ // UKF for http://kodlab.seas.upenn.edu/uploads/Arun/UKFpaper.pdf
+ // Reference in case the link is dead:
+ // Kraft, Edgar. "A quaternion-based unscented Kalman filter for orientation
+ // tracking." In Proceedings of the Sixth International Conference of
+ // Information Fusion, vol. 1, pp. 47-54. 2003.
+
+ // A good reference for quaternions is available at
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/
+ //
+ // A good reference for angular velocity vectors with quaternions is at
+ // http://www.euclideanspace.com/physics/kinematics/angularvelocity/
+
+ // Creates a rotational velocity vector to be integrated.
+ //
+ // omega is the rotational velocity vector in body coordinates.
+ // q is a matrix with the compononents of the quaternion in it.
+ //
+ // Returns dq / dt
+ static Eigen::Vector4d QuaternionDerivative(Eigen::Vector3d omega,
+ const Eigen::Vector4d &q_matrix) {
+ Eigen::Quaternion<double> q(q_matrix);
+
+ Eigen::Quaternion<double> omega_q;
+ omega_q.w() = 0.0;
+ omega_q.vec() = 0.5 * (q * omega);
+
+ Eigen::Quaternion<double> deriv = omega_q * q;
+ return deriv.coeffs();
+ }
+
+ // Moves the robot by the provided rotation vector (U).
+ Eigen::Matrix<double, kNumStates, 1> A(
+ const Eigen::Matrix<double, kNumStates, 1> &X,
+ const Eigen::Matrix<double, kNumInputs, 1> &U) const override {
+ return RungeKutta(
+ std::bind(&QuaternionDerivative, U, std::placeholders::_1), X, kDt);
+ }
+
+ // Returns the expected accelerometer measurement (which is just going to be
+ // 1g downwards).
+ Eigen::Matrix<double, kNumMeasurements, 1> H(
+ const Eigen::Matrix<double, kNumStates, 1> &X) const override {
+ // TODO(austin): Figure out how to compute what the sensors *should* read.
+ Eigen::Quaternion<double> Xquat(X);
+ Eigen::Matrix<double, 3, 1> gprime =
+ Xquat * Eigen::Matrix<double, 3, 1>(0.0, 0.0, -1.0);
+ return gprime;
+ }
+};
+
+} // namespace drivetrain
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_IMPROVED_DOWN_ESTIMATOR_H_
diff --git a/frc971/control_loops/drivetrain/improved_down_estimator_test.cc b/frc971/control_loops/drivetrain/improved_down_estimator_test.cc
new file mode 100644
index 0000000..db7eeee
--- /dev/null
+++ b/frc971/control_loops/drivetrain/improved_down_estimator_test.cc
@@ -0,0 +1,280 @@
+#include "frc971/control_loops/runge_kutta.h"
+
+#include <Eigen/Geometry>
+#include <random>
+
+#include "aos/testing/random_seed.h"
+#include "frc971/control_loops/drivetrain/improved_down_estimator.h"
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+// Do a known transformation to see if quaternion integration is working
+// correctly.
+TEST(RungeKuttaTest, QuaternionIntegral) {
+ Eigen::Vector3d ux = Eigen::Vector3d::UnitX();
+ Eigen::Vector3d uy = Eigen::Vector3d::UnitY();
+ Eigen::Vector3d uz = Eigen::Vector3d::UnitZ();
+
+ Eigen::Quaternion<double> q(
+ Eigen::AngleAxis<double>(0.5 * M_PI, Eigen::Vector3d::UnitY()));
+
+ Eigen::Quaternion<double> q0(
+ Eigen::AngleAxis<double>(0, Eigen::Vector3d::UnitY()));
+
+ auto qux = q * ux;
+
+ VLOG(1) << "Q is w: " << q.w() << " vec: " << q.vec();
+ VLOG(1) << "ux is " << ux;
+ VLOG(1) << "qux is " << qux;
+
+ // Start by rotating around the X world vector for pi/2
+ Eigen::Quaternion<double> integral1(
+ RungeKutta(std::bind(&drivetrain::DrivetrainUkf::QuaternionDerivative, ux,
+ std::placeholders::_1),
+ q0.coeffs(), 0.5 * M_PI));
+
+ VLOG(1) << "integral1 * uz => " << integral1 * uz;
+
+ // Then rotate around the Y world vector for pi/2
+ Eigen::Quaternion<double> integral2(
+ RungeKutta(std::bind(&drivetrain::DrivetrainUkf::QuaternionDerivative, uy,
+ std::placeholders::_1),
+ integral1.normalized().coeffs(), 0.5 * M_PI));
+
+ VLOG(1) << "integral2 * uz => " << integral2 * uz;
+
+ // Then rotate around the X world vector for -pi/2
+ Eigen::Quaternion<double> integral3(
+ RungeKutta(std::bind(&drivetrain::DrivetrainUkf::QuaternionDerivative,
+ -ux, std::placeholders::_1),
+ integral2.normalized().coeffs(), 0.5 * M_PI));
+
+ integral3.normalize();
+
+ VLOG(1) << "Integral is w: " << integral1.w() << " vec: " << integral1.vec()
+ << " norm " << integral1.norm();
+
+ VLOG(1) << "Integral is w: " << integral3.w() << " vec: " << integral3.vec()
+ << " norm " << integral3.norm();
+
+ VLOG(1) << "ux => " << integral3 * ux;
+ EXPECT_NEAR(0.0, (uy - integral3 * ux).norm(), 5e-2);
+}
+
+TEST(RungeKuttaTest, Ukf) {
+ drivetrain::DrivetrainUkf dtukf;
+ Eigen::Vector3d ux = Eigen::Vector3d::UnitX();
+ Eigen::Matrix<double, 3, 1> measurement;
+ measurement.setZero();
+ dtukf.Predict(ux, measurement);
+}
+
+// Tests that small perturbations around a couple quaternions averaged out
+// return the original quaternion.
+TEST(RungeKuttaTest, QuaternionMean) {
+ Eigen::Matrix<double, 4, 7> vectors;
+ vectors.col(0) << 0, 0, 0, 1;
+ for (int i = 0; i < 3; ++i) {
+ Eigen::Matrix<double, 4, 1> perturbation;
+ perturbation.setZero();
+ perturbation(i, 0) = 0.1;
+
+ vectors.col(i * 2 + 1) = vectors.col(0) + perturbation;
+ vectors.col(i * 2 + 2) = vectors.col(0) - perturbation;
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ vectors.col(i).normalize();
+ }
+
+ Eigen::Matrix<double, 4, 1> mean = drivetrain::QuaternionMean(vectors);
+
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_NEAR(mean(i, 0), vectors(i, 0), 0.001) << ": Failed on index " << i;
+ }
+}
+
+// Tests that computing sigma points, and then computing the mean and covariance
+// returns the original answer.
+TEST(RungeKuttaTest, SigmaPoints) {
+ const Eigen::Quaternion<double> mean(
+ Eigen::AngleAxis<double>(M_PI / 2.0, Eigen::Vector3d::UnitX()));
+
+ Eigen::Matrix<double, 3, 3> covariance;
+ covariance << 0.4, -0.1, 0.2, -0.1, 0.6, 0.0, 0.2, 0.0, 0.5;
+ covariance *= 0.1;
+
+ const Eigen::Matrix<double, 4, 3 * 2 + 1> vectors =
+ drivetrain::GenerateSigmaPoints(mean, covariance);
+
+ const Eigen::Matrix<double, 4, 1> calculated_mean =
+ drivetrain::QuaternionMean(vectors);
+
+ VLOG(1) << "actual mean: " << mean.coeffs();
+ VLOG(1) << "calculated mean: " << calculated_mean;
+
+ Eigen::Matrix<double, 3, 3 * 2 + 1> Wprime;
+ Eigen::Matrix<double, 3, 3> calculated_covariance =
+ drivetrain::ComputeQuaternionCovariance(
+ Eigen::Quaternion<double>(calculated_mean), vectors, &Wprime);
+
+ EXPECT_NEAR(1.0,
+ (mean.conjugate().coeffs() * calculated_mean.transpose()).norm(),
+ 1e-4);
+
+ EXPECT_NEAR(0.0, (calculated_covariance - covariance).norm(), 1e-8);
+}
+
+// Tests that computing sigma points with a large covariance that will precisely
+// wrap, that we do clip the perturbations.
+TEST(RungeKuttaTest, ClippedSigmaPoints) {
+ const Eigen::Quaternion<double> mean(
+ Eigen::AngleAxis<double>(M_PI / 2.0, Eigen::Vector3d::UnitX()));
+
+ Eigen::Matrix<double, 3, 3> covariance;
+ covariance << 0.4, -0.1, 0.2, -0.1, 0.6, 0.0, 0.2, 0.0, 0.5;
+ covariance *= 100.0;
+
+ const Eigen::Matrix<double, 4, 3 * 2 + 1> vectors =
+ drivetrain::GenerateSigmaPoints(mean, covariance);
+
+ const Eigen::Matrix<double, 4, 1> calculated_mean =
+ drivetrain::QuaternionMean(vectors);
+
+ Eigen::Matrix<double, 3, 3 * 2 + 1> Wprime;
+ Eigen::Matrix<double, 3, 3> calculated_covariance =
+ drivetrain::ComputeQuaternionCovariance(
+ Eigen::Quaternion<double>(calculated_mean), vectors, &Wprime);
+
+ EXPECT_NEAR(1.0,
+ (mean.conjugate().coeffs() * calculated_mean.transpose()).norm(),
+ 1e-4);
+
+ const double calculated_covariance_norm = calculated_covariance.norm();
+ const double covariance_norm = covariance.norm();
+ EXPECT_LT(calculated_covariance_norm, covariance_norm / 2.0)
+ << "Calculated covariance should be much smaller than the original "
+ "covariance.";
+}
+
+// Tests that ToRotationVectorFromQuaternion works for a 0 rotation.
+TEST(RungeKuttaTest, ToRotationVectorFromQuaternionAtZero) {
+ Eigen::Matrix<double, 3, 1> vector =
+ drivetrain::ToRotationVectorFromQuaternion(
+ Eigen::Quaternion<double>(
+ Eigen::AngleAxis<double>(0.0, Eigen::Vector3d::UnitX()))
+ .coeffs());
+
+ EXPECT_NEAR(0.0, (vector - Eigen::Vector3d::Zero()).norm(), 1e-4);
+}
+
+// Tests that ToRotationVectorFromQuaternion works for a real rotation.
+TEST(RungeKuttaTest, ToRotationVectorFromQuaternion) {
+ Eigen::Matrix<double, 3, 1> vector =
+ drivetrain::ToRotationVectorFromQuaternion(
+ Eigen::Quaternion<double>(
+ Eigen::AngleAxis<double>(M_PI * 0.5, Eigen::Vector3d::UnitX()))
+ .coeffs());
+
+ EXPECT_NEAR(0.0, (vector - Eigen::Vector3d::UnitX() * M_PI * 0.5).norm(),
+ 1e-4);
+}
+
+// Tests that ToRotationVectorFromQuaternion works for a solution with negative
+// coefficients.
+TEST(RungeKuttaTest, ToRotationVectorFromQuaternionNegative) {
+ Eigen::Matrix<double, 3, 1> vector =
+ drivetrain::ToRotationVectorFromQuaternion(
+ Eigen::Quaternion<double>(
+ -Eigen::Quaternion<double>(
+ Eigen::AngleAxis<double>(M_PI * 0.5,
+ Eigen::Vector3d::UnitX()))
+ .coeffs())
+ .coeffs());
+
+ EXPECT_NEAR(0.0, (vector - Eigen::Vector3d::UnitX() * M_PI * 0.5).norm(),
+ 1e-4);
+}
+
+// Tests that ToQuaternionFromRotationVector works for a 0 rotation.
+TEST(RungeKuttaTest, ToQuaternionFromRotationVectorAtZero) {
+ Eigen::Matrix<double, 4, 1> quaternion =
+ drivetrain::ToQuaternionFromRotationVector(Eigen::Vector3d::Zero());
+
+ EXPECT_NEAR(0.0, (quaternion -
+ Eigen::Quaternion<double>(
+ Eigen::AngleAxis<double>(0.0, Eigen::Vector3d::UnitX()))
+ .coeffs()).norm(),
+ 1e-4);
+}
+
+// Tests that ToQuaternionFromRotationVector works for a real rotation.
+TEST(RungeKuttaTest, ToQuaternionFromRotationVector) {
+ Eigen::Matrix<double, 4, 1> quaternion =
+ drivetrain::ToQuaternionFromRotationVector(Eigen::Vector3d::UnitX() *
+ M_PI * 0.5);
+
+ EXPECT_NEAR(0.0, (quaternion -
+ Eigen::Quaternion<double>(
+ Eigen::AngleAxis<double>(
+ M_PI * 0.5, Eigen::Vector3d::UnitX())).coeffs())
+
+ .norm(),
+ 1e-4);
+}
+
+// Tests that ToQuaternionFromRotationVector correctly clips a rotation vector
+// that is too large in magnitude.
+TEST(RungeKuttaTest, ToQuaternionFromLargeRotationVector) {
+ const double kMaxAngle = 2.0;
+ const Eigen::Vector3d rotation_vector =
+ Eigen::Vector3d::UnitX() * kMaxAngle * 2.0;
+ const Eigen::Matrix<double, 3, 1> clipped_vector =
+ drivetrain::ToRotationVectorFromQuaternion(
+ drivetrain::ToQuaternionFromRotationVector(rotation_vector,
+ kMaxAngle));
+
+ EXPECT_NEAR(0.0, (rotation_vector / 2.0 - clipped_vector).norm(), 1e-4);
+}
+
+// Tests that ToQuaternionFromRotationVector and ToRotationVectorFromQuaternion
+// works for random rotations.
+TEST(RungeKuttaTest, RandomQuaternions) {
+ std::mt19937 generator(aos::testing::RandomSeed());
+ std::uniform_real_distribution<double> random_scalar(-1.0, 1.0);
+
+ for (int i = 0; i < 1000; ++i) {
+ Eigen::Matrix<double, 3, 1> axis;
+ axis << random_scalar(generator), random_scalar(generator),
+ random_scalar(generator);
+ EXPECT_GE(axis.norm(), 1e-6);
+ axis.normalize();
+
+ const double angle = random_scalar(generator) * M_PI;
+
+ Eigen::Matrix<double, 4, 1> quaternion =
+ drivetrain::ToQuaternionFromRotationVector(axis * angle);
+
+ Eigen::Quaternion<double> answer(Eigen::AngleAxis<double>(angle, axis));
+
+ EXPECT_NEAR(quaternion(3, 0), std::cos(angle / 2.0), 1e-8);
+ EXPECT_NEAR(answer.w(), std::cos(angle / 2.0), 1e-8);
+
+ EXPECT_NEAR(1.0, (answer.coeffs() * quaternion.transpose()).norm(), 1e-6);
+
+ const Eigen::Matrix<double, 3, 1> recalculated_axis =
+ drivetrain::ToRotationVectorFromQuaternion(quaternion);
+
+ EXPECT_NEAR(std::abs(angle), recalculated_axis.norm(), 1e-8);
+
+ EXPECT_NEAR(0.0, (axis * angle - recalculated_axis).norm(), 1e-8);
+ }
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/python/control_loop.py b/frc971/control_loops/python/control_loop.py
index 8bd70fe..919968e 100644
--- a/frc971/control_loops/python/control_loop.py
+++ b/frc971/control_loops/python/control_loop.py
@@ -661,3 +661,27 @@
self.Kt = 1.0 / self.Kv
# Stall Torque in N m
self.stall_torque = self.Kt * self.stall_current
+
+
+class Falcon(object):
+ """Class representing the VexPro Falcon 500 motor.
+
+ All numbers based on data from
+ https://www.vexrobotics.com/vexpro/falcon-500."""
+
+ def __init__(self):
+ # Stall Torque in N m
+ self.stall_torque = 4.69
+ # Stall Current in Amps
+ self.stall_current = 257.0
+ # Free Speed in rad / sec
+ self.free_speed = 6380.0 / 60.0 * 2.0 * numpy.pi
+ # Free Current in Amps
+ self.free_current = 1.5
+ # Resistance of the motor, divided by 2 to account for the 2 motors
+ self.resistance = 12.0 / self.stall_current
+ # Motor velocity constant
+ self.Kv = (self.free_speed /
+ (12.0 - self.resistance * self.free_current))
+ # Torque constant
+ self.Kt = self.stall_torque / self.stall_current
diff --git a/frc971/wpilib/ADIS16448.cc b/frc971/wpilib/ADIS16448.cc
index 57987e6..76c5894 100644
--- a/frc971/wpilib/ADIS16448.cc
+++ b/frc971/wpilib/ADIS16448.cc
@@ -8,10 +8,8 @@
#include <chrono>
#include "aos/init.h"
-#include "aos/robot_state/robot_state_generated.h"
#include "aos/time/time.h"
#include "frc971/wpilib/imu_generated.h"
-#include "frc971/zeroing/averager.h"
namespace frc971 {
namespace wpilib {
@@ -120,7 +118,6 @@
ADIS16448::ADIS16448(::aos::ShmEventLoop *event_loop, frc::SPI::Port port,
frc::DigitalInput *dio1)
: event_loop_(event_loop),
- robot_state_fetcher_(event_loop_->MakeFetcher<::aos::RobotState>("/aos")),
imu_values_sender_(
event_loop_->MakeSender<::frc971::IMUValues>("/drivetrain")),
spi_(new frc::SPI(port)),
@@ -184,13 +181,6 @@
void ADIS16448::DoRun() {
InitializeUntilSuccessful();
- // Rounded to approximate the 204.8 Hz.
- constexpr size_t kImuSendRate = 205;
-
- zeroing::Averager<double, 6 * kImuSendRate> average_gyro_x;
- zeroing::Averager<double, 6 * kImuSendRate> average_gyro_y;
- zeroing::Averager<double, 6 * kImuSendRate> average_gyro_z;
-
bool got_an_interrupt = false;
while (event_loop_->is_running()) {
{
@@ -205,7 +195,6 @@
}
}
got_an_interrupt = true;
- const monotonic_clock::time_point read_time = monotonic_clock::now();
uint8_t to_send[2 * 14], to_receive[2 * 14];
memset(&to_send[0], 0, sizeof(to_send));
@@ -249,11 +238,11 @@
IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
- imu_builder.add_fpga_timestamp(::aos::time::DurationInSeconds(
- dio1_->ReadRisingTimestamp().time_since_epoch()));
+ const auto fpga_time = dio1_->ReadRisingTimestamp();
+ imu_builder.add_fpga_timestamp(
+ ::aos::time::DurationInSeconds(fpga_time.time_since_epoch()));
imu_builder.add_monotonic_timestamp_ns(
- chrono::duration_cast<chrono::nanoseconds>(read_time.time_since_epoch())
- .count());
+ time_converter_.FpgaToMonotonic(fpga_time).time_since_epoch().count());
float gyro_x =
ConvertValue(&to_receive[4], kGyroLsbDegreeSecond * M_PI / 180.0);
@@ -262,32 +251,6 @@
float gyro_z =
ConvertValue(&to_receive[8], kGyroLsbDegreeSecond * M_PI / 180.0);
- // The first few seconds of samples are averaged and subtracted from
- // subsequent samples for zeroing purposes.
- if (!gyros_are_zeroed_) {
- average_gyro_x.AddData(gyro_x);
- average_gyro_y.AddData(gyro_y);
- average_gyro_z.AddData(gyro_z);
-
- if (average_gyro_x.full() && average_gyro_y.full() &&
- average_gyro_z.full()) {
- robot_state_fetcher_.Fetch();
- if (robot_state_fetcher_.get() &&
- robot_state_fetcher_->outputs_enabled()) {
- gyro_x_zeroed_offset_ = -average_gyro_x.GetAverage();
- gyro_y_zeroed_offset_ = -average_gyro_y.GetAverage();
- gyro_z_zeroed_offset_ = -average_gyro_z.GetAverage();
- AOS_LOG(INFO, "total gyro zero offset X:%f, Y:%f, Z:%f\n",
- gyro_x_zeroed_offset_, gyro_y_zeroed_offset_,
- gyro_z_zeroed_offset_);
- gyros_are_zeroed_ = true;
- }
- }
- }
- gyro_x += gyro_x_zeroed_offset_;
- gyro_y += gyro_y_zeroed_offset_;
- gyro_z += gyro_z_zeroed_offset_;
-
imu_builder.add_gyro_x(gyro_x);
imu_builder.add_gyro_y(gyro_y);
imu_builder.add_gyro_z(gyro_z);
diff --git a/frc971/wpilib/ADIS16448.h b/frc971/wpilib/ADIS16448.h
index 8f6da89..04d1712 100644
--- a/frc971/wpilib/ADIS16448.h
+++ b/frc971/wpilib/ADIS16448.h
@@ -13,7 +13,7 @@
#include "aos/events/shm_event_loop.h"
#include "aos/logging/logging.h"
-#include "aos/robot_state/robot_state_generated.h"
+#include "frc971/wpilib/fpga_time_conversion.h"
#include "frc971/wpilib/imu_generated.h"
#include "frc971/wpilib/spi_rx_clearer.h"
@@ -49,13 +49,8 @@
spi_idle_callback_ = std::move(spi_idle_callback);
}
- double gyro_x_zeroed_offset() const { return gyro_x_zeroed_offset_; }
- double gyro_y_zeroed_offset() const { return gyro_y_zeroed_offset_; }
- double gyro_z_zeroed_offset() const { return gyro_z_zeroed_offset_; }
-
private:
- // Initializes the sensor and then loops until Quit() is called taking
- // readings.
+ // Initializes the sensor and then takes readings until Quit() is called.
void DoRun();
// Try to initialize repeatedly as long as we're supposed to be running.
@@ -90,7 +85,6 @@
bool Initialize();
::aos::EventLoop *event_loop_;
- ::aos::Fetcher<::aos::RobotState> robot_state_fetcher_;
::aos::Sender<::frc971::IMUValues> imu_values_sender_;
// TODO(Brian): This object has no business owning these ones.
@@ -101,13 +95,9 @@
std::function<void()> spi_idle_callback_ = []() {};
- // The averaged values of the gyro over 6 seconds after power up.
- bool gyros_are_zeroed_ = false;
- double gyro_x_zeroed_offset_ = 0.0;
- double gyro_y_zeroed_offset_ = 0.0;
- double gyro_z_zeroed_offset_ = 0.0;
-
SpiRxClearer rx_clearer_;
+
+ FpgaTimeConverter time_converter_;
};
} // namespace wpilib
diff --git a/frc971/wpilib/ADIS16470.cc b/frc971/wpilib/ADIS16470.cc
new file mode 100644
index 0000000..839b82b
--- /dev/null
+++ b/frc971/wpilib/ADIS16470.cc
@@ -0,0 +1,464 @@
+#include "frc971/wpilib/ADIS16470.h"
+
+#include <inttypes.h>
+
+#include "glog/logging.h"
+
+#include "aos/time/time.h"
+#include "hal/HAL.h"
+
+namespace frc971 {
+namespace wpilib {
+namespace {
+namespace registers {
+
+// Flash memory write count
+constexpr uint8_t FLASH_CNT = 0x00;
+// Diagnostic and operational status
+constexpr uint8_t DIAG_STAT = 0x02;
+// X-axis gyroscope output, lower word
+constexpr uint8_t X_GYRO_LOW = 0x04;
+// X-axis gyroscope output, upper word
+constexpr uint8_t X_GYRO_OUT = 0x06;
+// Y-axis gyroscope output, lower word
+constexpr uint8_t Y_GYRO_LOW = 0x08;
+// Y-axis gyroscope output, upper word
+constexpr uint8_t Y_GYRO_OUT = 0x0A;
+// Z-axis gyroscope output, lower word
+constexpr uint8_t Z_GYRO_LOW = 0x0C;
+// Z-axis gyroscope output, upper word
+constexpr uint8_t Z_GYRO_OUT = 0x0E;
+// X-axis accelerometer output, lower word
+constexpr uint8_t X_ACCL_LOW = 0x10;
+// X-axis accelerometer output, upper word
+constexpr uint8_t X_ACCL_OUT = 0x12;
+// Y-axis accelerometer output, lower word
+constexpr uint8_t Y_ACCL_OUT = 0x16;
+// Y-axis accelerometer output, upper word
+constexpr uint8_t Z_ACCL_LOW = 0x18;
+// Z-axis accelerometer output, lower word
+constexpr uint8_t Z_ACCL_OUT = 0x1A;
+// Z-axis accelerometer output, upper word
+constexpr uint8_t TEMP_OUT = 0x1C;
+// Temperature output (internal, not calibrated)
+constexpr uint8_t TIME_STAMP = 0x1E;
+// PPS mode time stamp
+constexpr uint8_t X_DELTANG_LOW = 0x24;
+// X-axis delta angle output, lower word
+constexpr uint8_t X_DELTANG_OUT = 0x26;
+// X-axis delta angle output, upper word
+constexpr uint8_t Y_DELTANG_LOW = 0x28;
+// Y-axis delta angle output, lower word
+constexpr uint8_t Y_DELTANG_OUT = 0x2A;
+// Y-axis delta angle output, upper word
+constexpr uint8_t Z_DELTANG_LOW = 0x2C;
+// Z-axis delta angle output, lower word
+constexpr uint8_t Z_DELTANG_OUT = 0x2E;
+// Z-axis delta angle output, upper word
+constexpr uint8_t X_DELTVEL_LOW = 0x30;
+// X-axis delta velocity output, lower word
+constexpr uint8_t X_DELTVEL_OUT = 0x32;
+// X-axis delta velocity output, upper word
+constexpr uint8_t Y_DELTVEL_LOW = 0x34;
+// Y-axis delta velocity output, lower word
+constexpr uint8_t Y_DELTVEL_OUT = 0x36;
+// Y-axis delta velocity output, upper word
+constexpr uint8_t Z_DELTVEL_LOW = 0x38;
+// Z-axis delta velocity output, lower word
+constexpr uint8_t Z_DELTVEL_OUT = 0x3A;
+// Z-axis delta velocity output, upper word
+constexpr uint8_t XG_BIAS_LOW = 0x40;
+// X-axis gyroscope bias offset correction, lower word
+constexpr uint8_t XG_BIAS_HIGH = 0x42;
+// X-axis gyroscope bias offset correction, upper word
+constexpr uint8_t YG_BIAS_LOW = 0x44;
+// Y-axis gyroscope bias offset correction, lower word
+constexpr uint8_t YG_BIAS_HIGH = 0x46;
+// Y-axis gyroscope bias offset correction, upper word
+constexpr uint8_t ZG_BIAS_LOW = 0x48;
+// Z-axis gyroscope bias offset correction, lower word
+constexpr uint8_t ZG_BIAS_HIGH = 0x4A;
+// Z-axis gyroscope bias offset correction, upper word
+constexpr uint8_t XA_BIAS_LOW = 0x4C;
+// X-axis accelerometer bias offset correction, lower word
+constexpr uint8_t XA_BIAS_HIGH = 0x4E;
+// X-axis accelerometer bias offset correction, upper word
+constexpr uint8_t YA_BIAS_LOW = 0x50;
+// Y-axis accelerometer bias offset correction, lower word
+constexpr uint8_t YA_BIAS_HIGH = 0x52;
+// Y-axis accelerometer bias offset correction, upper word
+constexpr uint8_t ZA_BIAS_LOW = 0x54;
+// Z-axis accelerometer bias offset correction, lower word
+constexpr uint8_t ZA_BIAS_HIGH = 0x56;
+// Z-axis accelerometer bias offset correction, upper word
+constexpr uint8_t FILT_CTRL = 0x5C;
+// Filter control
+constexpr uint8_t MSC_CTRL = 0x60;
+// Miscellaneous control
+constexpr uint8_t UP_SCALE = 0x62;
+// Clock scale factor, PPS mode
+constexpr uint8_t DEC_RATE = 0x64;
+// Decimation rate control (output data rate)
+constexpr uint8_t NULL_CNFG = 0x66;
+// Auto-null configuration control
+constexpr uint8_t GLOB_CMD = 0x68;
+// Global commands
+constexpr uint8_t FIRM_REV = 0x6C;
+// Firmware revision
+constexpr uint8_t FIRM_DM = 0x6E;
+// Firmware revision date, month and day
+constexpr uint8_t FIRM_Y = 0x70;
+// Firmware revision date, year
+constexpr uint8_t PROD_ID = 0x72;
+// Product identification
+constexpr uint8_t SERIAL_NUM = 0x74;
+// Serial number (relative to assembly lot)
+constexpr uint8_t USER_SCR1 = 0x76;
+// User scratch register 1
+constexpr uint8_t USER_SCR2 = 0x78;
+// User scratch register 2
+constexpr uint8_t USER_SCR3 = 0x7A;
+// User scratch register 3
+constexpr uint8_t FLSHCNT_LOW = 0x7C;
+// Flash update count, lower word
+constexpr uint8_t FLSHCNT_HIGH = 0x7E;
+// Flash update count, upper word
+constexpr uint8_t Y_ACCL_LOW = 0x14;
+
+} // namespace registers
+
+// The complete automatic packet we will send. This needs to include the dummy 0
+// bytes making up full 16-bit frames.
+// Note that in addition to the 24-byte limit from the FPGA, this is also
+// limited to 12 16-bit register reads by the IMU itself given that we're
+// reading at the full 2kHz rate.
+// We rotate the registers here by 1, such that the first thing we read is the
+// last thing triggered by the previous reading. We put DIAG_STAT in this
+// position because we don't care if it's one cycle stale.
+constexpr uint8_t kAutospiPacket[] = {
+ // X
+ registers::X_GYRO_OUT, 0,
+ registers::X_ACCL_OUT, 0, registers::X_ACCL_LOW, 0,
+ // Y
+ registers::Y_GYRO_OUT, 0,
+ registers::Y_ACCL_OUT, 0, registers::Y_ACCL_LOW, 0,
+ // Z
+ registers::Z_GYRO_OUT, 0,
+ registers::Z_ACCL_OUT, 0, registers::Z_ACCL_LOW, 0,
+ // Other
+ registers::TEMP_OUT, 0, registers::DIAG_STAT, 0,
+};
+// clang-format on
+
+static_assert((sizeof(kAutospiPacket) % 2) == 0,
+ "Need a whole number of register reads");
+
+static constexpr size_t kAutospiDataSize = sizeof(kAutospiPacket) + 1 /* timestamp */;
+
+// radian/second/LSB for the gyros (for just the 16-bit value).
+constexpr double kGyroLsbRadianSecond =
+ 1.0 / 10.0 * (2.0 * M_PI / 360.0) /* degrees -> radians */;
+// G/LSB for the accelerometers (for the full 32-bit value).
+constexpr double kAccelerometerLsbG = 1.0 / 54'428'800.0;
+// C/LSB for the temperature.
+constexpr double kTemperatureLsbDegree = 0.1;
+
+// This is what the datasheet says PROD_ID should be.
+constexpr uint16_t kExpectedProductId = 0x4056;
+// This is the PROD_ID we observe.
+constexpr uint16_t kObservedProductId = 0x4256;
+
+} // namespace
+
+ADIS16470::ADIS16470(aos::EventLoop *event_loop, frc::SPI *spi,
+ frc::DigitalInput *data_ready, frc::DigitalOutput *reset)
+ : event_loop_(event_loop),
+ imu_values_sender_(
+ event_loop_->MakeSender<::frc971::IMUValues>("/drivetrain")),
+ initialize_timer_(
+ event_loop_->AddTimer([this]() { DoInitializeStep(); })),
+ spi_(spi),
+ data_ready_(data_ready),
+ reset_(reset) {
+ // Rather than put the entire data packet into the header, just put a size
+ // there and verify it matches here.
+ CHECK_EQ(kAutospiDataSize, read_data_.size());
+
+ // We're not doing burst mode, so this is the IMU's rated speed.
+ spi_->SetClockRate(2'000'000);
+ spi_->SetChipSelectActiveLow();
+ spi_->SetClockActiveLow();
+ spi_->SetSampleDataOnTrailingEdge();
+ spi_->SetMSBFirst();
+
+ // NI's SPI driver defaults to SCHED_OTHER. Find it's PID with ps, and change
+ // it to a RT priority of 33.
+ PCHECK(
+ system("ps -ef | grep '\\[spi0\\]' | awk '{print $1}' | xargs chrt -f -p "
+ "33") == 0);
+
+ event_loop_->OnRun([this]() { BeginInitialization(); });
+}
+
+void ADIS16470::DoReads() {
+ if (state_ != State::kRunning) {
+ // Not sure how to interpret data received now, so ignore it.
+ return;
+ }
+
+ int amount_to_read =
+ spi_->ReadAutoReceivedData(to_read_.data(), 0, 0 /* don't block */);
+ while (true) {
+ if (amount_to_read == 0) break;
+ CHECK(!to_read_.empty());
+ const int amount_read_now = std::min<int>(amount_to_read, to_read_.size());
+ CHECK_GT(amount_read_now, 0) << "amount_to_read: " << amount_to_read
+ << ", to_read_.size(): " << to_read_.size();
+ spi_->ReadAutoReceivedData(to_read_.data(), amount_read_now,
+ 0 /* don't block */);
+ to_read_ = to_read_.subspan(amount_read_now);
+ amount_to_read -= amount_read_now;
+
+ if (to_read_.empty()) {
+ ProcessReading();
+
+ // Reset for the next reading.
+ to_read_ = absl::MakeSpan(read_data_);
+ } else {
+ CHECK_EQ(amount_to_read, 0);
+ break;
+ }
+ }
+}
+
+void ADIS16470::DoInitializeStep() {
+ switch (state_) {
+ case State::kUninitialized: {
+ to_read_ = absl::MakeSpan(read_data_);
+
+ // First, set the SPI to normal mode so it stops trying to talk
+ // automatically.
+ spi_->StopAuto();
+
+ reset_->Set(false);
+ // Datasheet says it needs a 1 us pulse, so make sure we do something in
+ // between asserting and deasserting.
+ std::this_thread::sleep_for(::std::chrono::milliseconds(1));
+ reset_->Set(true);
+
+ state_ = State::kWaitForReset;
+ // Datasheet says it takes 193 ms to come out of reset, so give it some
+ // margin on top of that.
+ initialize_timer_->Setup(event_loop_->monotonic_now() +
+ std::chrono::milliseconds(250));
+ }
+ break;
+
+ case State::kWaitForReset: {
+ flatbuffers::Offset<ADIS16470DiagStat> start_diag_stat;
+ flatbuffers::Offset<ADIS16470DiagStat> self_test_diag_stat;
+ bool success = false;
+ auto builder = imu_values_sender_.MakeBuilder();
+
+ // Configure the IMU the way we want it.
+ const uint16_t product_id = ReadRegister(registers::PROD_ID, 0);
+ if (product_id == kExpectedProductId ||
+ product_id == kObservedProductId) {
+ const uint16_t start_diag_stat_value =
+ ReadRegister(registers::DIAG_STAT, 0);
+ start_diag_stat = PackDiagStat(builder.fbb(), start_diag_stat_value);
+ if (!DiagStatHasError(
+ *GetTemporaryPointer(*builder.fbb(), start_diag_stat))) {
+ WriteRegister(registers::FILT_CTRL, 0 /* no filtering */);
+ WriteRegister(
+ registers::MSC_CTRL,
+ (1 << 7) /* enable gyro linear g compensation */ |
+ (1 << 6) /* enable point of percussion alignment */ |
+ (0 << 2) /* internal clock mode */ |
+ (0 << 1) /* sync polarity, doesn't matter */ |
+ (1 << 0) /* data ready is active high */);
+ WriteRegister(registers::DEC_RATE,
+ 0 /* no internal decimation (averaging) */);
+
+ // Start a sensor self test.
+ WriteRegister(registers::GLOB_CMD, 1 << 2);
+ // Datasheet says it takes 14ms, so give it some margin.
+ std::this_thread::sleep_for(std::chrono::milliseconds(25));
+ // Read DIAG_STAT again, and queue up a read of the first part of the
+ // autospi data packet.
+ const uint16_t self_test_diag_stat_value =
+ ReadRegister(registers::DIAG_STAT, kAutospiPacket[0]);
+ self_test_diag_stat =
+ PackDiagStat(builder.fbb(), self_test_diag_stat_value);
+ if (!DiagStatHasError(
+ *GetTemporaryPointer(*builder.fbb(), self_test_diag_stat))) {
+ // Initialize automatic mode, but don't start it yet.
+ spi_->InitAuto(kAutospiDataSize * 100);
+ spi_->SetAutoTransmitData(kAutospiPacket,
+ 0 /* no extra 0s at the end */);
+ // No idea what units the "stall period" is in. This value is just
+ // bigger than the 16us min from the datasheet. It does not appear
+ // to scale with SPICLK frequency. Empirically, this value comes out
+ // to 16.7us.
+ spi_->ConfigureAutoStall(
+ HAL_SPI_kOnboardCS0,
+ 0 /* the minimum CS delay is enough for this IMU */, 670,
+ 1 /* toggle CS every 2 8-bit bytes */);
+
+ // Read any data queued up by the FPGA.
+ while (true){
+ uint32_t buffer;
+ if (spi_->ReadAutoReceivedData(&buffer, 1, 0 /* don't block */) ==
+ 0) {
+ break;
+ }
+ }
+
+ // Finally, enable automatic mode so it starts reading data.
+ spi_->StartAutoTrigger(*data_ready_, true, false);
+ success = true;
+ }
+ }
+ }
+
+ IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
+ imu_builder.add_product_id(product_id);
+ if (!start_diag_stat.IsNull()) {
+ imu_builder.add_start_diag_stat(start_diag_stat);
+ }
+ if (!self_test_diag_stat.IsNull()) {
+ imu_builder.add_self_test_diag_stat(self_test_diag_stat);
+ }
+ builder.Send(imu_builder.Finish());
+ if (success) {
+ state_ = State::kRunning;
+ } else {
+ BeginInitialization();
+ }
+ }
+ break;
+
+ case State::kRunning:
+ LOG(FATAL) << "Not a reset state";
+ }
+}
+
+void ADIS16470::ProcessReading() {
+ // If we ever see this, we'll need to decide how to handle it. Probably reset
+ // everything and try again.
+ CHECK_EQ(0, spi_->GetAutoDroppedCount());
+
+ auto builder = imu_values_sender_.MakeBuilder();
+
+ absl::Span<const uint32_t> to_process = read_data_;
+ hal::fpga_clock::time_point fpga_time;
+ {
+ int32_t status = 0;
+ const uint64_t fpga_expanded = HAL_ExpandFPGATime(to_process[0], &status);
+ CHECK_EQ(0, status);
+ fpga_time =
+ hal::fpga_clock::time_point(hal::fpga_clock::duration(fpga_expanded));
+ }
+ to_process = to_process.subspan(1);
+
+ const uint16_t diag_stat_value = (static_cast<uint16_t>(to_process[0]) << 8) |
+ static_cast<uint16_t>(to_process[1]);
+ const auto diag_stat = PackDiagStat(builder.fbb(), diag_stat_value);
+ to_process = to_process.subspan(2);
+
+ IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
+ imu_builder.add_fpga_timestamp(
+ aos::time::DurationInSeconds(fpga_time.time_since_epoch()));
+ imu_builder.add_monotonic_timestamp_ns(
+ time_converter_.FpgaToMonotonic(fpga_time).time_since_epoch().count());
+ imu_builder.add_previous_reading_diag_stat(diag_stat);
+
+ imu_builder.add_gyro_x(ConvertValue16(to_process, kGyroLsbRadianSecond));
+ to_process = to_process.subspan(2);
+ imu_builder.add_accelerometer_x(
+ ConvertValue32(to_process, kAccelerometerLsbG));
+ to_process = to_process.subspan(4);
+ imu_builder.add_gyro_y(ConvertValue16(to_process, kGyroLsbRadianSecond));
+ to_process = to_process.subspan(2);
+ imu_builder.add_accelerometer_y(
+ ConvertValue32(to_process, kAccelerometerLsbG));
+ to_process = to_process.subspan(4);
+ imu_builder.add_gyro_z(ConvertValue16(to_process, kGyroLsbRadianSecond));
+ to_process = to_process.subspan(2);
+ imu_builder.add_accelerometer_z(
+ ConvertValue32(to_process, kAccelerometerLsbG));
+ to_process = to_process.subspan(4);
+
+ imu_builder.add_temperature(
+ ConvertValue16(to_process, kTemperatureLsbDegree));
+ to_process = to_process.subspan(2);
+
+ CHECK(to_process.empty()) << "Have leftover bytes: " << to_process.size();
+
+ builder.Send(imu_builder.Finish());
+}
+
+double ADIS16470::ConvertValue32(absl::Span<const uint32_t> data,
+ double lsb_per_output) {
+ const uint32_t unsigned_value = (static_cast<uint32_t>(data[0]) << 24) |
+ (static_cast<uint32_t>(data[1]) << 16) |
+ (static_cast<uint32_t>(data[2]) << 8) |
+ static_cast<uint32_t>(data[3]);
+ int32_t signed_value;
+ memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
+ return static_cast<double>(signed_value) * lsb_per_output;
+}
+
+double ADIS16470::ConvertValue16(absl::Span<const uint32_t> data,
+ double lsb_per_output) {
+ const uint16_t unsigned_value =
+ (static_cast<uint16_t>(data[0]) << 8) | static_cast<uint16_t>(data[1]);
+ int16_t signed_value;
+ memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
+ return static_cast<double>(signed_value) * lsb_per_output;
+}
+
+flatbuffers::Offset<ADIS16470DiagStat> ADIS16470::PackDiagStat(
+ flatbuffers::FlatBufferBuilder *fbb, uint16_t value) {
+ ADIS16470DiagStat::Builder diag_stat_builder(*fbb);
+ diag_stat_builder.add_clock_error(value & (1 << 7));
+ diag_stat_builder.add_memory_failure(value & (1 << 6));
+ diag_stat_builder.add_sensor_failure(value & (1 << 5));
+ diag_stat_builder.add_standby_mode(value & (1 << 4));
+ diag_stat_builder.add_spi_communication_error(value & (1 << 3));
+ diag_stat_builder.add_flash_memory_update_error(value & (1 << 2));
+ diag_stat_builder.add_data_path_overrun(value & (1 << 1));
+ return diag_stat_builder.Finish();
+}
+
+bool ADIS16470::DiagStatHasError(const ADIS16470DiagStat &diag_stat) {
+ return diag_stat.clock_error() || diag_stat.memory_failure() ||
+ diag_stat.sensor_failure() || diag_stat.standby_mode() ||
+ diag_stat.spi_communication_error() ||
+ diag_stat.flash_memory_update_error() || diag_stat.data_path_overrun();
+}
+
+uint16_t ADIS16470::ReadRegister(uint8_t register_address,
+ uint8_t next_register_address) {
+ uint8_t send_buffer[2] = {static_cast<uint8_t>(register_address & 0x7f), 0};
+ uint8_t dummy[2];
+ spi_->Transaction(send_buffer, dummy, sizeof(send_buffer));
+ uint8_t receive_buffer[2];
+ uint8_t next_send_buffer[2] = {
+ static_cast<uint8_t>(next_register_address & 0x7f), 0};
+ spi_->Transaction(next_send_buffer, receive_buffer, sizeof(receive_buffer));
+ return (static_cast<uint16_t>(receive_buffer[0]) << 8) |
+ static_cast<uint16_t>(receive_buffer[1]);
+}
+
+void ADIS16470::WriteRegister(uint8_t register_address, uint16_t value) {
+ uint8_t buffer1[2] = {static_cast<uint8_t>(register_address | 0x80),
+ static_cast<uint8_t>(value & 0xff)};
+ uint8_t buffer2[2] = {static_cast<uint8_t>(register_address | 0x81),
+ static_cast<uint8_t>(value >> 8)};
+ spi_->Write(buffer1, sizeof(buffer1));
+ spi_->Write(buffer2, sizeof(buffer2));
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/ADIS16470.h b/frc971/wpilib/ADIS16470.h
new file mode 100644
index 0000000..4c9917f
--- /dev/null
+++ b/frc971/wpilib/ADIS16470.h
@@ -0,0 +1,97 @@
+#ifndef FRC971_WPILIB_ADIS16470_H_
+#define FRC971_WPILIB_ADIS16470_H_
+
+#include "absl/types/span.h"
+
+#include "aos/events/event_loop.h"
+#include "frc971/wpilib/ahal/DigitalInput.h"
+#include "frc971/wpilib/ahal/DigitalOutput.h"
+#include "frc971/wpilib/ahal/DigitalSource.h"
+#include "frc971/wpilib/ahal/SPI.h"
+#include "frc971/wpilib/fpga_time_conversion.h"
+#include "frc971/wpilib/imu_generated.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Handles interfacing with an Analog Devices ADIS16470 over SPI and sending the
+// resulting values out on a channel.
+//
+// This relies on the AutoRead functionality in the FPGA to read values when
+// data is ready. It then allows the FPGA to buffer those until right before the
+// relevant control loops run, at which point they are all sent out on the
+// relevant channel.
+class ADIS16470 {
+ public:
+ // event_loop's thread will be hijacked before processing any events.
+ // spi is how to talk to the sensor over SPI.
+ // data_ready is the Data Ready (DR) pin (J6).
+ // reset is the Reset (RST) pin (F3).
+ ADIS16470(aos::EventLoop *event_loop, frc::SPI *spi,
+ frc::DigitalInput *data_ready, frc::DigitalOutput *reset);
+
+ ADIS16470(const ADIS16470 &) = delete;
+ ADIS16470 &operator=(const ADIS16470 &) = delete;
+
+ // Reads all the queued-up data and sends out any complete readings.
+ void DoReads();
+
+ private:
+ enum class State {
+ kUninitialized,
+ kWaitForReset,
+ kRunning,
+ };
+
+ // Performs one (non-blocking) initialization step.
+ void DoInitializeStep();
+
+ // Processes a complete reading in read_data_.
+ void ProcessReading();
+
+ // Converts a 32-bit value at data to a scaled output value where a value of 1
+ // corresponds to lsb_per_output.
+ static double ConvertValue32(absl::Span<const uint32_t> data,
+ double lsb_per_output);
+ static double ConvertValue16(absl::Span<const uint32_t> data,
+ double lsb_per_output);
+
+ static flatbuffers::Offset<ADIS16470DiagStat> PackDiagStat(
+ flatbuffers::FlatBufferBuilder *fbb, uint16_t value);
+
+ static bool DiagStatHasError(const ADIS16470DiagStat &diag_stat);
+
+ // These may only be called during configuration, when spi_ is not in
+ // automatic mode.
+ uint16_t ReadRegister(uint8_t register_address,
+ uint8_t next_register_address);
+ void WriteRegister(uint8_t register_address, uint16_t value);
+
+ void BeginInitialization() {
+ state_ = State::kUninitialized;
+ initialize_timer_->Setup(event_loop_->monotonic_now() +
+ std::chrono::milliseconds(25));
+ }
+
+ aos::EventLoop *const event_loop_;
+ aos::Sender<::frc971::IMUValues> imu_values_sender_;
+ aos::TimerHandler *const initialize_timer_;
+
+ frc::SPI *const spi_;
+ frc::DigitalInput *const data_ready_;
+ frc::DigitalOutput *const reset_;
+
+ State state_ = State::kUninitialized;
+
+ // Data we've read from the FPGA.
+ std::array<uint32_t, 23> read_data_;
+ // Data that we need to read from the FPGA to get a complete reading.
+ absl::Span<uint32_t> to_read_;
+
+ FpgaTimeConverter time_converter_;
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_ADIS16470_H_
diff --git a/frc971/wpilib/BUILD b/frc971/wpilib/BUILD
index b1bf587..bcdd2ce 100644
--- a/frc971/wpilib/BUILD
+++ b/frc971/wpilib/BUILD
@@ -89,6 +89,8 @@
hdrs = [
"gyro_interface.h",
],
+ # This library uses some deprecated parts of the SPI API.
+ copts = ["-Wno-deprecated-declarations"],
restricted_to = ["//tools:roborio"],
deps = [
"//aos/logging",
@@ -266,6 +268,26 @@
)
cc_library(
+ name = "ADIS16470",
+ srcs = [
+ "ADIS16470.cc",
+ ],
+ hdrs = [
+ "ADIS16470.h",
+ ],
+ restricted_to = ["//tools:roborio"],
+ deps = [
+ ":fpga_time_conversion",
+ ":imu_fbs",
+ "//aos/events:event_loop",
+ "//aos/time",
+ "//third_party:wpilib",
+ "@com_github_google_glog//:glog",
+ "@com_google_absl//absl/types:span",
+ ],
+)
+
+cc_library(
name = "ADIS16448",
srcs = [
"ADIS16448.cc",
@@ -273,15 +295,17 @@
hdrs = [
"ADIS16448.h",
],
+ # This library uses some deprecated parts of the SPI API.
+ copts = ["-Wno-deprecated-declarations"],
restricted_to = ["//tools:roborio"],
deps = [
+ ":fpga_time_conversion",
":imu_fbs",
":spi_rx_clearer",
"//aos:init",
"//aos/events:event_loop",
"//aos/events:shm_event_loop",
"//aos/logging",
- "//aos/robot_state:robot_state_fbs",
"//aos/time",
"//frc971/zeroing:averager",
"//third_party:wpilib",
@@ -328,6 +352,7 @@
":dma",
":dma_edge_counting",
":encoder_and_potentiometer",
+ ":fpga_time_conversion",
":wpilib_interface",
"//aos:init",
"//aos/events:event_loop",
@@ -357,3 +382,20 @@
"//third_party:wpilib",
],
)
+
+cc_library(
+ name = "fpga_time_conversion",
+ srcs = [
+ "fpga_time_conversion.cc",
+ ],
+ hdrs = [
+ "fpga_time_conversion.h",
+ ],
+ restricted_to = ["//tools:roborio"],
+ deps = [
+ "//aos/time",
+ "//aos/util:compiler_memory_barrier",
+ "//third_party:wpilib",
+ "@com_github_google_glog//:glog",
+ ],
+)
diff --git a/frc971/wpilib/ahal/SPI.cc b/frc971/wpilib/ahal/SPI.cc
index 00e7dc9..2e8a2fc 100644
--- a/frc971/wpilib/ahal/SPI.cc
+++ b/frc971/wpilib/ahal/SPI.cc
@@ -1,90 +1,93 @@
/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
+/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
-#include "hal/SPI.h"
#include "frc971/wpilib/ahal/SPI.h"
#include <cstring>
+#include <utility>
-#include "hal/HAL.h"
-#include "wpi/SmallVector.h"
+#include <hal/SPI.h>
+#include <wpi/SmallVector.h>
+#include <wpi/mutex.h>
-using namespace frc;
+#include "frc971/wpilib/ahal/DigitalSource.h"
+#include "frc971/wpilib/ahal/WPIErrors.h"
-#define HAL_FATAL_WITH_STATUS(status)
+namespace frc {
-SPI::SPI(Port SPIport) {
-#ifdef WPILIB2017
- m_port = SPIport;
-#else
- m_port = static_cast<HAL_SPIPort>(SPIport);
-#endif
+SPI::SPI(Port port) : m_port(static_cast<HAL_SPIPort>(port)) {
int32_t status = 0;
HAL_InitializeSPI(m_port, &status);
- HAL_FATAL_WITH_STATUS(status);
-
- static int instances = 0;
- instances++;
- HAL_Report(HALUsageReporting::kResourceType_SPI, instances);
+ wpi_setHALError(status);
}
SPI::~SPI() { HAL_CloseSPI(m_port); }
-void SPI::SetClockRate(double hz) { HAL_SetSPISpeed(m_port, hz); }
+void SPI::SetClockRate(int hz) { HAL_SetSPISpeed(m_port, hz); }
void SPI::SetMSBFirst() {
m_msbFirst = true;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetLSBFirst() {
m_msbFirst = false;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
+}
+
+void SPI::SetSampleDataOnLeadingEdge() {
+ m_sampleOnTrailing = false;
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
+}
+
+void SPI::SetSampleDataOnTrailingEdge() {
+ m_sampleOnTrailing = true;
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetSampleDataOnFalling() {
m_sampleOnTrailing = true;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetSampleDataOnRising() {
m_sampleOnTrailing = false;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetClockActiveLow() {
- m_clk_idle_high = true;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ m_clockIdleHigh = true;
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetClockActiveHigh() {
- m_clk_idle_high = false;
- HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
+ m_clockIdleHigh = false;
+ HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
}
void SPI::SetChipSelectActiveHigh() {
int32_t status = 0;
HAL_SetSPIChipSelectActiveHigh(m_port, &status);
- HAL_FATAL_WITH_STATUS(status);
+ wpi_setHALError(status);
}
void SPI::SetChipSelectActiveLow() {
int32_t status = 0;
HAL_SetSPIChipSelectActiveLow(m_port, &status);
- HAL_FATAL_WITH_STATUS(status);
+ wpi_setHALError(status);
}
-int SPI::Write(uint8_t *data, int size) {
+int SPI::Write(uint8_t* data, int size) {
int retVal = 0;
retVal = HAL_WriteSPI(m_port, data, size);
return retVal;
}
-int SPI::Read(bool initiate, uint8_t *dataReceived, int size) {
+int SPI::Read(bool initiate, uint8_t* dataReceived, int size) {
int retVal = 0;
if (initiate) {
wpi::SmallVector<uint8_t, 32> dataToSend;
@@ -96,8 +99,79 @@
return retVal;
}
-int SPI::Transaction(uint8_t *dataToSend, uint8_t *dataReceived, int size) {
+int SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived, int size) {
int retVal = 0;
retVal = HAL_TransactionSPI(m_port, dataToSend, dataReceived, size);
return retVal;
}
+
+void SPI::InitAuto(int bufferSize) {
+ int32_t status = 0;
+ HAL_InitSPIAuto(m_port, bufferSize, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::FreeAuto() {
+ int32_t status = 0;
+ HAL_FreeSPIAuto(m_port, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize) {
+ int32_t status = 0;
+ HAL_SetSPIAutoTransmitData(m_port, dataToSend.data(), dataToSend.size(),
+ zeroSize, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::StartAutoRate(double period) {
+ int32_t status = 0;
+ HAL_StartSPIAutoRate(m_port, period, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::StartAutoTrigger(DigitalSource& source, bool rising, bool falling) {
+ int32_t status = 0;
+ HAL_StartSPIAutoTrigger(
+ m_port, source.GetPortHandleForRouting(),
+ (HAL_AnalogTriggerType)source.GetAnalogTriggerTypeForRouting(), rising,
+ falling, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::StopAuto() {
+ int32_t status = 0;
+ HAL_StopSPIAuto(m_port, &status);
+ wpi_setHALError(status);
+}
+
+void SPI::ForceAutoRead() {
+ int32_t status = 0;
+ HAL_ForceSPIAutoRead(m_port, &status);
+ wpi_setHALError(status);
+}
+
+int SPI::ReadAutoReceivedData(uint32_t *buffer, int numToRead, double timeout) {
+ int32_t status = 0;
+ int32_t val = HAL_ReadSPIAutoReceivedData(m_port, buffer, numToRead,
+ timeout, &status);
+ wpi_setHALError(status);
+ return val;
+}
+
+int SPI::GetAutoDroppedCount() {
+ int32_t status = 0;
+ int32_t val = HAL_GetSPIAutoDroppedCount(m_port, &status);
+ wpi_setHALError(status);
+ return val;
+}
+
+void SPI::ConfigureAutoStall(HAL_SPIPort /*port*/, int csToSclkTicks,
+ int stallTicks, int pow2BytesPerRead) {
+ int32_t status = 0;
+ HAL_ConfigureSPIAutoStall(m_port, csToSclkTicks, stallTicks, pow2BytesPerRead,
+ &status);
+ wpi_setHALError(status);
+}
+
+} // namespace frc
diff --git a/frc971/wpilib/ahal/SPI.h b/frc971/wpilib/ahal/SPI.h
index 898f426..9656f55 100644
--- a/frc971/wpilib/ahal/SPI.h
+++ b/frc971/wpilib/ahal/SPI.h
@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
-/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
+/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -7,12 +7,17 @@
#pragma once
-#include "hal/SPI.h"
+#include <stdint.h>
+
+#include <memory>
+
+#include <hal/SPITypes.h>
+#include <wpi/ArrayRef.h>
+#include <wpi/deprecated.h>
namespace frc {
-class DigitalOutput;
-class DigitalInput;
+class DigitalSource;
/**
* SPI bus interface class.
@@ -21,91 +26,230 @@
* It probably should not be used directly.
*
*/
-class SPI {
+class SPI final {
public:
- enum Port : int32_t {
- kOnboardCS0 = 0,
- kOnboardCS1,
- kOnboardCS2,
- kOnboardCS3,
- kMXP
- };
- explicit SPI(Port SPIport);
- virtual ~SPI();
+ enum Port { kOnboardCS0 = 0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP };
- SPI(const SPI &) = delete;
- SPI &operator=(const SPI &) = delete;
+ /**
+ * Constructor
+ *
+ * @param port the physical SPI port
+ */
+ explicit SPI(Port port);
- // Configure the rate of the generated clock signal.
- //
- // The claimed default value is 500,000Hz, and the claimed maximum value is
- // 4,000,000Hz.
- //
- // This appears to have a very inflexible clocking setup. You can get 0.781MHz
- // or 1.563MHz, but nothing in between. At least it rounds down the requested
- // value like it should... 0.781MHz also appears to be the minimum.
- void SetClockRate(double hz);
+ ~SPI();
- // Configure the order that bits are sent and received on the wire
- // to be most significant bit first.
+ SPI(SPI&&) = default;
+ SPI& operator=(SPI&&) = default;
+
+ /**
+ * Configure the rate of the generated clock signal.
+ *
+ * The default value is 500,000Hz.
+ * The maximum value is 4,000,000Hz.
+ *
+ * @param hz The clock rate in Hertz.
+ */
+ void SetClockRate(int hz);
+
+ /**
+ * Configure the order that bits are sent and received on the wire
+ * to be most significant bit first.
+ */
void SetMSBFirst();
- // Configure the order that bits are sent and received on the wire
- // to be least significant bit first.
+
+ /**
+ * Configure the order that bits are sent and received on the wire
+ * to be least significant bit first.
+ */
void SetLSBFirst();
- // Configure that the data is stable on the falling edge and the data
- // changes on the rising edge.
+ /**
+ * Configure that the data is stable on the leading edge and the data
+ * changes on the trailing edge.
+ */
+ void SetSampleDataOnLeadingEdge();
+
+ /**
+ * Configure that the data is stable on the trailing edge and the data
+ * changes on the leading edge.
+ */
+ void SetSampleDataOnTrailingEdge();
+
+ /**
+ * Configure that the data is stable on the falling edge and the data
+ * changes on the rising edge.
+ */
+ WPI_DEPRECATED("Use SetSampleDataOnTrailingEdge in most cases.")
void SetSampleDataOnFalling();
- // Configure that the data is stable on the rising edge and the data
- // changes on the falling edge.
+
+ /**
+ * Configure that the data is stable on the rising edge and the data
+ * changes on the falling edge.
+ */
+ WPI_DEPRECATED("Use SetSampleDataOnLeadingEdge in most cases")
void SetSampleDataOnRising();
- // Configure the clock output line to be active low.
- // This is sometimes called clock polarity high or clock idle high.
+ /**
+ * Configure the clock output line to be active low.
+ * This is sometimes called clock polarity high or clock idle high.
+ */
void SetClockActiveLow();
- // Configure the clock output line to be active high.
- // This is sometimes called clock polarity low or clock idle low.
+
+ /**
+ * Configure the clock output line to be active high.
+ * This is sometimes called clock polarity low or clock idle low.
+ */
void SetClockActiveHigh();
- // Configure the chip select line to be active high.
+ /**
+ * Configure the chip select line to be active high.
+ */
void SetChipSelectActiveHigh();
- // Configure the chip select line to be active low.
+
+ /**
+ * Configure the chip select line to be active low.
+ */
void SetChipSelectActiveLow();
- // Write data to the slave device. Blocks until there is space in the
- // output FIFO.
- //
- // If not running in output only mode, also saves the data received
- // on the MISO input during the transfer into the receive FIFO.
- int Write(uint8_t *data, int size);
- // Read a word from the receive FIFO.
- //
- // Waits for the current transfer to complete if the receive FIFO is empty.
- //
- // If the receive FIFO is empty, there is no active transfer, and initiate
- // is false, errors.
- //
- // @param initiate If true, this function pushes "0" into the transmit buffer
- // and initiates a transfer. If false, this function assumes
- // that data is already in the receive FIFO from a previous
- // write.
- int Read(bool initiate, uint8_t *dataReceived, int size);
- // Perform a simultaneous read/write transaction with the device
- //
- // @param dataToSend The data to be written out to the device
- // @param dataReceived Buffer to receive data from the device
- // @param size The length of the transaction, in bytes
- int Transaction(uint8_t *dataToSend, uint8_t *dataReceived, int size);
+ /**
+ * Write data to the slave device. Blocks until there is space in the
+ * output FIFO.
+ *
+ * If not running in output only mode, also saves the data received
+ * on the MISO input during the transfer into the receive FIFO.
+ */
+ virtual int Write(uint8_t* data, int size);
+
+ /**
+ * Read a word from the receive FIFO.
+ *
+ * Waits for the current transfer to complete if the receive FIFO is empty.
+ *
+ * If the receive FIFO is empty, there is no active transfer, and initiate
+ * is false, errors.
+ *
+ * @param initiate If true, this function pushes "0" into the transmit buffer
+ * and initiates a transfer. If false, this function assumes
+ * that data is already in the receive FIFO from a previous
+ * write.
+ */
+ virtual int Read(bool initiate, uint8_t* dataReceived, int size);
+
+ /**
+ * Perform a simultaneous read/write transaction with the device
+ *
+ * @param dataToSend The data to be written out to the device
+ * @param dataReceived Buffer to receive data from the device
+ * @param size The length of the transaction, in bytes
+ */
+ virtual int Transaction(uint8_t* dataToSend, uint8_t* dataReceived, int size);
+
+ /**
+ * Initialize automatic SPI transfer engine.
+ *
+ * Only a single engine is available, and use of it blocks use of all other
+ * chip select usage on the same physical SPI port while it is running.
+ *
+ * @param bufferSize buffer size in bytes
+ */
+ void InitAuto(int bufferSize);
+
+ /**
+ * Frees the automatic SPI transfer engine.
+ */
+ void FreeAuto();
+
+ /**
+ * Set the data to be transmitted by the engine.
+ *
+ * Up to 16 bytes are configurable, and may be followed by up to 127 zero
+ * bytes.
+ *
+ * @param dataToSend data to send (maximum 16 bytes)
+ * @param zeroSize number of zeros to send after the data
+ */
+ void SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize);
+
+ /**
+ * Start running the automatic SPI transfer engine at a periodic rate.
+ *
+ * InitAuto() and SetAutoTransmitData() must be called before calling this
+ * function.
+ *
+ * @param period period between transfers, in seconds (us resolution)
+ */
+ void StartAutoRate(double period);
+
+ /**
+ * Start running the automatic SPI transfer engine when a trigger occurs.
+ *
+ * InitAuto() and SetAutoTransmitData() must be called before calling this
+ * function.
+ *
+ * @param source digital source for the trigger (may be an analog trigger)
+ * @param rising trigger on the rising edge
+ * @param falling trigger on the falling edge
+ */
+ void StartAutoTrigger(DigitalSource& source, bool rising, bool falling);
+
+ /**
+ * Stop running the automatic SPI transfer engine.
+ */
+ void StopAuto();
+
+ /**
+ * Force the engine to make a single transfer.
+ */
+ void ForceAutoRead();
+
+ /**
+ * Read data that has been transferred by the automatic SPI transfer engine.
+ *
+ * Transfers may be made a byte at a time, so it's necessary for the caller
+ * to handle cases where an entire transfer has not been completed.
+ *
+ * Each received data sequence consists of a timestamp followed by the
+ * received data bytes, one byte per word (in the least significant byte).
+ * The length of each received data sequence is the same as the combined
+ * size of the data and zeroSize set in SetAutoTransmitData().
+ *
+ * Blocks until numToRead words have been read or timeout expires.
+ * May be called with numToRead=0 to retrieve how many words are available.
+ *
+ * @param buffer buffer where read words are stored
+ * @param numToRead number of words to read
+ * @param timeout timeout in seconds (ms resolution)
+ * @return Number of words remaining to be read
+ */
+ int ReadAutoReceivedData(uint32_t* buffer, int numToRead, double timeout);
+
+ /**
+ * Get the number of bytes dropped by the automatic SPI transfer engine due
+ * to the receive buffer being full.
+ *
+ * @return Number of bytes dropped
+ */
+ int GetAutoDroppedCount();
+
+ /**
+ * Configure the Auto SPI Stall time between reads.
+ *
+ * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for
+ * MXP.
+ * @param csToSclkTicks the number of ticks to wait before asserting the cs
+ * pin
+ * @param stallTicks the number of ticks to stall for
+ * @param pow2BytesPerRead the number of bytes to read before stalling
+ */
+ void ConfigureAutoStall(HAL_SPIPort port, int csToSclkTicks, int stallTicks,
+ int pow2BytesPerRead);
protected:
-#ifdef WPILIB2017
- int m_port;
-#else
- HAL_SPIPort m_port;
-#endif
- bool m_msbFirst = false; // default little-endian
- bool m_sampleOnTrailing = false; // default data updated on falling edge
- bool m_clk_idle_high = false; // default clock active high
+ hal::SPIPort m_port;
+ bool m_msbFirst = false; // Default little-endian
+ bool m_sampleOnTrailing = false; // Default data updated on falling edge
+ bool m_clockIdleHigh = false; // Default clock active high
private:
void Init();
diff --git a/frc971/wpilib/fpga_time_conversion.cc b/frc971/wpilib/fpga_time_conversion.cc
new file mode 100644
index 0000000..6f9a267
--- /dev/null
+++ b/frc971/wpilib/fpga_time_conversion.cc
@@ -0,0 +1,36 @@
+#include "frc971/wpilib/fpga_time_conversion.h"
+
+#include "aos/util/compiler_memory_barrier.h"
+
+namespace frc971 {
+namespace wpilib {
+
+std::optional<std::chrono::nanoseconds> CalculateFpgaOffset() {
+ aos_compiler_memory_barrier();
+ const hal::fpga_clock::time_point fpga_time_before = hal::fpga_clock::now();
+ aos_compiler_memory_barrier();
+ const aos::monotonic_clock::time_point monotonic_now =
+ aos::monotonic_clock::now();
+ aos_compiler_memory_barrier();
+ const hal::fpga_clock::time_point fpga_time_after = hal::fpga_clock::now();
+ aos_compiler_memory_barrier();
+
+ const std::chrono::nanoseconds fpga_sample_length =
+ fpga_time_after - fpga_time_before;
+
+ if (fpga_sample_length < fpga_sample_length.zero()) {
+ return std::nullopt;
+ }
+ if (fpga_sample_length > std::chrono::microseconds(20)) {
+ return std::nullopt;
+ }
+
+ const std::chrono::nanoseconds fpga_average =
+ (fpga_time_after.time_since_epoch() +
+ fpga_time_before.time_since_epoch()) /
+ 2;
+ return monotonic_now.time_since_epoch() - fpga_average;
+}
+
+} // namespace wpilib
+} // namespace frc971
diff --git a/frc971/wpilib/fpga_time_conversion.h b/frc971/wpilib/fpga_time_conversion.h
new file mode 100644
index 0000000..9862918
--- /dev/null
+++ b/frc971/wpilib/fpga_time_conversion.h
@@ -0,0 +1,56 @@
+#ifndef FRC971_WPILIB_FPGA_TIME_CONVERSION_H_
+#define FRC971_WPILIB_FPGA_TIME_CONVERSION_H_
+
+#include <optional>
+#include <chrono>
+
+#include "aos/time/time.h"
+#include "glog/logging.h"
+#include "hal/cpp/fpga_clock.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Returns the offset from the monotonic clock to the FPGA time. This is defined
+// as `fpga_time - monotonic_time`.
+// Returns nullopt if sampling the time once failed.
+std::optional<std::chrono::nanoseconds> CalculateFpgaOffset();
+
+class FpgaTimeConverter {
+ public:
+ aos::monotonic_clock::time_point FpgaToMonotonic(
+ hal::fpga_clock::time_point fpga_time) {
+ UpdateOffset();
+ return aos::monotonic_clock::epoch() +
+ (fpga_time.time_since_epoch() + offset_);
+ }
+
+ hal::fpga_clock::time_point MonotonicToFpga(
+ aos::monotonic_clock::time_point monotonic_time) {
+ UpdateOffset();
+ return hal::fpga_clock::epoch() +
+ std::chrono::duration_cast<hal::fpga_clock::duration>(
+ monotonic_time.time_since_epoch() - offset_);
+ }
+
+ private:
+ void UpdateOffset() {
+ for (int i = 0; i < 10; ++i) {
+ const auto new_offset = CalculateFpgaOffset();
+ if (new_offset) {
+ offset_ = *new_offset;
+ return;
+ } else if (offset_ != offset_.min()) {
+ return;
+ }
+ }
+ LOG(FATAL) << "Failed to calculate FPGA offset";
+ }
+
+ std::chrono::nanoseconds offset_ = std::chrono::nanoseconds::min();
+};
+
+} // namespace wpilib
+} // namespace frc971
+
+#endif // FRC971_WPILIB_FPGA_TIME_CONVERSION_H_
diff --git a/frc971/wpilib/gyro_sender.cc b/frc971/wpilib/gyro_sender.cc
index e7273ee..c62d40a 100644
--- a/frc971/wpilib/gyro_sender.cc
+++ b/frc971/wpilib/gyro_sender.cc
@@ -137,7 +137,7 @@
if (joystick_state_fetcher_.get() &&
joystick_state_fetcher_->outputs_enabled() &&
zeroing_data_.full()) {
- zero_offset_ = -zeroing_data_.GetAverage();
+ zero_offset_ = -zeroing_data_.GetAverage()(0, 0);
AOS_LOG(INFO, "total zero offset %f\n", zero_offset_);
zeroed_ = true;
}
diff --git a/frc971/wpilib/imu.fbs b/frc971/wpilib/imu.fbs
index f48f31f..65b181b 100644
--- a/frc971/wpilib/imu.fbs
+++ b/frc971/wpilib/imu.fbs
@@ -1,7 +1,55 @@
namespace frc971;
+// The values in the DIAG_STAT register for the ADIS16470.
+table ADIS16470DiagStat {
+ // True indicates that the internal data sampling clock (fSM, see Figure 15
+ // and Figure 16) does not synchronize with the external clock, which only
+ // applies when using scale sync mode (Register MSC_CTRL, Bits[4:2] = 010, see
+ // Table 101). When this occurs, adjust the frequency of the clock signal on
+ // the SYNC pin to operate within the appropriate range.
+ clock_error:bool;
+
+ // True indicates a failure in the flash memory test (Register GLOB_CMD, Bit
+ // 4, see Table 109), which involves a comparison between a cyclic redundancy
+ // check (CRC) computation of the present flash memory and a CRC computation
+ // from the same memory locations at the time of initial programming (during
+ // production process). If this occurs, repeat the same test. If this error
+ // persists, replace the ADIS16470 device.
+ memory_failure:bool;
+
+ // True indicates failure of at least one sensor, at the conclusion of the
+ // self test (Register GLOB_CMD, Bit 2, see Table 109). If this occurs, repeat
+ // the same test. If this error persists, replace the ADIS16470. Motion,
+ // during the execution of this test, can cause a false failure.
+ sensor_failure:bool;
+
+ // True indicates that the voltage across VDD and GND is <2.8 V, which causes
+ // data processing to stop. When VDD ≥ 2.8 V for 250 ms, the ADIS16470
+ // reinitializes itself and starts producing data again.
+ standby_mode:bool;
+
+ // True indicates that the total number of SCLK cycles is not equal to an
+ // integer multiple of 16. When this occurs, repeat the previous communication
+ // sequence. Persistence in this error may indicate a weakness in the SPI
+ // service that the ADIS16470 is receiving from the system it is supporting.
+ spi_communication_error:bool;
+
+ // True indicates that the most recent flash memory update (Register GLOB_CMD,
+ // Bit 3, see Table 109) failed. If this occurs, ensure that VDD ≥ 3 V and
+ // repeat the update attempt. If this error persists, replace the ADIS16470.
+ flash_memory_update_error:bool;
+
+ // True indicates that one of the data paths have experienced an overrun
+ // condition. If this occurs, initiate a reset, using the RST pin (see Table
+ // 5, Pin F3) or Register GLOB_CMD, Bit 7 (see Table 109). See the Serial Port
+ // Operation section for more details on conditions that may cause this bit to
+ // be set to 1.
+ data_path_overrun:bool;
+}
+
// Values returned from an IMU.
-// Published on ".frc971.imu_values"
+// All of these are raw from the hardware, without any form of zeroing or
+// temperature compensation applied.
table IMUValues {
// Gyro readings in radians/second.
// Positive is clockwise looking at the connector.
@@ -35,8 +83,22 @@
// FPGA timestamp when the values were captured.
fpga_timestamp:double;
- // CLOCK_MONOTONIC time in nanoseconds when the values were captured.
+ // CLOCK_MONOTONIC time in nanoseconds when the values were captured,
+ // converted from fpga_timestamp.
monotonic_timestamp_ns:long;
+
+ // For an ADIS16470, the DIAG_STAT value immediately after reset.
+ start_diag_stat:ADIS16470DiagStat;
+ // For an ADIS16470, the DIAG_STAT value after the initial sensor self test we
+ // trigger is finished.
+ self_test_diag_stat:ADIS16470DiagStat;
+ // For an ADIS16470, the DIAG_STAT value associated with the previous set of
+ // readings. This will never change during normal operation, so being 1 cycle
+ // state is OK.
+ previous_reading_diag_stat:ADIS16470DiagStat;
+
+ // The value read from the PROD_ID register.
+ product_id:uint16;
}
root_type IMUValues;
diff --git a/frc971/wpilib/sensor_reader.cc b/frc971/wpilib/sensor_reader.cc
index 5bcb84a..0d970e8 100644
--- a/frc971/wpilib/sensor_reader.cc
+++ b/frc971/wpilib/sensor_reader.cc
@@ -9,6 +9,7 @@
#include "frc971/wpilib/ahal/DigitalInput.h"
#include "frc971/wpilib/ahal/DriverStation.h"
#include "frc971/wpilib/ahal/Utility.h"
+#include "frc971/wpilib/fpga_time_conversion.h"
#include "frc971/wpilib/wpilib_interface.h"
#include "hal/PWM.h"
@@ -60,35 +61,20 @@
monotonic_clock::time_point SensorReader::GetPWMStartTime() {
int32_t status = 0;
- const hal::fpga_clock::time_point new_fpga_time = hal::fpga_clock::time_point(
- hal::fpga_clock::duration(HAL_GetPWMCycleStartTime(&status)));
+ const auto new_fpga_time =
+ hal::fpga_clock::duration(HAL_GetPWMCycleStartTime(&status));
- aos_compiler_memory_barrier();
- const hal::fpga_clock::time_point fpga_time_before = hal::fpga_clock::now();
- aos_compiler_memory_barrier();
- const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
- aos_compiler_memory_barrier();
- const hal::fpga_clock::time_point fpga_time_after = hal::fpga_clock::now();
- aos_compiler_memory_barrier();
-
- const chrono::nanoseconds fpga_sample_length =
- fpga_time_after - fpga_time_before;
- const chrono::nanoseconds fpga_offset =
- hal::fpga_clock::time_point((fpga_time_after.time_since_epoch() +
- fpga_time_before.time_since_epoch()) /
- 2) -
- new_fpga_time;
-
- // Make sure that there wasn't a context switch while we were sampling the
- // clocks. If there was, we are better off rejecting the sample than using
- // it.
- if (ds_->IsSysActive() && fpga_sample_length <= chrono::microseconds(20) &&
- fpga_sample_length >= chrono::microseconds(0)) {
- // Compute when the edge was.
- return monotonic_now - fpga_offset;
- } else {
+ if (!ds_->IsSysActive()) {
return monotonic_clock::min_time;
}
+
+ const auto fpga_offset = CalculateFpgaOffset();
+ // If we failed to sample the offset, just ignore this reading.
+ if (!fpga_offset) {
+ return monotonic_clock::min_time;
+ }
+
+ return monotonic_clock::epoch() + (new_fpga_time + *fpga_offset);
}
void SensorReader::DoStart() {
diff --git a/frc971/zeroing/BUILD b/frc971/zeroing/BUILD
index fe95bdc..a2b908b 100644
--- a/frc971/zeroing/BUILD
+++ b/frc971/zeroing/BUILD
@@ -7,6 +7,10 @@
hdrs = [
"averager.h",
],
+ deps = [
+ "@com_github_google_glog//:glog",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
)
cc_test(
@@ -17,6 +21,36 @@
deps = [
":averager",
"//aos/testing:googletest",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
+cc_library(
+ name = "imu_zeroer",
+ srcs = [
+ "imu_zeroer.cc",
+ ],
+ hdrs = [
+ "imu_zeroer.h",
+ ],
+ deps = [
+ ":averager",
+ "//frc971/wpilib:imu_fbs",
+ "@com_github_google_glog//:glog",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
+cc_test(
+ name = "imu_zeroer_test",
+ srcs = [
+ "imu_zeroer_test.cc",
+ ],
+ deps = [
+ ":imu_zeroer",
+ "//aos:flatbuffers",
+ "//aos/testing:googletest",
+ "@org_tuxfamily_eigen//:eigen",
],
)
@@ -73,3 +107,14 @@
"//aos/testing:googletest",
],
)
+
+cc_test(
+ name = "unwrap_test",
+ srcs = [
+ "unwrap_test.cc",
+ ],
+ deps = [
+ ":wrap",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/frc971/zeroing/averager.h b/frc971/zeroing/averager.h
index 879c246..b6ff09d 100644
--- a/frc971/zeroing/averager.h
+++ b/frc971/zeroing/averager.h
@@ -5,45 +5,79 @@
#include <array>
#include <stdint.h>
+#include "Eigen/Dense"
+#include "glog/logging.h"
+
namespace frc971 {
namespace zeroing {
// Averages a set of given numbers. Numbers are given one at a time. Once full
// the average may be requested.
-template <typename data_type, size_t data_size>
+// TODO(james): Look at deduplicating this with some of the work in the
+// MoveDetector.
+template <typename Scalar, size_t num_samples, int rows_per_sample = 1>
class Averager {
public:
+ typedef Eigen::Matrix<Scalar, rows_per_sample, 1> Vector;
// Adds one data point to the set of data points to be averaged.
- // If more than "data_size" samples are added, they will start overwriting
+ // If more than "num_samples" samples are added, they will start overwriting
// the oldest ones.
- void AddData(data_type data) {
+ void AddData(Scalar data) {
+ CHECK_EQ(1, rows_per_sample);
+ AddData(Vector(data));
+ }
+ void AddData(const Vector &data) {
data_[data_point_index_] = data;
- num_data_points_ = ::std::min(data_size, num_data_points_ + 1);
- data_point_index_ = (data_point_index_ + 1) % data_size;
+ num_data_points_ = std::min(num_samples, num_data_points_ + 1);
+ data_point_index_ = (data_point_index_ + 1) % num_samples;
}
// Returns the average of the data points.
- data_type GetAverage() const {
- // TODO(phil): What do we want to do without any elements?
+ Vector GetAverage() const {
if (num_data_points_ == 0) {
- return 0;
+ return Vector::Zero();
}
- data_type average = 0;
- for (data_type data : data_) {
+ Vector average;
+ average.setZero();
+ for (const Vector &data : data_) {
average += data;
}
return average / num_data_points_;
}
- // Returns true when we've gathered data_size data points.
- bool full() const { return num_data_points_ >= data_size; };
+ // Return the difference between the min and max values in the data buffer.
+ Scalar GetRange() const {
+ if (num_data_points_ == 0) {
+ return 0.0;
+ }
+ Vector min_value;
+ min_value.setConstant(std::numeric_limits<Scalar>::max());
+ Vector max_value;
+ max_value.setConstant(std::numeric_limits<Scalar>::lowest());
+ // The array will always fill up starting at zero, so we can iterate from
+ // zero safely.
+ for (size_t ii = 0; ii < num_data_points_; ++ii) {
+ const Vector &value = data_[ii];
+ min_value = min_value.cwiseMin(value);
+ max_value = max_value.cwiseMax(value);
+ }
+ return (max_value - min_value).maxCoeff();
+ }
- size_t size() const { return data_size; }
+ void Reset() {
+ num_data_points_ = 0;
+ data_point_index_ = 0;
+ }
+
+ // Returns true when we've gathered num_samples data points.
+ bool full() const { return num_data_points_ >= num_samples; };
+
+ size_t size() const { return num_samples; }
private:
// Data points to be averaged.
- ::std::array<data_type, data_size> data_;
+ std::array<Vector, num_samples> data_;
// Which data point in "data_" will be filled in next.
size_t data_point_index_ = 0;
// Number of data points added via AddData().
diff --git a/frc971/zeroing/averager_test.cc b/frc971/zeroing/averager_test.cc
index 930a3cb..9f8f727 100644
--- a/frc971/zeroing/averager_test.cc
+++ b/frc971/zeroing/averager_test.cc
@@ -17,7 +17,7 @@
averager.AddData(static_cast<int>(i));
}
ASSERT_TRUE(averager.full());
- ASSERT_EQ(2, averager.GetAverage());
+ ASSERT_EQ(2, averager.GetAverage()(0, 0));
}
// Makes sure that we can compute the average of a bunch of floats.
@@ -28,7 +28,32 @@
averager.AddData(static_cast<float>(i) / 3.0);
}
ASSERT_TRUE(averager.full());
- ASSERT_NEAR(16.5, averager.GetAverage(), 0.001);
+ ASSERT_NEAR(16.5, averager.GetAverage()(0, 0), 0.001);
+}
+
+TEST_F(AveragerTest, CalculateRange) {
+ Averager<float, 5, 2> averager;
+ ASSERT_EQ(0, averager.GetRange());
+ averager.AddData({100, 10});
+ averager.AddData({105, 15});
+ averager.AddData({90, 9});
+ ASSERT_EQ(15, averager.GetRange());
+ for (size_t ii = 0; ii < averager.size(); ++ii) {
+ averager.AddData({10, 20});
+ }
+ ASSERT_EQ(0, averager.GetRange());
+}
+
+TEST_F(AveragerTest, ResetAverager) {
+ Averager<float, 5> averager;
+ for (size_t ii = 0; ii < averager.size(); ++ii) {
+ averager.AddData(10);
+ }
+ ASSERT_TRUE(averager.full());
+ ASSERT_EQ(10.0, averager.GetAverage()(0, 0));
+ averager.Reset();
+ ASSERT_FALSE(averager.full());
+ ASSERT_EQ(0.0, averager.GetAverage()(0, 0));
}
} // namespace zeroing
diff --git a/frc971/zeroing/imu_zeroer.cc b/frc971/zeroing/imu_zeroer.cc
new file mode 100644
index 0000000..25bf6f6
--- /dev/null
+++ b/frc971/zeroing/imu_zeroer.cc
@@ -0,0 +1,52 @@
+#include "frc971/zeroing/imu_zeroer.h"
+
+namespace frc971::zeroing {
+
+ImuZeroer::ImuZeroer() {
+ gyro_average_.setZero();
+ last_gyro_sample_.setZero();
+ last_accel_sample_.setZero();
+}
+
+bool ImuZeroer::Zeroed() const { return zeroed_ || Faulted(); }
+
+bool ImuZeroer::Faulted() const { return faulted_; }
+
+Eigen::Vector3d ImuZeroer::ZeroedGyro() const {
+ return last_gyro_sample_ - gyro_average_;
+}
+Eigen::Vector3d ImuZeroer::ZeroedAccel() const { return last_accel_sample_; }
+Eigen::Vector3d ImuZeroer::GyroOffset() const { return gyro_average_; }
+
+bool ImuZeroer::GyroZeroReady() const {
+ return gyro_averager_.full() && gyro_averager_.GetRange() < kGyroMaxVariation;
+}
+
+bool ImuZeroer::AccelZeroReady() const {
+ return accel_averager_.full() &&
+ accel_averager_.GetRange() < kAccelMaxVariation;
+}
+
+void ImuZeroer::ProcessMeasurement(const IMUValues &values) {
+ last_gyro_sample_ << values.gyro_x(), values.gyro_y(), values.gyro_z();
+ gyro_averager_.AddData(last_gyro_sample_);
+ last_accel_sample_ << values.accelerometer_x(), values.accelerometer_y(),
+ values.accelerometer_z();
+ accel_averager_.AddData(last_accel_sample_);
+ if (GyroZeroReady() && AccelZeroReady()) {
+ if (!zeroed_) {
+ zeroed_ = true;
+ gyro_average_ = gyro_averager_.GetAverage();
+ } else {
+ // If we got a new zero and it is substantially different from the
+ // original zero, fault.
+ if ((gyro_averager_.GetAverage() - gyro_average_).norm() >
+ kGyroFaultVariation) {
+ faulted_ = true;
+ }
+ }
+ gyro_averager_.Reset();
+ }
+}
+
+} // namespace frc971::zeroing
diff --git a/frc971/zeroing/imu_zeroer.h b/frc971/zeroing/imu_zeroer.h
new file mode 100644
index 0000000..e95c231
--- /dev/null
+++ b/frc971/zeroing/imu_zeroer.h
@@ -0,0 +1,54 @@
+#ifndef FRC971_ZEROING_IMU_ZEROER_H_
+#define FRC971_ZEROING_IMU_ZEROER_H_
+
+#include "frc971/wpilib/imu_generated.h"
+#include "frc971/zeroing/averager.h"
+
+namespace frc971::zeroing {
+
+// This class handles processing IMU measurements and zeroing them once it is
+// able to do so.
+class ImuZeroer {
+ public:
+ // Average 5 seconds of data (assuming 2kHz sampling rate).
+ static constexpr size_t kSamplesToAverage = 10000.0;
+
+ ImuZeroer();
+ bool Zeroed() const;
+ bool Faulted() const;
+ void ProcessMeasurement(const IMUValues &values);
+ Eigen::Vector3d GyroOffset() const;
+ Eigen::Vector3d ZeroedGyro() const;
+ Eigen::Vector3d ZeroedAccel() const;
+ private:
+ // Max variation (difference between the maximum and minimum value) in a
+ // kSamplesToAverage range before we allow using the samples for zeroing.
+ // These values are currently based on looking at results from the ADIS16448.
+ static constexpr double kGyroMaxVariation = 0.02; // rad / sec
+ // Max variation in the range before we consider the accelerometer readings to
+ // be steady.
+ static constexpr double kAccelMaxVariation = 0.02; // g's
+ // If we ever are able to rezero and get a zero that is more than
+ // kGyroFaultVariation away from the original zeroing, fault.
+ static constexpr double kGyroFaultVariation = 0.005; // rad / sec
+
+ bool GyroZeroReady() const;
+ bool AccelZeroReady() const;
+
+ Averager<double, kSamplesToAverage, 3> gyro_averager_;
+ // Averager for the accelerometer readings--we don't currently actually
+ // average the readings, but we do check that the accelerometer readings have
+ // stayed roughly constant during the calibration period.
+ Averager<double, kSamplesToAverage, 3> accel_averager_;
+ // The average zero position of the gyro.
+ Eigen::Vector3d gyro_average_;
+ Eigen::Vector3d last_gyro_sample_;
+ Eigen::Vector3d last_accel_sample_;
+ // Whether we have currently zeroed yet.
+ bool zeroed_ = false;
+ // Whether the zeroing has faulted at any point thus far.
+ bool faulted_ = false;
+};
+
+} // namespace frc971::zeroing
+#endif // FRC971_ZEROING_IMU_ZEROER_H_
diff --git a/frc971/zeroing/imu_zeroer_test.cc b/frc971/zeroing/imu_zeroer_test.cc
new file mode 100644
index 0000000..9919e2f
--- /dev/null
+++ b/frc971/zeroing/imu_zeroer_test.cc
@@ -0,0 +1,159 @@
+#include "aos/flatbuffers.h"
+#include "gtest/gtest.h"
+#include "frc971/zeroing/imu_zeroer.h"
+
+namespace frc971::zeroing {
+
+aos::FlatbufferDetachedBuffer<IMUValues> MakeMeasurement(
+ const Eigen::Vector3d &gyro, const Eigen::Vector3d &accel) {
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(1);
+ IMUValuesBuilder builder(fbb);
+ builder.add_gyro_x(gyro.x());
+ builder.add_gyro_y(gyro.y());
+ builder.add_gyro_z(gyro.z());
+ builder.add_accelerometer_x(accel.x());
+ builder.add_accelerometer_y(accel.y());
+ builder.add_accelerometer_z(accel.z());
+ fbb.Finish(builder.Finish());
+ return fbb.Release();
+}
+
+// Tests that when we initialize everything is in a sane state.
+TEST(ImuZeroerTest, InitializeUnzeroed) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ ASSERT_FALSE(zeroer.Faulted());
+ ASSERT_EQ(0.0, zeroer.GyroOffset().norm());
+ ASSERT_EQ(0.0, zeroer.ZeroedGyro().norm());
+ ASSERT_EQ(0.0, zeroer.ZeroedAccel().norm());
+ // A measurement before we are zeroed should just result in the measurement
+ // being passed through without modification.
+ zeroer.ProcessMeasurement(MakeMeasurement({1, 2, 3}, {4, 5, 6}).message());
+ ASSERT_FALSE(zeroer.Zeroed());
+ ASSERT_FALSE(zeroer.Faulted());
+ ASSERT_EQ(0.0, zeroer.GyroOffset().norm());
+ ASSERT_EQ(1.0, zeroer.ZeroedGyro().x());
+ ASSERT_EQ(2.0, zeroer.ZeroedGyro().y());
+ ASSERT_EQ(3.0, zeroer.ZeroedGyro().z());
+ ASSERT_EQ(4.0, zeroer.ZeroedAccel().x());
+ ASSERT_EQ(5.0, zeroer.ZeroedAccel().y());
+ ASSERT_EQ(6.0, zeroer.ZeroedAccel().z());
+}
+
+// Tests that we zero if we receive a bunch of identical measurements.
+TEST(ImuZeroerTest, ZeroOnConstantData) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Zeroed());
+ zeroer.ProcessMeasurement(MakeMeasurement({1, 2, 3}, {4, 5, 6}).message());
+ }
+ ASSERT_TRUE(zeroer.Zeroed());
+ ASSERT_FALSE(zeroer.Faulted());
+ // Gyro should be zeroed to {1, 2, 3}.
+ ASSERT_EQ(1.0, zeroer.GyroOffset().x());
+ ASSERT_EQ(2.0, zeroer.GyroOffset().y());
+ ASSERT_EQ(3.0, zeroer.GyroOffset().z());
+ ASSERT_EQ(0.0, zeroer.ZeroedGyro().x());
+ ASSERT_EQ(0.0, zeroer.ZeroedGyro().y());
+ ASSERT_EQ(0.0, zeroer.ZeroedGyro().z());
+ // Accelerometer readings should not be affected.
+ ASSERT_EQ(4.0, zeroer.ZeroedAccel().x());
+ ASSERT_EQ(5.0, zeroer.ZeroedAccel().y());
+ ASSERT_EQ(6.0, zeroer.ZeroedAccel().z());
+ // If we get another measurement offset by {1, 1, 1} we should read the result
+ // as {1, 1, 1}.
+ zeroer.ProcessMeasurement(MakeMeasurement({2, 3, 4}, {0, 0, 0}).message());
+ ASSERT_FALSE(zeroer.Faulted());
+ ASSERT_EQ(1.0, zeroer.ZeroedGyro().x());
+ ASSERT_EQ(1.0, zeroer.ZeroedGyro().y());
+ ASSERT_EQ(1.0, zeroer.ZeroedGyro().z());
+}
+
+// Tests that we tolerate small amounts of noise in the incoming data and can
+// still zero.
+TEST(ImuZeroerTest, ZeroOnLowNoiseData) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Zeroed());
+ const double offset =
+ (static_cast<double>(ii) / (ImuZeroer::kSamplesToAverage - 1) - 0.5) *
+ 0.01;
+ zeroer.ProcessMeasurement(
+ MakeMeasurement({1 + offset, 2 + offset, 3 + offset},
+ {4 + offset, 5 + offset, 6 + offset})
+ .message());
+ }
+ ASSERT_TRUE(zeroer.Zeroed());
+ ASSERT_FALSE(zeroer.Faulted());
+ // Gyro should be zeroed to {1, 2, 3}.
+ ASSERT_NEAR(1.0, zeroer.GyroOffset().x(), 1e-10);
+ ASSERT_NEAR(2.0, zeroer.GyroOffset().y(), 1e-10);
+ ASSERT_NEAR(3.0, zeroer.GyroOffset().z(), 1e-10);
+ // If we get another measurement offset by {1, 1, 1} we should read the result
+ // as {1, 1, 1}.
+ zeroer.ProcessMeasurement(MakeMeasurement({2, 3, 4}, {0, 0, 0}).message());
+ ASSERT_FALSE(zeroer.Faulted());
+ ASSERT_NEAR(1.0, zeroer.ZeroedGyro().x(), 1e-10);
+ ASSERT_NEAR(1.0, zeroer.ZeroedGyro().y(), 1e-10);
+ ASSERT_NEAR(1.0, zeroer.ZeroedGyro().z(), 1e-10);
+ ASSERT_EQ(0.0, zeroer.ZeroedAccel().x());
+ ASSERT_EQ(0.0, zeroer.ZeroedAccel().y());
+ ASSERT_EQ(0.0, zeroer.ZeroedAccel().z());
+}
+
+// Tests that we do not zero if there is too much noise in the input data.
+TEST(ImuZeroerTest, NoZeroOnHighNoiseData) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Zeroed());
+ const double offset =
+ (static_cast<double>(ii) / (ImuZeroer::kSamplesToAverage - 1) - 0.5) *
+ 1.0;
+ zeroer.ProcessMeasurement(
+ MakeMeasurement({1 + offset, 2 + offset, 3 + offset},
+ {4 + offset, 5 + offset, 6 + offset})
+ .message());
+ }
+ ASSERT_FALSE(zeroer.Zeroed());
+ ASSERT_FALSE(zeroer.Faulted());
+}
+
+// Tests that we fault if we successfully rezero and get a significantly offset
+// zero.
+TEST(ImuZeroerTest, FaultOnNewZero) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Zeroed());
+ zeroer.ProcessMeasurement(MakeMeasurement({1, 2, 3}, {4, 5, 6}).message());
+ }
+ ASSERT_TRUE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Faulted())
+ << "We should not fault until we complete a second cycle of zeroing.";
+ zeroer.ProcessMeasurement(MakeMeasurement({1, 5, 3}, {4, 5, 6}).message());
+ }
+ ASSERT_TRUE(zeroer.Faulted());
+}
+
+// Tests that we do not fault if the zero only changes by a small amount.
+TEST(ImuZeroerTest, NoFaultOnSimilarZero) {
+ ImuZeroer zeroer;
+ ASSERT_FALSE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ ASSERT_FALSE(zeroer.Zeroed());
+ zeroer.ProcessMeasurement(MakeMeasurement({1, 2, 3}, {4, 5, 6}).message());
+ }
+ ASSERT_TRUE(zeroer.Zeroed());
+ for (size_t ii = 0; ii < ImuZeroer::kSamplesToAverage; ++ii) {
+ zeroer.ProcessMeasurement(
+ MakeMeasurement({1, 2.0001, 3}, {4, 5, 6}).message());
+ }
+ ASSERT_FALSE(zeroer.Faulted());
+}
+
+} // namespace frc971::zeroing
diff --git a/y2018/control_loops/superstructure/intake/unwrap_test.cc b/frc971/zeroing/unwrap_test.cc
similarity index 89%
rename from y2018/control_loops/superstructure/intake/unwrap_test.cc
rename to frc971/zeroing/unwrap_test.cc
index 6211846..d1e6db9 100644
--- a/y2018/control_loops/superstructure/intake/unwrap_test.cc
+++ b/frc971/zeroing/unwrap_test.cc
@@ -1,10 +1,8 @@
+#include "frc971/zeroing/wrap.h"
#include "gtest/gtest.h"
-#include "y2018/control_loops/superstructure/intake/sensor_unwrap.h"
-namespace y2018 {
-namespace control_loops {
-namespace superstructure {
-namespace intake {
+namespace frc971 {
+namespace zeroing {
namespace testing {
TEST(SensorTest, UnwrapOnce) {
@@ -65,7 +63,5 @@
}
} // namespace testing
-} // namespace intake
-} // namespace superstructure
-} // namespace control_loops
-} // namespace y2018
+} // namespace zeroing
+} // namespace frc971
diff --git a/frc971/zeroing/wrap.cc b/frc971/zeroing/wrap.cc
index 593ac97..1a0504f 100644
--- a/frc971/zeroing/wrap.cc
+++ b/frc971/zeroing/wrap.cc
@@ -5,6 +5,57 @@
namespace frc971 {
namespace zeroing {
+UnwrapSensor::UnwrapSensor(double sensor_offset, double sensor_range)
+ : sensor_offset_(sensor_offset), sensor_range_(sensor_range) {
+ Reset();
+}
+
+double UnwrapSensor::Unwrap(double current_sensor_value) {
+ // First time the function is called it will use that value to initialize the
+ // wrap calculation. This catches cases where the offset and first value
+ // difference triggers an unwanted wrap at the first calculation.
+ if (uninitialized_ == true) {
+ uninitialized_ = false;
+ } else {
+ // Calculates the lower sensor value and set the max sensor range
+ // If offset is not 0, this will correct the zeroing offset
+ const double sensor_min_value = sensor_offset_;
+ const double sensor_max_value = sensor_range_ + sensor_min_value;
+
+ // Check if provided sensor value is within the range. This to prevent the
+ // function to get out of sync. Will not throw an error, but continue and
+ // return the value + wrapped factor and not process this value.
+ if (current_sensor_value < sensor_min_value ||
+ current_sensor_value > sensor_max_value) {
+ return current_sensor_value + (sensor_range_ * wrap_count_);
+ }
+
+ // Calculate the positive or negative movement
+ const double sensor_move = current_sensor_value - sensor_last_value_;
+
+ // Function assumes that a movement of more then 1/2 of the range
+ // indicates that we wrapped, instead of moved very fast.
+ if (std::abs(sensor_move) > (sensor_range_ / 2)) {
+ if (sensor_move >= 0) {
+ // sensor moved past the sensor_min_value
+ wrap_count_ -= 1;
+ } else {
+ // sensor moved past the sensor_max_value
+ wrap_count_ += 1;
+ }
+ }
+ }
+ sensor_last_value_ = current_sensor_value;
+ // return the unwrapped sensor value
+ return current_sensor_value + (sensor_range_ * wrap_count_);
+}
+
+void UnwrapSensor::Reset() {
+ wrap_count_ = 0;
+ sensor_last_value_ = sensor_offset_;
+ uninitialized_ = true;
+}
+
float Wrap(float nearest, float value, float period) {
return remainderf(value - nearest, period) + nearest;
}
diff --git a/frc971/zeroing/wrap.h b/frc971/zeroing/wrap.h
index 976e605..f96b9fb 100644
--- a/frc971/zeroing/wrap.h
+++ b/frc971/zeroing/wrap.h
@@ -4,6 +4,40 @@
namespace frc971 {
namespace zeroing {
+// UnwrapSensor takes in a sensor value from a sensor that loops in a certain
+// interval. ex(the sensor moves from 0 to 10 and back to 0 while moving the
+// same direction) By checking for big gaps in sensor readings it assumes you
+// have wrapped either back or forwards and handles accordingly. It returns the
+// overall sensor value.
+
+class UnwrapSensor {
+ public:
+ // The sensor_offset (+ or -) present the sensor value that is 'zero'
+ // The sensor_range presents the absolute value of the sensor range from 0 to
+ // sensor_range. This will be adjusted using the sensor_offset
+ UnwrapSensor(double sensor_offset, double sensor_range);
+
+ // Takes a wrapped sensor value and unwraps it to give you its total position.
+ double Unwrap(double current_sensor_value);
+
+ void Reset();
+
+ int sensor_wrapped() const { return wrap_count_; }
+
+ private:
+ const double sensor_offset_, sensor_range_;
+
+ // The last value given from set_position, starts at offset
+ double sensor_last_value_ = sensor_offset_;
+
+ // Log if sensor is in wrapped state in either direction
+ int wrap_count_ = 0;
+
+ // function waits for first call with a value to set sensor_last_value_. Will
+ // start to calculate the spring unwrap at the second function call.
+ bool uninitialized_ = true;
+};
+
// Returns a modified value which has been wrapped such that it is +- period/2
// away from nearest.
double Wrap(double nearest, double value, double period);
diff --git a/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp b/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
index 37c5f0e..977d447 100644
--- a/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
+++ b/third_party/allwpilib/hal/src/main/native/athena/SPI.cpp
@@ -565,12 +565,18 @@
void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
int32_t dataSize, int32_t zeroSize,
int32_t* status) {
- if (dataSize < 0 || dataSize > 32) {
+ static_assert(tSPI::kNumAutoTxRegisters >= 6,
+ "FPGA does not have enough tx registers");
+ static_assert(tSPI::kNumAutoTxElements == 4,
+ "FPGA has the wrong number of tx elements");
+ // 24 = 6 * 4, but the documentation needs updating if it ever changes, so
+ // just hard-code it here.
+ if (dataSize < 0 || dataSize > 23) {
*status = PARAMETER_OUT_OF_RANGE;
return;
}
- if (zeroSize < 0 || zeroSize > 127) {
+ if (zeroSize < 0 || zeroSize >= 128) {
*status = PARAMETER_OUT_OF_RANGE;
return;
}
diff --git a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
index 8e721bc..9bc9564 100644
--- a/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
+++ b/third_party/allwpilib/wpilibc/src/main/native/include/frc/SPI.h
@@ -166,10 +166,10 @@
/**
* Set the data to be transmitted by the engine.
*
- * Up to 16 bytes are configurable, and may be followed by up to 127 zero
+ * Up to 23 bytes are configurable, and may be followed by up to 127 zero
* bytes.
*
- * @param dataToSend data to send (maximum 16 bytes)
+ * @param dataToSend data to send (maximum 23 bytes)
* @param zeroSize number of zeros to send after the data
*/
void SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize);
diff --git a/third_party/linux/BUILD b/third_party/linux/BUILD
deleted file mode 100644
index 007b913..0000000
--- a/third_party/linux/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-licenses(["notice"])
-
-package(default_visibility = ["//visibility:public"])
-
-filegroup(
- name = "sctp",
- srcs = [
- "sctp.h",
- ],
-)
diff --git a/third_party/linux/sctp.h b/third_party/linux/sctp.h
deleted file mode 100644
index bd82046..0000000
--- a/third_party/linux/sctp.h
+++ /dev/null
@@ -1,1105 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/* SCTP kernel implementation
- * (C) Copyright IBM Corp. 2001, 2004
- * Copyright (c) 1999-2000 Cisco, Inc.
- * Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2002 Intel Corp.
- *
- * This file is part of the SCTP kernel implementation
- *
- * This header represents the structures and constants needed to support
- * the SCTP Extension to the Sockets API.
- *
- * This SCTP implementation is free software;
- * you can redistribute it and/or modify it under the terms of
- * the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This SCTP implementation 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Please send any bug reports or fixes you make to the
- * email address(es):
- * lksctp developers <linux-sctp@vger.kernel.org>
- *
- * Or submit a bug report through the following website:
- * http://www.sf.net/projects/lksctp
- *
- * Written or modified by:
- * La Monte H.P. Yarroll <piggy@acm.org>
- * R. Stewart <randall@sctp.chicago.il.us>
- * K. Morneau <kmorneau@cisco.com>
- * Q. Xie <qxie1@email.mot.com>
- * Karl Knutson <karl@athena.chicago.il.us>
- * Jon Grimm <jgrimm@us.ibm.com>
- * Daisy Chang <daisyc@us.ibm.com>
- * Ryan Layer <rmlayer@us.ibm.com>
- * Ardelle Fan <ardelle.fan@intel.com>
- * Sridhar Samudrala <sri@us.ibm.com>
- * Inaky Perez-Gonzalez <inaky.gonzalez@intel.com>
- * Vlad Yasevich <vladislav.yasevich@hp.com>
- *
- * Any bugs reported given to us we will try to fix... any fixes shared will
- * be incorporated into the next SCTP release.
- */
-
-#ifndef _SCTP_H
-#define _SCTP_H
-
-#include <linux/types.h>
-#include <linux/socket.h>
-
-typedef __s32 sctp_assoc_t;
-
-/* The following symbols come from the Sockets API Extensions for
- * SCTP <draft-ietf-tsvwg-sctpsocket-07.txt>.
- */
-#define SCTP_RTOINFO 0
-#define SCTP_ASSOCINFO 1
-#define SCTP_INITMSG 2
-#define SCTP_NODELAY 3 /* Get/set nodelay option. */
-#define SCTP_AUTOCLOSE 4
-#define SCTP_SET_PEER_PRIMARY_ADDR 5
-#define SCTP_PRIMARY_ADDR 6
-#define SCTP_ADAPTATION_LAYER 7
-#define SCTP_DISABLE_FRAGMENTS 8
-#define SCTP_PEER_ADDR_PARAMS 9
-#define SCTP_DEFAULT_SEND_PARAM 10
-#define SCTP_EVENTS 11
-#define SCTP_I_WANT_MAPPED_V4_ADDR 12 /* Turn on/off mapped v4 addresses */
-#define SCTP_MAXSEG 13 /* Get/set maximum fragment. */
-#define SCTP_STATUS 14
-#define SCTP_GET_PEER_ADDR_INFO 15
-#define SCTP_DELAYED_ACK_TIME 16
-#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME
-#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME
-#define SCTP_CONTEXT 17
-#define SCTP_FRAGMENT_INTERLEAVE 18
-#define SCTP_PARTIAL_DELIVERY_POINT 19 /* Set/Get partial delivery point */
-#define SCTP_MAX_BURST 20 /* Set/Get max burst */
-#define SCTP_AUTH_CHUNK 21 /* Set only: add a chunk type to authenticate */
-#define SCTP_HMAC_IDENT 22
-#define SCTP_AUTH_KEY 23
-#define SCTP_AUTH_ACTIVE_KEY 24
-#define SCTP_AUTH_DELETE_KEY 25
-#define SCTP_PEER_AUTH_CHUNKS 26 /* Read only */
-#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */
-#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */
-#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */
-#define SCTP_AUTO_ASCONF 30
-#define SCTP_PEER_ADDR_THLDS 31
-#define SCTP_RECVRCVINFO 32
-#define SCTP_RECVNXTINFO 33
-#define SCTP_DEFAULT_SNDINFO 34
-
-/* Internal Socket Options. Some of the sctp library functions are
- * implemented using these socket options.
- */
-#define SCTP_SOCKOPT_BINDX_ADD 100 /* BINDX requests for adding addrs */
-#define SCTP_SOCKOPT_BINDX_REM 101 /* BINDX requests for removing addrs. */
-#define SCTP_SOCKOPT_PEELOFF 102 /* peel off association. */
-/* Options 104-106 are deprecated and removed. Do not use this space */
-#define SCTP_SOCKOPT_CONNECTX_OLD 107 /* CONNECTX old requests. */
-#define SCTP_GET_PEER_ADDRS 108 /* Get all peer address. */
-#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local address. */
-#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */
-#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */
-#define SCTP_GET_ASSOC_STATS 112 /* Read only */
-#define SCTP_PR_SUPPORTED 113
-#define SCTP_DEFAULT_PRINFO 114
-#define SCTP_PR_ASSOC_STATUS 115
-#define SCTP_PR_STREAM_STATUS 116
-#define SCTP_RECONFIG_SUPPORTED 117
-#define SCTP_ENABLE_STREAM_RESET 118
-#define SCTP_RESET_STREAMS 119
-#define SCTP_RESET_ASSOC 120
-#define SCTP_ADD_STREAMS 121
-#define SCTP_SOCKOPT_PEELOFF_FLAGS 122
-#define SCTP_STREAM_SCHEDULER 123
-#define SCTP_STREAM_SCHEDULER_VALUE 124
-
-/* PR-SCTP policies */
-#define SCTP_PR_SCTP_NONE 0x0000
-#define SCTP_PR_SCTP_TTL 0x0010
-#define SCTP_PR_SCTP_RTX 0x0020
-#define SCTP_PR_SCTP_PRIO 0x0030
-#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_PRIO
-#define SCTP_PR_SCTP_MASK 0x0030
-
-#define __SCTP_PR_INDEX(x) ((x >> 4) - 1)
-#define SCTP_PR_INDEX(x) __SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x)
-
-#define SCTP_PR_POLICY(x) ((x) & SCTP_PR_SCTP_MASK)
-#define SCTP_PR_SET_POLICY(flags, x) \
- do { \
- flags &= ~SCTP_PR_SCTP_MASK; \
- flags |= x; \
- } while (0)
-
-#define SCTP_PR_TTL_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL)
-#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX)
-#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO)
-
-/* For enable stream reset */
-#define SCTP_ENABLE_RESET_STREAM_REQ 0x01
-#define SCTP_ENABLE_RESET_ASSOC_REQ 0x02
-#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04
-#define SCTP_ENABLE_STRRESET_MASK 0x07
-
-#define SCTP_STREAM_RESET_INCOMING 0x01
-#define SCTP_STREAM_RESET_OUTGOING 0x02
-
-/* These are bit fields for msghdr->msg_flags. See section 5.1. */
-/* On user space Linux, these live in <bits/socket.h> as an enum. */
-enum sctp_msg_flags {
- MSG_NOTIFICATION = 0x8000,
-#define MSG_NOTIFICATION MSG_NOTIFICATION
-};
-
-/* 5.3.1 SCTP Initiation Structure (SCTP_INIT)
- *
- * This cmsghdr structure provides information for initializing new
- * SCTP associations with sendmsg(). The SCTP_INITMSG socket option
- * uses this same data structure. This structure is not used for
- * recvmsg().
- *
- * cmsg_level cmsg_type cmsg_data[]
- * ------------ ------------ ----------------------
- * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg
- */
-struct sctp_initmsg {
- __u16 sinit_num_ostreams;
- __u16 sinit_max_instreams;
- __u16 sinit_max_attempts;
- __u16 sinit_max_init_timeo;
-};
-
-/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV)
- *
- * This cmsghdr structure specifies SCTP options for sendmsg() and
- * describes SCTP header information about a received message through
- * recvmsg().
- *
- * cmsg_level cmsg_type cmsg_data[]
- * ------------ ------------ ----------------------
- * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo
- */
-struct sctp_sndrcvinfo {
- __u16 sinfo_stream;
- __u16 sinfo_ssn;
- __u16 sinfo_flags;
- __u32 sinfo_ppid;
- __u32 sinfo_context;
- __u32 sinfo_timetolive;
- __u32 sinfo_tsn;
- __u32 sinfo_cumtsn;
- sctp_assoc_t sinfo_assoc_id;
-};
-
-/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
- *
- * This cmsghdr structure specifies SCTP options for sendmsg().
- *
- * cmsg_level cmsg_type cmsg_data[]
- * ------------ ------------ -------------------
- * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo
- */
-struct sctp_sndinfo {
- __u16 snd_sid;
- __u16 snd_flags;
- __u32 snd_ppid;
- __u32 snd_context;
- sctp_assoc_t snd_assoc_id;
-};
-
-/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO)
- *
- * This cmsghdr structure describes SCTP receive information
- * about a received message through recvmsg().
- *
- * cmsg_level cmsg_type cmsg_data[]
- * ------------ ------------ -------------------
- * IPPROTO_SCTP SCTP_RCVINFO struct sctp_rcvinfo
- */
-struct sctp_rcvinfo {
- __u16 rcv_sid;
- __u16 rcv_ssn;
- __u16 rcv_flags;
- __u32 rcv_ppid;
- __u32 rcv_tsn;
- __u32 rcv_cumtsn;
- __u32 rcv_context;
- sctp_assoc_t rcv_assoc_id;
-};
-
-/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO)
- *
- * This cmsghdr structure describes SCTP receive information
- * of the next message that will be delivered through recvmsg()
- * if this information is already available when delivering
- * the current message.
- *
- * cmsg_level cmsg_type cmsg_data[]
- * ------------ ------------ -------------------
- * IPPROTO_SCTP SCTP_NXTINFO struct sctp_nxtinfo
- */
-struct sctp_nxtinfo {
- __u16 nxt_sid;
- __u16 nxt_flags;
- __u32 nxt_ppid;
- __u32 nxt_length;
- sctp_assoc_t nxt_assoc_id;
-};
-
-/*
- * sinfo_flags: 16 bits (unsigned integer)
- *
- * This field may contain any of the following flags and is composed of
- * a bitwise OR of these values.
- */
-enum sctp_sinfo_flags {
- SCTP_UNORDERED = (1 << 0), /* Send/receive message unordered. */
- SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */
- SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */
- SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */
- SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */
- SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */
-};
-
-typedef union {
- __u8 raw;
- struct sctp_initmsg init;
- struct sctp_sndrcvinfo sndrcv;
-} sctp_cmsg_data_t;
-
-/* These are cmsg_types. */
-typedef enum sctp_cmsg_type {
- SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */
-#define SCTP_INIT SCTP_INIT
- SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */
-#define SCTP_SNDRCV SCTP_SNDRCV
- SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */
-#define SCTP_SNDINFO SCTP_SNDINFO
- SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */
-#define SCTP_RCVINFO SCTP_RCVINFO
- SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */
-#define SCTP_NXTINFO SCTP_NXTINFO
-} sctp_cmsg_t;
-
-/*
- * 5.3.1.1 SCTP_ASSOC_CHANGE
- *
- * Communication notifications inform the ULP that an SCTP association
- * has either begun or ended. The identifier for a new association is
- * provided by this notificaion. The notification information has the
- * following format:
- *
- */
-struct sctp_assoc_change {
- __u16 sac_type;
- __u16 sac_flags;
- __u32 sac_length;
- __u16 sac_state;
- __u16 sac_error;
- __u16 sac_outbound_streams;
- __u16 sac_inbound_streams;
- sctp_assoc_t sac_assoc_id;
- __u8 sac_info[0];
-};
-
-/*
- * sac_state: 32 bits (signed integer)
- *
- * This field holds one of a number of values that communicate the
- * event that happened to the association. They include:
- *
- * Note: The following state names deviate from the API draft as
- * the names clash too easily with other kernel symbols.
- */
-enum sctp_sac_state {
- SCTP_COMM_UP,
- SCTP_COMM_LOST,
- SCTP_RESTART,
- SCTP_SHUTDOWN_COMP,
- SCTP_CANT_STR_ASSOC,
-};
-
-/*
- * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
- *
- * When a destination address on a multi-homed peer encounters a change
- * an interface details event is sent. The information has the
- * following structure:
- */
-struct sctp_paddr_change {
- __u16 spc_type;
- __u16 spc_flags;
- __u32 spc_length;
- struct sockaddr_storage spc_aaddr;
- int spc_state;
- int spc_error;
- sctp_assoc_t spc_assoc_id;
-} __attribute__((packed, aligned(4)));
-
-/*
- * spc_state: 32 bits (signed integer)
- *
- * This field holds one of a number of values that communicate the
- * event that happened to the address. They include:
- */
-enum sctp_spc_state {
- SCTP_ADDR_AVAILABLE,
- SCTP_ADDR_UNREACHABLE,
- SCTP_ADDR_REMOVED,
- SCTP_ADDR_ADDED,
- SCTP_ADDR_MADE_PRIM,
- SCTP_ADDR_CONFIRMED,
-};
-
-
-/*
- * 5.3.1.3 SCTP_REMOTE_ERROR
- *
- * A remote peer may send an Operational Error message to its peer.
- * This message indicates a variety of error conditions on an
- * association. The entire error TLV as it appears on the wire is
- * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP
- * specification [SCTP] and any extensions for a list of possible
- * error formats. SCTP error TLVs have the format:
- */
-struct sctp_remote_error {
- __u16 sre_type;
- __u16 sre_flags;
- __u32 sre_length;
- __be16 sre_error;
- sctp_assoc_t sre_assoc_id;
- __u8 sre_data[0];
-};
-
-
-/*
- * 5.3.1.4 SCTP_SEND_FAILED
- *
- * If SCTP cannot deliver a message it may return the message as a
- * notification.
- */
-struct sctp_send_failed {
- __u16 ssf_type;
- __u16 ssf_flags;
- __u32 ssf_length;
- __u32 ssf_error;
- struct sctp_sndrcvinfo ssf_info;
- sctp_assoc_t ssf_assoc_id;
- __u8 ssf_data[0];
-};
-
-/*
- * ssf_flags: 16 bits (unsigned integer)
- *
- * The flag value will take one of the following values
- *
- * SCTP_DATA_UNSENT - Indicates that the data was never put on
- * the wire.
- *
- * SCTP_DATA_SENT - Indicates that the data was put on the wire.
- * Note that this does not necessarily mean that the
- * data was (or was not) successfully delivered.
- */
-enum sctp_ssf_flags {
- SCTP_DATA_UNSENT,
- SCTP_DATA_SENT,
-};
-
-/*
- * 5.3.1.5 SCTP_SHUTDOWN_EVENT
- *
- * When a peer sends a SHUTDOWN, SCTP delivers this notification to
- * inform the application that it should cease sending data.
- */
-struct sctp_shutdown_event {
- __u16 sse_type;
- __u16 sse_flags;
- __u32 sse_length;
- sctp_assoc_t sse_assoc_id;
-};
-
-/*
- * 5.3.1.6 SCTP_ADAPTATION_INDICATION
- *
- * When a peer sends a Adaptation Layer Indication parameter , SCTP
- * delivers this notification to inform the application
- * that of the peers requested adaptation layer.
- */
-struct sctp_adaptation_event {
- __u16 sai_type;
- __u16 sai_flags;
- __u32 sai_length;
- __u32 sai_adaptation_ind;
- sctp_assoc_t sai_assoc_id;
-};
-
-/*
- * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
- *
- * When a receiver is engaged in a partial delivery of a
- * message this notification will be used to indicate
- * various events.
- */
-struct sctp_pdapi_event {
- __u16 pdapi_type;
- __u16 pdapi_flags;
- __u32 pdapi_length;
- __u32 pdapi_indication;
- sctp_assoc_t pdapi_assoc_id;
-};
-
-enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
-
-/*
- * 5.3.1.8. SCTP_AUTHENTICATION_EVENT
- *
- * When a receiver is using authentication this message will provide
- * notifications regarding new keys being made active as well as errors.
- */
-struct sctp_authkey_event {
- __u16 auth_type;
- __u16 auth_flags;
- __u32 auth_length;
- __u16 auth_keynumber;
- __u16 auth_altkeynumber;
- __u32 auth_indication;
- sctp_assoc_t auth_assoc_id;
-};
-
-enum { SCTP_AUTH_NEWKEY = 0, };
-
-/*
- * 6.1.9. SCTP_SENDER_DRY_EVENT
- *
- * When the SCTP stack has no more user data to send or retransmit, this
- * notification is given to the user. Also, at the time when a user app
- * subscribes to this event, if there is no data to be sent or
- * retransmit, the stack will immediately send up this notification.
- */
-struct sctp_sender_dry_event {
- __u16 sender_dry_type;
- __u16 sender_dry_flags;
- __u32 sender_dry_length;
- sctp_assoc_t sender_dry_assoc_id;
-};
-
-#define SCTP_STREAM_RESET_INCOMING_SSN 0x0001
-#define SCTP_STREAM_RESET_OUTGOING_SSN 0x0002
-#define SCTP_STREAM_RESET_DENIED 0x0004
-#define SCTP_STREAM_RESET_FAILED 0x0008
-struct sctp_stream_reset_event {
- __u16 strreset_type;
- __u16 strreset_flags;
- __u32 strreset_length;
- sctp_assoc_t strreset_assoc_id;
- __u16 strreset_stream_list[];
-};
-
-#define SCTP_ASSOC_RESET_DENIED 0x0004
-#define SCTP_ASSOC_RESET_FAILED 0x0008
-struct sctp_assoc_reset_event {
- __u16 assocreset_type;
- __u16 assocreset_flags;
- __u32 assocreset_length;
- sctp_assoc_t assocreset_assoc_id;
- __u32 assocreset_local_tsn;
- __u32 assocreset_remote_tsn;
-};
-
-#define SCTP_ASSOC_CHANGE_DENIED 0x0004
-#define SCTP_ASSOC_CHANGE_FAILED 0x0008
-#define SCTP_STREAM_CHANGE_DENIED SCTP_ASSOC_CHANGE_DENIED
-#define SCTP_STREAM_CHANGE_FAILED SCTP_ASSOC_CHANGE_FAILED
-struct sctp_stream_change_event {
- __u16 strchange_type;
- __u16 strchange_flags;
- __u32 strchange_length;
- sctp_assoc_t strchange_assoc_id;
- __u16 strchange_instrms;
- __u16 strchange_outstrms;
-};
-
-/*
- * Described in Section 7.3
- * Ancillary Data and Notification Interest Options
- */
-struct sctp_event_subscribe {
- __u8 sctp_data_io_event;
- __u8 sctp_association_event;
- __u8 sctp_address_event;
- __u8 sctp_send_failure_event;
- __u8 sctp_peer_error_event;
- __u8 sctp_shutdown_event;
- __u8 sctp_partial_delivery_event;
- __u8 sctp_adaptation_layer_event;
- __u8 sctp_authentication_event;
- __u8 sctp_sender_dry_event;
- __u8 sctp_stream_reset_event;
- __u8 sctp_assoc_reset_event;
- __u8 sctp_stream_change_event;
-};
-
-/*
- * 5.3.1 SCTP Notification Structure
- *
- * The notification structure is defined as the union of all
- * notification types.
- *
- */
-union sctp_notification {
- struct {
- __u16 sn_type; /* Notification type. */
- __u16 sn_flags;
- __u32 sn_length;
- } sn_header;
- struct sctp_assoc_change sn_assoc_change;
- struct sctp_paddr_change sn_paddr_change;
- struct sctp_remote_error sn_remote_error;
- struct sctp_send_failed sn_send_failed;
- struct sctp_shutdown_event sn_shutdown_event;
- struct sctp_adaptation_event sn_adaptation_event;
- struct sctp_pdapi_event sn_pdapi_event;
- struct sctp_authkey_event sn_authkey_event;
- struct sctp_sender_dry_event sn_sender_dry_event;
- struct sctp_stream_reset_event sn_strreset_event;
- struct sctp_assoc_reset_event sn_assocreset_event;
- struct sctp_stream_change_event sn_strchange_event;
-};
-
-/* Section 5.3.1
- * All standard values for sn_type flags are greater than 2^15.
- * Values from 2^15 and down are reserved.
- */
-
-enum sctp_sn_type {
- SCTP_SN_TYPE_BASE = (1<<15),
- SCTP_ASSOC_CHANGE,
-#define SCTP_ASSOC_CHANGE SCTP_ASSOC_CHANGE
- SCTP_PEER_ADDR_CHANGE,
-#define SCTP_PEER_ADDR_CHANGE SCTP_PEER_ADDR_CHANGE
- SCTP_SEND_FAILED,
-#define SCTP_SEND_FAILED SCTP_SEND_FAILED
- SCTP_REMOTE_ERROR,
-#define SCTP_REMOTE_ERROR SCTP_REMOTE_ERROR
- SCTP_SHUTDOWN_EVENT,
-#define SCTP_SHUTDOWN_EVENT SCTP_SHUTDOWN_EVENT
- SCTP_PARTIAL_DELIVERY_EVENT,
-#define SCTP_PARTIAL_DELIVERY_EVENT SCTP_PARTIAL_DELIVERY_EVENT
- SCTP_ADAPTATION_INDICATION,
-#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTATION_INDICATION
- SCTP_AUTHENTICATION_EVENT,
-#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT
- SCTP_SENDER_DRY_EVENT,
-#define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT
- SCTP_STREAM_RESET_EVENT,
-#define SCTP_STREAM_RESET_EVENT SCTP_STREAM_RESET_EVENT
- SCTP_ASSOC_RESET_EVENT,
-#define SCTP_ASSOC_RESET_EVENT SCTP_ASSOC_RESET_EVENT
- SCTP_STREAM_CHANGE_EVENT,
-#define SCTP_STREAM_CHANGE_EVENT SCTP_STREAM_CHANGE_EVENT
-};
-
-/* Notification error codes used to fill up the error fields in some
- * notifications.
- * SCTP_PEER_ADDRESS_CHAGE : spc_error
- * SCTP_ASSOC_CHANGE : sac_error
- * These names should be potentially included in the draft 04 of the SCTP
- * sockets API specification.
- */
-typedef enum sctp_sn_error {
- SCTP_FAILED_THRESHOLD,
- SCTP_RECEIVED_SACK,
- SCTP_HEARTBEAT_SUCCESS,
- SCTP_RESPONSE_TO_USER_REQ,
- SCTP_INTERNAL_ERROR,
- SCTP_SHUTDOWN_GUARD_EXPIRES,
- SCTP_PEER_FAULTY,
-} sctp_sn_error_t;
-
-/*
- * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
- *
- * The protocol parameters used to initialize and bound retransmission
- * timeout (RTO) are tunable. See [SCTP] for more information on how
- * these parameters are used in RTO calculation.
- */
-struct sctp_rtoinfo {
- sctp_assoc_t srto_assoc_id;
- __u32 srto_initial;
- __u32 srto_max;
- __u32 srto_min;
-};
-
-/*
- * 7.1.2 Association Parameters (SCTP_ASSOCINFO)
- *
- * This option is used to both examine and set various association and
- * endpoint parameters.
- */
-struct sctp_assocparams {
- sctp_assoc_t sasoc_assoc_id;
- __u16 sasoc_asocmaxrxt;
- __u16 sasoc_number_peer_destinations;
- __u32 sasoc_peer_rwnd;
- __u32 sasoc_local_rwnd;
- __u32 sasoc_cookie_life;
-};
-
-/*
- * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
- *
- * Requests that the peer mark the enclosed address as the association
- * primary. The enclosed address must be one of the association's
- * locally bound addresses. The following structure is used to make a
- * set primary request:
- */
-struct sctp_setpeerprim {
- sctp_assoc_t sspp_assoc_id;
- struct sockaddr_storage sspp_addr;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
- *
- * Requests that the local SCTP stack use the enclosed peer address as
- * the association primary. The enclosed address must be one of the
- * association peer's addresses. The following structure is used to
- * make a set peer primary request:
- */
-struct sctp_prim {
- sctp_assoc_t ssp_assoc_id;
- struct sockaddr_storage ssp_addr;
-} __attribute__((packed, aligned(4)));
-
-/* For backward compatibility use, define the old name too */
-#define sctp_setprim sctp_prim
-
-/*
- * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
- *
- * Requests that the local endpoint set the specified Adaptation Layer
- * Indication parameter for all future INIT and INIT-ACK exchanges.
- */
-struct sctp_setadaptation {
- __u32 ssb_adaptation_ind;
-};
-
-/*
- * 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
- *
- * Applications can enable or disable heartbeats for any peer address
- * of an association, modify an address's heartbeat interval, force a
- * heartbeat to be sent immediately, and adjust the address's maximum
- * number of retransmissions sent before an address is considered
- * unreachable. The following structure is used to access and modify an
- * address's parameters:
- */
-enum sctp_spp_flags {
- SPP_HB_ENABLE = 1<<0, /*Enable heartbeats*/
- SPP_HB_DISABLE = 1<<1, /*Disable heartbeats*/
- SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
- SPP_HB_DEMAND = 1<<2, /*Send heartbeat immediately*/
- SPP_PMTUD_ENABLE = 1<<3, /*Enable PMTU discovery*/
- SPP_PMTUD_DISABLE = 1<<4, /*Disable PMTU discovery*/
- SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
- SPP_SACKDELAY_ENABLE = 1<<5, /*Enable SACK*/
- SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/
- SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
- SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */
-};
-
-struct sctp_paddrparams {
- sctp_assoc_t spp_assoc_id;
- struct sockaddr_storage spp_address;
- __u32 spp_hbinterval;
- __u16 spp_pathmaxrxt;
- __u32 spp_pathmtu;
- __u32 spp_sackdelay;
- __u32 spp_flags;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
- *
- * This set option adds a chunk type that the user is requesting to be
- * received only in an authenticated way. Changes to the list of chunks
- * will only effect future associations on the socket.
- */
-struct sctp_authchunk {
- __u8 sauth_chunk;
-};
-
-/*
- * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
- *
- * This option gets or sets the list of HMAC algorithms that the local
- * endpoint requires the peer to use.
- */
-/* This here is only used by user space as is. It might not be a good idea
- * to export/reveal the whole structure with reserved fields etc.
- */
-enum {
- SCTP_AUTH_HMAC_ID_SHA1 = 1,
- SCTP_AUTH_HMAC_ID_SHA256 = 3,
-};
-
-struct sctp_hmacalgo {
- __u32 shmac_num_idents;
- __u16 shmac_idents[];
-};
-
-/* Sadly, user and kernel space have different names for
- * this structure member, so this is to not break anything.
- */
-#define shmac_number_of_idents shmac_num_idents
-
-/*
- * 7.1.20. Set a shared key (SCTP_AUTH_KEY)
- *
- * This option will set a shared secret key which is used to build an
- * association shared key.
- */
-struct sctp_authkey {
- sctp_assoc_t sca_assoc_id;
- __u16 sca_keynumber;
- __u16 sca_keylength;
- __u8 sca_key[];
-};
-
-/*
- * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
- *
- * This option will get or set the active shared key to be used to build
- * the association shared key.
- */
-
-struct sctp_authkeyid {
- sctp_assoc_t scact_assoc_id;
- __u16 scact_keynumber;
-};
-
-
-/*
- * 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK)
- *
- * This option will effect the way delayed acks are performed. This
- * option allows you to get or set the delayed ack time, in
- * milliseconds. It also allows changing the delayed ack frequency.
- * Changing the frequency to 1 disables the delayed sack algorithm. If
- * the assoc_id is 0, then this sets or gets the endpoints default
- * values. If the assoc_id field is non-zero, then the set or get
- * effects the specified association for the one to many model (the
- * assoc_id field is ignored by the one to one model). Note that if
- * sack_delay or sack_freq are 0 when setting this option, then the
- * current values will remain unchanged.
- */
-struct sctp_sack_info {
- sctp_assoc_t sack_assoc_id;
- uint32_t sack_delay;
- uint32_t sack_freq;
-};
-
-struct sctp_assoc_value {
- sctp_assoc_t assoc_id;
- uint32_t assoc_value;
-};
-
-struct sctp_stream_value {
- sctp_assoc_t assoc_id;
- uint16_t stream_id;
- uint16_t stream_value;
-};
-
-/*
- * 7.2.2 Peer Address Information
- *
- * Applications can retrieve information about a specific peer address
- * of an association, including its reachability state, congestion
- * window, and retransmission timer values. This information is
- * read-only. The following structure is used to access this
- * information:
- */
-struct sctp_paddrinfo {
- sctp_assoc_t spinfo_assoc_id;
- struct sockaddr_storage spinfo_address;
- __s32 spinfo_state;
- __u32 spinfo_cwnd;
- __u32 spinfo_srtt;
- __u32 spinfo_rto;
- __u32 spinfo_mtu;
-} __attribute__((packed, aligned(4)));
-
-/* Peer addresses's state. */
-/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x]
- * calls.
- * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters.
- * Not yet confirmed by a heartbeat and not available for data
- * transfers.
- * ACTIVE : Peer address confirmed, active and available for data transfers.
- * INACTIVE: Peer address inactive and not available for data transfers.
- */
-enum sctp_spinfo_state {
- SCTP_INACTIVE,
- SCTP_PF,
- SCTP_ACTIVE,
- SCTP_UNCONFIRMED,
- SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */
-};
-
-/*
- * 7.2.1 Association Status (SCTP_STATUS)
- *
- * Applications can retrieve current status information about an
- * association, including association state, peer receiver window size,
- * number of unacked data chunks, and number of data chunks pending
- * receipt. This information is read-only. The following structure is
- * used to access this information:
- */
-struct sctp_status {
- sctp_assoc_t sstat_assoc_id;
- __s32 sstat_state;
- __u32 sstat_rwnd;
- __u16 sstat_unackdata;
- __u16 sstat_penddata;
- __u16 sstat_instrms;
- __u16 sstat_outstrms;
- __u32 sstat_fragmentation_point;
- struct sctp_paddrinfo sstat_primary;
-};
-
-/*
- * 7.2.3. Get the list of chunks the peer requires to be authenticated
- * (SCTP_PEER_AUTH_CHUNKS)
- *
- * This option gets a list of chunks for a specified association that
- * the peer requires to be received authenticated only.
- */
-struct sctp_authchunks {
- sctp_assoc_t gauth_assoc_id;
- __u32 gauth_number_of_chunks;
- uint8_t gauth_chunks[];
-};
-
-/* The broken spelling has been released already in lksctp-tools header,
- * so don't break anyone, now that it's fixed.
- */
-#define guth_number_of_chunks gauth_number_of_chunks
-
-/* Association states. */
-enum sctp_sstat_state {
- SCTP_EMPTY = 0,
- SCTP_CLOSED = 1,
- SCTP_COOKIE_WAIT = 2,
- SCTP_COOKIE_ECHOED = 3,
- SCTP_ESTABLISHED = 4,
- SCTP_SHUTDOWN_PENDING = 5,
- SCTP_SHUTDOWN_SENT = 6,
- SCTP_SHUTDOWN_RECEIVED = 7,
- SCTP_SHUTDOWN_ACK_SENT = 8,
-};
-
-/*
- * 8.2.6. Get the Current Identifiers of Associations
- * (SCTP_GET_ASSOC_ID_LIST)
- *
- * This option gets the current list of SCTP association identifiers of
- * the SCTP associations handled by a one-to-many style socket.
- */
-struct sctp_assoc_ids {
- __u32 gaids_number_of_ids;
- sctp_assoc_t gaids_assoc_id[];
-};
-
-/*
- * 8.3, 8.5 get all peer/local addresses in an association.
- * This parameter struct is used by SCTP_GET_PEER_ADDRS and
- * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
- * sctp_getpaddrs() and sctp_getladdrs() API.
- */
-struct sctp_getaddrs_old {
- sctp_assoc_t assoc_id;
- int addr_num;
- struct sockaddr *addrs;
-};
-
-struct sctp_getaddrs {
- sctp_assoc_t assoc_id; /*input*/
- __u32 addr_num; /*output*/
- __u8 addrs[0]; /*output, variable size*/
-};
-
-/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves
- * association stats. All stats are counts except sas_maxrto and
- * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since
- * the last call. Will return 0 when RTO was not update since last call
- */
-struct sctp_assoc_stats {
- sctp_assoc_t sas_assoc_id; /* Input */
- /* Transport of observed max RTO */
- struct sockaddr_storage sas_obs_rto_ipaddr;
- __u64 sas_maxrto; /* Maximum Observed RTO for period */
- __u64 sas_isacks; /* SACKs received */
- __u64 sas_osacks; /* SACKs sent */
- __u64 sas_opackets; /* Packets sent */
- __u64 sas_ipackets; /* Packets received */
- __u64 sas_rtxchunks; /* Retransmitted Chunks */
- __u64 sas_outofseqtsns;/* TSN received > next expected */
- __u64 sas_idupchunks; /* Dups received (ordered+unordered) */
- __u64 sas_gapcnt; /* Gap Acknowledgements Received */
- __u64 sas_ouodchunks; /* Unordered data chunks sent */
- __u64 sas_iuodchunks; /* Unordered data chunks received */
- __u64 sas_oodchunks; /* Ordered data chunks sent */
- __u64 sas_iodchunks; /* Ordered data chunks received */
- __u64 sas_octrlchunks; /* Control chunks sent */
- __u64 sas_ictrlchunks; /* Control chunks received */
-};
-
-/*
- * 8.1 sctp_bindx()
- *
- * The flags parameter is formed from the bitwise OR of zero or more of the
- * following currently defined flags:
- */
-#define SCTP_BINDX_ADD_ADDR 0x01
-#define SCTP_BINDX_REM_ADDR 0x02
-
-/* This is the structure that is passed as an argument(optval) to
- * getsockopt(SCTP_SOCKOPT_PEELOFF).
- */
-typedef struct {
- sctp_assoc_t associd;
- int sd;
-} sctp_peeloff_arg_t;
-
-typedef struct {
- sctp_peeloff_arg_t p_arg;
- unsigned flags;
-} sctp_peeloff_flags_arg_t;
-
-/*
- * Peer Address Thresholds socket option
- */
-struct sctp_paddrthlds {
- sctp_assoc_t spt_assoc_id;
- struct sockaddr_storage spt_address;
- __u16 spt_pathmaxrxt;
- __u16 spt_pathpfthld;
-};
-
-/*
- * Socket Option for Getting the Association/Stream-Specific PR-SCTP Status
- */
-struct sctp_prstatus {
- sctp_assoc_t sprstat_assoc_id;
- __u16 sprstat_sid;
- __u16 sprstat_policy;
- __u64 sprstat_abandoned_unsent;
- __u64 sprstat_abandoned_sent;
-};
-
-struct sctp_default_prinfo {
- sctp_assoc_t pr_assoc_id;
- __u32 pr_value;
- __u16 pr_policy;
-};
-
-struct sctp_info {
- __u32 sctpi_tag;
- __u32 sctpi_state;
- __u32 sctpi_rwnd;
- __u16 sctpi_unackdata;
- __u16 sctpi_penddata;
- __u16 sctpi_instrms;
- __u16 sctpi_outstrms;
- __u32 sctpi_fragmentation_point;
- __u32 sctpi_inqueue;
- __u32 sctpi_outqueue;
- __u32 sctpi_overall_error;
- __u32 sctpi_max_burst;
- __u32 sctpi_maxseg;
- __u32 sctpi_peer_rwnd;
- __u32 sctpi_peer_tag;
- __u8 sctpi_peer_capable;
- __u8 sctpi_peer_sack;
- __u16 __reserved1;
-
- /* assoc status info */
- __u64 sctpi_isacks;
- __u64 sctpi_osacks;
- __u64 sctpi_opackets;
- __u64 sctpi_ipackets;
- __u64 sctpi_rtxchunks;
- __u64 sctpi_outofseqtsns;
- __u64 sctpi_idupchunks;
- __u64 sctpi_gapcnt;
- __u64 sctpi_ouodchunks;
- __u64 sctpi_iuodchunks;
- __u64 sctpi_oodchunks;
- __u64 sctpi_iodchunks;
- __u64 sctpi_octrlchunks;
- __u64 sctpi_ictrlchunks;
-
- /* primary transport info */
- struct sockaddr_storage sctpi_p_address;
- __s32 sctpi_p_state;
- __u32 sctpi_p_cwnd;
- __u32 sctpi_p_srtt;
- __u32 sctpi_p_rto;
- __u32 sctpi_p_hbinterval;
- __u32 sctpi_p_pathmaxrxt;
- __u32 sctpi_p_sackdelay;
- __u32 sctpi_p_sackfreq;
- __u32 sctpi_p_ssthresh;
- __u32 sctpi_p_partial_bytes_acked;
- __u32 sctpi_p_flight_size;
- __u16 sctpi_p_error;
- __u16 __reserved2;
-
- /* sctp sock info */
- __u32 sctpi_s_autoclose;
- __u32 sctpi_s_adaptation_ind;
- __u32 sctpi_s_pd_point;
- __u8 sctpi_s_nodelay;
- __u8 sctpi_s_disable_fragments;
- __u8 sctpi_s_v4mapped;
- __u8 sctpi_s_frag_interleave;
- __u32 sctpi_s_type;
- __u32 __reserved3;
-};
-
-struct sctp_reset_streams {
- sctp_assoc_t srs_assoc_id;
- uint16_t srs_flags;
- uint16_t srs_number_streams; /* 0 == ALL */
- uint16_t srs_stream_list[]; /* list if srs_num_streams is not 0 */
-};
-
-struct sctp_add_streams {
- sctp_assoc_t sas_assoc_id;
- uint16_t sas_instrms;
- uint16_t sas_outstrms;
-};
-
-/* SCTP Stream schedulers */
-enum sctp_sched_type {
- SCTP_SS_FCFS,
- SCTP_SS_DEFAULT = SCTP_SS_FCFS,
- SCTP_SS_PRIO,
- SCTP_SS_RR,
- SCTP_SS_MAX = SCTP_SS_RR
-};
-
-#endif /* _SCTP_H */
diff --git a/tools/cpp/CROSSTOOL b/tools/cpp/CROSSTOOL
index 20f47e3..c15eddf 100644
--- a/tools/cpp/CROSSTOOL
+++ b/tools/cpp/CROSSTOOL
@@ -914,7 +914,7 @@
needsPic: true
compiler_flag: "-target"
compiler_flag: "armv7a-arm-linux-gnueabif"
- compiler_flag: "--sysroot=external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc"
+ compiler_flag: "--sysroot=external/armhf_debian_rootfs"
compiler_flag: "-mfloat-abi=hard"
compiler_flag: "-mfpu=vfpv3-d16"
compiler_flag: "-nostdinc"
@@ -931,7 +931,9 @@
compiler_flag: "-isystem"
compiler_flag: "external/linaro_linux_gcc_repo/include/c++/7.4.1"
compiler_flag: "-isystem"
- compiler_flag: "external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/usr/include"
+ compiler_flag: "external/armhf_debian_rootfs/usr/include"
+ compiler_flag: "-isystem"
+ compiler_flag: "external/armhf_debian_rootfs/usr/include/arm-linux-gnueabihf"
compiler_flag: "-isystem"
compiler_flag: "external/org_frc971/third_party"
compiler_flag: "-D__STDC_FORMAT_MACROS"
@@ -960,12 +962,14 @@
compiler_flag: "-ggdb3"
linker_flag: "-target"
linker_flag: "armv7a-arm-linux-gnueabif"
- linker_flag: "--sysroot=external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc"
+ linker_flag: "--sysroot=external/armhf_debian_rootfs"
linker_flag: "-lstdc++"
linker_flag: "-Ltools/cpp/linaro_linux_gcc/clang_more_libs"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/lib"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/lib"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/usr/lib"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib/gcc/arm-linux-gnueabihf/8"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/lib/arm-linux-gnueabihf"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib/arm-linux-gnueabihf"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/lib"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib"
linker_flag: "-Lexternal/linaro_linux_gcc_repo/lib/gcc/arm-linux-gnueabihf/7.4.1"
linker_flag: "-Bexternal/linaro_linux_gcc_repo/lib/gcc/arm-linux-gnueabihf/7.4.1"
linker_flag: "-Bexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/bin"
@@ -990,12 +994,8 @@
mode: DYNAMIC
}
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/usr/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/usr/lib/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
+ cxx_builtin_include_directory: "%package(@armhf_debian_rootfs//usr/include)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//include)%/c++/7.4.1"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//lib/gcc/arm-linux-gnueabihf/7.4.1/include)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/include)%/c++/7.4.1"
diff --git a/tools/cpp/linaro_linux_gcc/BUILD b/tools/cpp/linaro_linux_gcc/BUILD
index b5ea5a5..7b7f3e5 100644
--- a/tools/cpp/linaro_linux_gcc/BUILD
+++ b/tools/cpp/linaro_linux_gcc/BUILD
@@ -1,13 +1,5 @@
package(default_visibility = ["//tools/cpp:__pkg__"])
-cc_library(
- name = "libpthread",
- srcs = [
- "clang_more_libs/libpthread.so",
- ],
- visibility = ["//visibility:public"],
-)
-
filegroup(
name = "gcc",
srcs = [
diff --git a/tools/cpp/linaro_linux_gcc/clang_more_libs/libpthread.so b/tools/cpp/linaro_linux_gcc/clang_more_libs/libpthread.so
deleted file mode 100644
index 71f034f..0000000
--- a/tools/cpp/linaro_linux_gcc/clang_more_libs/libpthread.so
+++ /dev/null
@@ -1,5 +0,0 @@
-/* GNU ld script
- Use the shared library, but some functions are only in
- the static library, so try that secondarily. */
-OUTPUT_FORMAT(elf32-littlearm)
-GROUP ( libpthread.so.0 libpthread_nonshared.a )
diff --git a/tools/cpp/static_crosstool.pb b/tools/cpp/static_crosstool.pb
index 8552dff..7261677 100644
--- a/tools/cpp/static_crosstool.pb
+++ b/tools/cpp/static_crosstool.pb
@@ -939,7 +939,7 @@
compiler_flag: "-target"
compiler_flag: "armv7a-arm-linux-gnueabif"
- compiler_flag: "--sysroot=external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc"
+ compiler_flag: "--sysroot=external/armhf_debian_rootfs"
compiler_flag: "-mfloat-abi=hard"
compiler_flag: "-mfpu=vfpv3-d16"
@@ -957,27 +957,29 @@
compiler_flag: "-isystem"
compiler_flag: "external/linaro_linux_gcc_repo/include/c++/7.4.1"
compiler_flag: "-isystem"
- compiler_flag: "external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/usr/include"
+ compiler_flag: "external/armhf_debian_rootfs/usr/include"
+ compiler_flag: "-isystem"
+ compiler_flag: "external/armhf_debian_rootfs/usr/include/arm-linux-gnueabihf"
+ compiler_flag: "-isystem"
+ compiler_flag: "external/org_frc971/third_party"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/usr/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/usr/lib/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
+ cxx_builtin_include_directory: "%package(@armhf_debian_rootfs//usr/include)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//include)%/c++/7.4.1"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include)%"
- cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/libc/lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//lib/gcc/arm-linux-gnueabihf/7.4.1/include)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//lib/gcc/arm-linux-gnueabihf/7.4.1/include-fixed)%"
cxx_builtin_include_directory: "%package(@linaro_linux_gcc_repo//arm-linux-gnueabihf/include)%/c++/7.4.1"
linker_flag: "-target"
linker_flag: "armv7a-arm-linux-gnueabif"
- linker_flag: "--sysroot=external/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc"
+ linker_flag: "--sysroot=external/armhf_debian_rootfs"
linker_flag: "-lstdc++"
linker_flag: "-Ltools/cpp/linaro_linux_gcc/clang_more_libs"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/lib"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/lib"
- linker_flag: "-Lexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/libc/usr/lib"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib/gcc/arm-linux-gnueabihf/8"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/lib/arm-linux-gnueabihf"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib/arm-linux-gnueabihf"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/lib"
+ linker_flag: "-Lexternal/armhf_debian_rootfs/usr/lib"
linker_flag: "-Lexternal/linaro_linux_gcc_repo/lib/gcc/arm-linux-gnueabihf/7.4.1"
linker_flag: "-Bexternal/linaro_linux_gcc_repo/lib/gcc/arm-linux-gnueabihf/7.4.1"
linker_flag: "-Bexternal/linaro_linux_gcc_repo/arm-linux-gnueabihf/bin"
diff --git a/y2018/control_loops/superstructure/intake/BUILD b/y2018/control_loops/superstructure/intake/BUILD
index ea44c7a..dde7c63 100644
--- a/y2018/control_loops/superstructure/intake/BUILD
+++ b/y2018/control_loops/superstructure/intake/BUILD
@@ -39,7 +39,6 @@
visibility = ["//visibility:public"],
deps = [
":intake_plants",
- ":sensor_unwrap",
"//aos:math",
"//aos/controls:control_loop",
"//frc971/control_loops:control_loops_fbs",
@@ -50,25 +49,3 @@
"//y2018/control_loops/superstructure:superstructure_status_fbs",
],
)
-
-cc_library(
- name = "sensor_unwrap",
- srcs = [
- "sensor_unwrap.cc",
- ],
- hdrs = [
- "sensor_unwrap.h",
- ],
- visibility = ["//visibility:public"],
-)
-
-cc_test(
- name = "unwrap_test",
- srcs = [
- "unwrap_test.cc",
- ],
- deps = [
- ":sensor_unwrap",
- "//aos/testing:googletest",
- ],
-)
diff --git a/y2018/control_loops/superstructure/intake/intake.h b/y2018/control_loops/superstructure/intake/intake.h
index ac4f971..e064c0e 100644
--- a/y2018/control_loops/superstructure/intake/intake.h
+++ b/y2018/control_loops/superstructure/intake/intake.h
@@ -5,11 +5,11 @@
#include "aos/commonmath.h"
#include "aos/controls/control_loop.h"
+#include "frc971/zeroing/wrap.h"
#include "frc971/zeroing/zeroing.h"
#include "y2018/constants.h"
#include "y2018/control_loops/superstructure/intake/intake_delayed_plant.h"
#include "y2018/control_loops/superstructure/intake/intake_plant.h"
-#include "y2018/control_loops/superstructure/intake/sensor_unwrap.h"
#include "y2018/control_loops/superstructure/superstructure_output_generated.h"
#include "y2018/control_loops/superstructure/superstructure_position_generated.h"
#include "y2018/control_loops/superstructure/superstructure_status_generated.h"
@@ -121,7 +121,8 @@
return ::y2018::constants::Values::kIntakeSpringRatio() * (2 * M_PI);
}
- UnwrapSensor spring_unwrap_{spring_offset_, spring_range()};
+ ::frc971::zeroing::UnwrapSensor spring_unwrap_{spring_offset_,
+ spring_range()};
State state_ = State::UNINITIALIZED;
diff --git a/y2018/control_loops/superstructure/intake/sensor_unwrap.cc b/y2018/control_loops/superstructure/intake/sensor_unwrap.cc
deleted file mode 100644
index c7d2a03..0000000
--- a/y2018/control_loops/superstructure/intake/sensor_unwrap.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-#include "y2018/control_loops/superstructure/intake/sensor_unwrap.h"
-
-#include <cmath>
-
-namespace y2018 {
-namespace control_loops {
-namespace superstructure {
-namespace intake {
-
-UnwrapSensor::UnwrapSensor(double sensor_offset, double sensor_range)
- : sensor_offset_(sensor_offset), sensor_range_(sensor_range) {
- Reset();
-}
-
-double UnwrapSensor::Unwrap(double current_sensor_value) {
- // First time the function is called it will use that value to initialize the
- // wrap calculation. This catches cases where the offset and first value
- // difference triggers an unwanted wrap at the first calculation.
- if (uninitialized_ == true) {
- uninitialized_ = false;
- } else {
- // Calculates the lower sensor value and set the max sensor range
- // If offset is not 0, this will correct the zeroing offset
- const double sensor_min_value = sensor_offset_;
- const double sensor_max_value = sensor_range_ + sensor_min_value;
-
- // Check if provided sensor value is within the range. This to prevent the
- // function to get out of sync. Will not throw an error, but continue and
- // return the value + wrapped factor and not process this value.
- if (current_sensor_value < sensor_min_value ||
- current_sensor_value > sensor_max_value) {
- return current_sensor_value + (sensor_range_ * wrap_count_);
- }
-
- // Calculate the positive or negative movement
- const double sensor_move = current_sensor_value - sensor_last_value_;
-
- // Function assumes that a movement of more then 1/2 of the range
- // indicates that we wrapped, instead of moved very fast.
- if (std::abs(sensor_move) > (sensor_range_ / 2)) {
- if (sensor_move >= 0) {
- // sensor moved past the sensor_min_value
- wrap_count_ -= 1;
- } else {
- // sensor moved past the sensor_max_value
- wrap_count_ += 1;
- }
- }
- }
- sensor_last_value_ = current_sensor_value;
- // return the unwrapped sensor value
- return current_sensor_value + (sensor_range_ * wrap_count_);
-}
-
-void UnwrapSensor::Reset() {
- wrap_count_ = 0;
- sensor_last_value_ = sensor_offset_;
- uninitialized_ = true;
-}
-
-} // namespace intake
-} // namespace superstructure
-} // namespace control_loops
-} // namespace y2018
diff --git a/y2018/control_loops/superstructure/intake/sensor_unwrap.h b/y2018/control_loops/superstructure/intake/sensor_unwrap.h
deleted file mode 100644
index ccb1b52..0000000
--- a/y2018/control_loops/superstructure/intake/sensor_unwrap.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_INTAKE_SENSOR_UNWRAP_H_
-#define Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_INTAKE_SENSOR_UNWRAP_H_
-
-namespace y2018 {
-namespace control_loops {
-namespace superstructure {
-namespace intake {
-
-// UnwrapSensor takes in a sensor value from a sensor that loops in a certain
-// interval. ex(the sensor moves from 0 to 10 and back to 0 while moving the
-// same direction) By checking for big gaps in sensor readings it assumes you
-// have wrapped either back or forwards and handles accordingly. It returns the
-// overall sensor value.
-
-class UnwrapSensor {
- public:
- // The sensor_offset (+ or -) present the sensor value that is 'zero'
- // The sensor_range presents the absolute value of the sensor range from 0 to
- // sensor_range. This will be adjusted using the sensor_offset
- UnwrapSensor(double sensor_offset, double sensor_range);
-
- // Takes a wrapped sensor value and unwraps it to give you its total position.
- double Unwrap(double current_sensor_value);
-
- void Reset();
-
- int sensor_wrapped() const { return wrap_count_; }
-
- private:
- const double sensor_offset_, sensor_range_;
-
- // The last value given from set_position, starts at offset
- double sensor_last_value_ = sensor_offset_;
-
- // Log if sensor is in wrapped state in either direction
- int wrap_count_ = 0;
-
- // function waits for first call with a value to set sensor_last_value_. Will
- // start to calculate the spring unwrap at the second function call.
- bool uninitialized_ = true;
-};
-
-} // namespace intake
-} // namespace superstructure
-} // namespace control_loops
-} // namespace y2018
-
-#endif // Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_INTAKE_SENSOR_UNWRAP_H_
diff --git a/y2019/BUILD b/y2019/BUILD
index d9cd4fd..328c93f 100644
--- a/y2019/BUILD
+++ b/y2019/BUILD
@@ -52,6 +52,8 @@
srcs = [
"wpilib_interface.cc",
],
+ # This library uses some deprecated parts of the SPI API.
+ copts = ["-Wno-deprecated-declarations"],
restricted_to = ["//tools:roborio"],
deps = [
":camera_log_fbs",