├── .clang_complete ├── okapi-docs.css ├── CODEOWNERS ├── firmware ├── squiggles.mk ├── libc.a ├── libm.a ├── libpros.a ├── v5.ld └── v5-hot.ld ├── logo.jpeg ├── kernel@4.0.3.zip ├── logo-doxygen.jpeg ├── run_cppcheck.sh ├── run_doxygen.sh ├── .gitignore ├── docs ├── api │ ├── index.md │ ├── utility.md │ ├── filters.md │ ├── chassis.md │ ├── devices.md │ └── control.md └── tutorials │ ├── concepts │ ├── logging.md │ ├── builders-and-tasks.md │ ├── controller-io.md │ ├── filtering.md │ └── settled-util.md │ ├── walkthrough │ ├── basicAutonomousMovement.md │ ├── gettingStarted.md │ ├── liftMovement.md │ └── asyncAutonomousMovement.md │ └── index.md ├── include ├── test │ ├── tests │ │ └── impl │ │ │ ├── utilTests.hpp │ │ │ ├── allImplTests.hpp │ │ │ ├── controllerTests.hpp │ │ │ ├── chassisControllerPidTests.hpp │ │ │ ├── asyncPosIntegratedControllerTests.hpp │ │ │ ├── chassisControllerIntegratedTests.hpp │ │ │ ├── asyncPosControllerBuilderIntegrationTests.hpp │ │ │ ├── asyncVelControllerBuilderIntegrationTests.hpp │ │ │ ├── chassisControllerBuilderIntegrationTests.hpp │ │ │ └── asyncMotionProfileControllerBuilderIntegrationTests.hpp │ └── testRunner.hpp ├── okapi │ ├── api │ │ ├── control │ │ │ ├── iterative │ │ │ │ ├── iterativePositionController.hpp │ │ │ │ ├── iterativeVelocityController.hpp │ │ │ │ └── iterativeController.hpp │ │ │ ├── async │ │ │ │ ├── asyncVelocityController.hpp │ │ │ │ ├── asyncController.hpp │ │ │ │ ├── asyncPositionController.hpp │ │ │ │ └── asyncVelPidController.hpp │ │ │ ├── controllerInput.hpp │ │ │ ├── controllerOutput.hpp │ │ │ ├── util │ │ │ │ ├── pathfinderUtil.hpp │ │ │ │ └── settledUtil.hpp │ │ │ └── offsettableControllerInput.hpp │ │ ├── odometry │ │ │ ├── stateMode.hpp │ │ │ ├── point.hpp │ │ │ ├── threeEncoderOdometry.hpp │ │ │ └── odometry.hpp │ │ ├── units │ │ │ ├── QAngularJerk.hpp │ │ │ ├── QAngularAcceleration.hpp │ │ │ ├── QJerk.hpp │ │ │ ├── QFrequency.hpp │ │ │ ├── QArea.hpp │ │ │ ├── QVolume.hpp │ │ │ ├── QAngle.hpp │ │ │ ├── QAcceleration.hpp │ │ │ ├── QAngularSpeed.hpp │ │ │ ├── QForce.hpp │ │ │ ├── QSpeed.hpp │ │ │ ├── QPressure.hpp │ │ │ ├── QTorque.hpp │ │ │ ├── RQuantityName.hpp │ │ │ ├── QTime.hpp │ │ │ └── QMass.hpp │ │ ├── device │ │ │ ├── rotarysensor │ │ │ │ ├── continuousRotarySensor.hpp │ │ │ │ └── rotarySensor.hpp │ │ │ └── button │ │ │ │ ├── abstractButton.hpp │ │ │ │ └── buttonBase.hpp │ │ ├── filter │ │ │ ├── filter.hpp │ │ │ ├── passthroughFilter.hpp │ │ │ ├── emaFilter.hpp │ │ │ ├── demaFilter.hpp │ │ │ ├── averageFilter.hpp │ │ │ ├── composableFilter.hpp │ │ │ ├── filteredControllerInput.hpp │ │ │ └── medianFilter.hpp │ │ ├── util │ │ │ ├── supplier.hpp │ │ │ ├── abstractRate.hpp │ │ │ └── timeUtil.hpp │ │ └── chassis │ │ │ └── model │ │ │ ├── readOnlyChassisModel.hpp │ │ │ ├── threeEncoderSkidSteerModel.hpp │ │ │ └── threeEncoderXDriveModel.hpp │ └── impl │ │ ├── util │ │ ├── timer.hpp │ │ ├── timeUtilFactory.hpp │ │ ├── configurableTimeUtilFactory.hpp │ │ └── rate.hpp │ │ ├── control │ │ └── util │ │ │ ├── controllerRunnerFactory.hpp │ │ │ └── pidTunerFactory.hpp │ │ └── device │ │ ├── button │ │ ├── controllerButton.hpp │ │ └── adiButton.hpp │ │ ├── controllerUtil.hpp │ │ ├── rotarysensor │ │ ├── integratedEncoder.hpp │ │ ├── potentiometer.hpp │ │ └── rotationSensor.hpp │ │ └── motor │ │ └── adiMotor.hpp ├── pros │ ├── error.h │ ├── device.h │ ├── llemu.h │ └── device.hpp ├── main.h └── api.h ├── run_clang-format.sh ├── .gitmodules ├── src ├── api │ ├── filter │ │ ├── filter.cpp │ │ ├── passthroughFilter.cpp │ │ ├── emaFilter.cpp │ │ ├── demaFilter.cpp │ │ ├── ekfFilter.cpp │ │ ├── composableFilter.cpp │ │ └── velMath.cpp │ ├── util │ │ ├── abstractRate.cpp │ │ ├── timeUtil.cpp │ │ ├── logging.cpp │ │ └── abstractTimer.cpp │ ├── device │ │ ├── rotarysensor │ │ │ └── rotarySensor.cpp │ │ ├── button │ │ │ ├── abstractButton.cpp │ │ │ └── buttonBase.cpp │ │ └── motor │ │ │ └── abstractMotor.cpp │ ├── control │ │ ├── offsettableControllerInput.cpp │ │ ├── util │ │ │ └── settledUtil.cpp │ │ └── async │ │ │ └── asyncVelPidController.cpp │ ├── odometry │ │ ├── odomState.cpp │ │ └── odomMath.cpp │ └── chassis │ │ └── model │ │ ├── threeEncoderSkidSteerModel.cpp │ │ └── threeEncoderXDriveModel.cpp ├── impl │ ├── util │ │ ├── timer.cpp │ │ ├── rate.cpp │ │ ├── configurableTimeUtilFactory.cpp │ │ └── timeUtilFactory.cpp │ ├── device │ │ ├── rotarysensor │ │ │ ├── potentiometer.cpp │ │ │ ├── integratedEncoder.cpp │ │ │ ├── adiGyro.cpp │ │ │ ├── rotationSensor.cpp │ │ │ └── adiEncoder.cpp │ │ ├── button │ │ │ ├── adiButton.cpp │ │ │ └── controllerButton.cpp │ │ ├── distanceSensor.cpp │ │ ├── adiUltrasonic.cpp │ │ ├── motor │ │ │ └── adiMotor.cpp │ │ ├── controllerUtil.cpp │ │ ├── opticalSensor.cpp │ │ └── controller.cpp │ └── filter │ │ └── velMathFactory.cpp ├── test │ └── allImplTests.cpp └── main.cpp ├── ISSUE_TEMPLATE.MD ├── .codecov.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── docs.yml │ └── test.yml ├── Doxyfile-mcss ├── test ├── controlTests.cpp ├── asyncVelPIDControllerTests.cpp ├── offsettableControllerInputTests.cpp ├── asyncPosPIDControllerTests.cpp ├── utilTests.cpp └── odomMathTests.cpp ├── .clang-format ├── CMakeLists.txt.in ├── PULL_REQUEST_TEMPLATE.md ├── closed-loop-controller-diagram.xml └── Makefile /.clang_complete: -------------------------------------------------------------------------------- 1 | -I. 2 | -I./include 3 | -------------------------------------------------------------------------------- /okapi-docs.css: -------------------------------------------------------------------------------- 1 | main p { 2 | text-indent: 0rem; 3 | } 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @OkapiLib/OkapiLib-dev 2 | .github/workflows @Octogonapus 3 | -------------------------------------------------------------------------------- /firmware/squiggles.mk: -------------------------------------------------------------------------------- 1 | INCLUDE+=-iquote"$(ROOT)/include/okapi/squiggles" 2 | -------------------------------------------------------------------------------- /logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/logo.jpeg -------------------------------------------------------------------------------- /firmware/libc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/firmware/libc.a -------------------------------------------------------------------------------- /firmware/libm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/firmware/libm.a -------------------------------------------------------------------------------- /kernel@4.0.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/kernel@4.0.3.zip -------------------------------------------------------------------------------- /logo-doxygen.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/logo-doxygen.jpeg -------------------------------------------------------------------------------- /firmware/libpros.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purduesigbots/OkapiLib/HEAD/firmware/libpros.a -------------------------------------------------------------------------------- /run_cppcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cppcheck --enable=all -I include/okapi include/test/tests src test 3 | -------------------------------------------------------------------------------- /run_doxygen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf html latex xml 3 | ./m.css/documentation/doxygen.py Doxyfile-mcss --debug 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.o 3 | *.obj 4 | 5 | # Executables 6 | *.bin 7 | *.elf 8 | 9 | # PROS 10 | bin/ 11 | .vscode/ 12 | .cache/ 13 | compile_commands.json 14 | temp.log 15 | temp.errors 16 | *.ini 17 | .d/ -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | - [Chassis API](docs/api/chassis.md) 4 | - [Control API](docs/api/control.md) 5 | - [Devices API](docs/api/devices.md) 6 | - [Filters API](docs/api/filters.md) 7 | - [Units API](docs/api/units.md) 8 | - [Utility API](docs/api/utility.md) 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/utilTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runUtilTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/allImplTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runAllImplTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/controllerTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runControllerTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/chassisControllerPidTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runChassisControllerPidTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/asyncPosIntegratedControllerTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runAsyncPosIntegratedControllerTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/chassisControllerIntegratedTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runChassisControllerIntegratedTests(); 9 | -------------------------------------------------------------------------------- /run_clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | find include/okapi/ -iname *.hpp | xargs clang-format -i -style=file 3 | find include/test/tests/ -iname *.hpp | xargs clang-format -i -style=file 4 | find src/ -iname *.cpp | xargs clang-format -i -style=file 5 | find test/ -iname *.cpp | xargs clang-format -i -style=file 6 | clang-format -i -style=file include/test/testRunner.hpp 7 | -------------------------------------------------------------------------------- /include/test/tests/impl/asyncPosControllerBuilderIntegrationTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runAsyncPosControllerBuilderIntegrationTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/asyncVelControllerBuilderIntegrationTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runAsyncVelControllerBuilderIntegrationTests(); 9 | -------------------------------------------------------------------------------- /include/test/tests/impl/chassisControllerBuilderIntegrationTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runChassisControllerBuilderIntegrationTests(); 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "include/test/snowhouse"] 2 | path = include/test/snowhouse 3 | url = https://github.com/banditcpp/snowhouse 4 | branch = headers-only 5 | [submodule "include/test/FakeIt"] 6 | path = include/test/FakeIt 7 | url = https://github.com/eranpeer/FakeIt.git 8 | [submodule "m.css"] 9 | path = m.css 10 | url = https://github.com/mosra/m.css.git 11 | -------------------------------------------------------------------------------- /src/api/filter/filter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/filter.hpp" 7 | 8 | namespace okapi { 9 | Filter::~Filter() = default; 10 | } // namespace okapi 11 | -------------------------------------------------------------------------------- /include/test/tests/impl/asyncMotionProfileControllerBuilderIntegrationTests.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | void runAsyncMotionProfileControllerBuilderIntegrationTests(); 9 | -------------------------------------------------------------------------------- /src/api/util/abstractRate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/util/abstractRate.hpp" 7 | 8 | namespace okapi { 9 | AbstractRate::~AbstractRate() = default; 10 | } 11 | -------------------------------------------------------------------------------- /src/api/device/rotarysensor/rotarySensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/device/rotarysensor/rotarySensor.hpp" 7 | 8 | namespace okapi { 9 | RotarySensor::~RotarySensor() = default; 10 | } 11 | -------------------------------------------------------------------------------- /src/api/device/button/abstractButton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/device/button/abstractButton.hpp" 7 | 8 | namespace okapi { 9 | AbstractButton::~AbstractButton() = default; 10 | 11 | bool AbstractButton::controllerGet() { 12 | return isPressed(); 13 | } 14 | } // namespace okapi 15 | -------------------------------------------------------------------------------- /src/impl/util/timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/util/timer.hpp" 7 | #include "api.h" 8 | 9 | namespace okapi { 10 | Timer::Timer() : AbstractTimer(millis()) { 11 | } 12 | 13 | QTime Timer::millis() const { 14 | return pros::millis() * millisecond; 15 | } 16 | } // namespace okapi 17 | -------------------------------------------------------------------------------- /docs/api/utility.md: -------------------------------------------------------------------------------- 1 | # Utility API 2 | 3 | - [(Abstract) Abstract Rate](@ref okapi::AbstractRate) 4 | - [(Abstract) Abstract Timer](@ref okapi::AbstractTimer) 5 | - [Logging](@ref okapi::Logger) 6 | - [Math Utilities](@ref mathUtil.hpp) 7 | - [Supplier](@ref okapi::Supplier) 8 | - [TimeUtil](@ref okapi::TimeUtil) 9 | - [TimeUtil Factory](@ref okapi::TimeUtilFactory) 10 | - [ConfigurableTimeUtil Factory](@ref okapi::ConfigurableTimeUtilFactory) 11 | - [Rate](@ref okapi::Rate) 12 | - [Timer](@ref okapi::Timer) 13 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.MD: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | [Description of the issue] 4 | 5 | ### Steps to Reproduce 6 | 7 | 1. [First Step] 8 | 2. [Second Step] 9 | 3. [and so on...] 10 | 11 | **Expected behavior:** [What you expect to happen] 12 | 13 | **Actual behavior:** [What actually happens] 14 | 15 | ### Versions 16 | 17 | What version of OkapiLib and PROS are you on? Run `pros conduct info-project` in your terminal. 18 | 19 | ### Additional Information 20 | 21 | Any additional information, including snippets of your code, required to reproduce the issue. 22 | -------------------------------------------------------------------------------- /include/okapi/api/control/iterative/iterativePositionController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/iterative/iterativeController.hpp" 9 | 10 | namespace okapi { 11 | template 12 | class IterativePositionController : public IterativeController {}; 13 | } // namespace okapi 14 | -------------------------------------------------------------------------------- /include/okapi/api/control/iterative/iterativeVelocityController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/iterative/iterativeController.hpp" 9 | 10 | namespace okapi { 11 | template 12 | class IterativeVelocityController : public IterativeController {}; 13 | } // namespace okapi 14 | -------------------------------------------------------------------------------- /include/okapi/api/control/async/asyncVelocityController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/async/asyncController.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | template 13 | class AsyncVelocityController : virtual public AsyncController {}; 14 | } // namespace okapi 15 | -------------------------------------------------------------------------------- /docs/api/filters.md: -------------------------------------------------------------------------------- 1 | # Filters API 2 | 3 | - [(Abstract) Filter](@ref okapi::Filter) 4 | - [Average Filter](@ref okapi::AverageFilter) 5 | - [Composable Filter](@ref okapi::ComposableFilter) 6 | - [Filtered Controller Input](@ref okapi::FilteredControllerInput) 7 | - [Passthrough Filter](@ref okapi::PassthroughFilter) 8 | - [DEMA Filter](@ref okapi::DemaFilter) 9 | - [EMA Filter](@ref okapi::EmaFilter) 10 | - [Median Filter](@ref okapi::MedianFilter) 11 | - [Kalman Filter](@ref okapi::EKFFilter) 12 | - [Velocity Math](@ref okapi::VelMath) 13 | - [VelMath Factory](@ref okapi::VelMathFactory) -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: nearest 8 | range: "60...90" 9 | 10 | status: 11 | project: 12 | default: 13 | target: 80% 14 | patch: 15 | default: 16 | target: 0% 17 | changes: no 18 | 19 | parsers: 20 | gcov: 21 | branch_detection: 22 | conditional: yes 23 | loop: yes 24 | method: no 25 | macro: no 26 | 27 | comment: 28 | layout: "header, diff" 29 | behavior: default 30 | require_changes: no 31 | 32 | github_checks: 33 | annotations: false 34 | -------------------------------------------------------------------------------- /include/okapi/api/odometry/stateMode.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | namespace okapi { 9 | /** 10 | * The mode for the OdomState calculated by Odometry. 11 | */ 12 | enum class StateMode { 13 | FRAME_TRANSFORMATION, ///< +x is forward, +y is right, 0 degrees is along +x 14 | CARTESIAN ///< +x is right, +y is forward, 0 degrees is along +y 15 | }; 16 | 17 | } // namespace okapi 18 | -------------------------------------------------------------------------------- /include/okapi/impl/util/timer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/util/abstractTimer.hpp" 9 | 10 | namespace okapi { 11 | class Timer : public AbstractTimer { 12 | public: 13 | Timer(); 14 | 15 | /** 16 | * Returns the current time in units of QTime. 17 | * 18 | * @return the current time 19 | */ 20 | QTime millis() const override; 21 | }; 22 | } // namespace okapi 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **Steps To Reproduce** 11 | Steps to reproduce the behavior: 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Versions** 17 | What version of OkapiLib and PROS are you on? Run `prosv5 conduct info-project` in your terminal. 18 | 19 | **Additional Information** 20 | Any additional information, including snippets of your code, required to reproduce the issue. 21 | -------------------------------------------------------------------------------- /include/okapi/api/units/QAngularJerk.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | 14 | namespace okapi { 15 | QUANTITY_TYPE(0, 0, -3, 1, QAngularJerk) 16 | } 17 | -------------------------------------------------------------------------------- /Doxyfile-mcss: -------------------------------------------------------------------------------- 1 | @INCLUDE = Doxyfile 2 | GENERATE_HTML = NO 3 | GENERATE_XML = YES 4 | XML_PROGRAMLISTING = NO 5 | M_SHOW_UNDOCUMENTED = YES 6 | M_FAVICON = logo-doxygen.jpeg 7 | 8 | M_LINKS_NAVBAR1 = \ 9 | md_docs_tutorials_index \ 10 | "md_docs_api_index md_docs_api_chassis md_docs_api_control md_docs_api_devices md_docs_api_filters md_docs_api_units md_docs_api_utility" 11 | 12 | M_LINKS_NAVBAR2 = pages annotated 13 | 14 | HTML_EXTRA_STYLESHEET = \ 15 | ./m.css/css/m-dark+documentation.compiled.css \ 16 | https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600 \ 17 | okapi-docs.css -------------------------------------------------------------------------------- /src/api/filter/passthroughFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | #include "okapi/api/filter/passthroughFilter.hpp" 8 | 9 | namespace okapi { 10 | PassthroughFilter::PassthroughFilter() = default; 11 | 12 | double PassthroughFilter::filter(const double ireading) { 13 | lastOutput = ireading; 14 | return lastOutput; 15 | } 16 | 17 | double PassthroughFilter::getOutput() const { 18 | return lastOutput; 19 | } 20 | } // namespace okapi 21 | -------------------------------------------------------------------------------- /include/okapi/api/units/QAngularAcceleration.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | 14 | namespace okapi { 15 | QUANTITY_TYPE(0, 0, -2, 1, QAngularAcceleration) 16 | } 17 | -------------------------------------------------------------------------------- /include/okapi/api/device/rotarysensor/continuousRotarySensor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/device/rotarysensor/rotarySensor.hpp" 9 | 10 | namespace okapi { 11 | class ContinuousRotarySensor : public RotarySensor { 12 | public: 13 | /** 14 | * Reset the sensor to zero. 15 | * 16 | * @return `1` on success, `PROS_ERR` on fail 17 | */ 18 | virtual std::int32_t reset() = 0; 19 | }; 20 | } // namespace okapi 21 | -------------------------------------------------------------------------------- /test/controlTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/util/flywheelSimulator.hpp" 7 | #include 8 | 9 | using namespace okapi; 10 | 11 | TEST(FlywheelSimulatorTest, BasicTest) { 12 | FlywheelSimulator sim; 13 | 14 | sim.setTorque(0.3); 15 | sim.step(); 16 | 17 | EXPECT_NEAR(sim.getAngle(), 0.000020193, 0.00000005); 18 | EXPECT_NEAR(sim.getOmega(), 0.0020193, 0.000005); 19 | EXPECT_NEAR(sim.getAcceleration(), 20.193, 0.0005); 20 | } 21 | -------------------------------------------------------------------------------- /include/okapi/api/control/controllerInput.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | namespace okapi { 9 | template class ControllerInput { 10 | public: 11 | /** 12 | * Get the sensor value for use in a control loop. This method might be automatically called in 13 | * another thread by the controller. 14 | * 15 | * @return the current sensor value, or ``PROS_ERR`` on a failure. 16 | */ 17 | virtual T controllerGet() = 0; 18 | }; 19 | } // namespace okapi 20 | -------------------------------------------------------------------------------- /include/okapi/api/units/QJerk.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QLength.hpp" 13 | #include "okapi/api/units/QTime.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(0, 1, -3, 0, QJerk) 18 | } 19 | -------------------------------------------------------------------------------- /include/okapi/api/device/rotarysensor/rotarySensor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/controllerInput.hpp" 9 | #include "okapi/api/coreProsAPI.hpp" 10 | 11 | namespace okapi { 12 | class RotarySensor : public ControllerInput { 13 | public: 14 | virtual ~RotarySensor(); 15 | 16 | /** 17 | * Get the current sensor value. 18 | * 19 | * @return the current sensor value, or `PROS_ERR` on a failure. 20 | */ 21 | virtual double get() const = 0; 22 | }; 23 | } // namespace okapi 24 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AccessModifierOffset: 0 3 | AllowAllParametersOfDeclarationOnNextLine: false 4 | AlwaysBreakAfterReturnType: None 5 | AllowShortBlocksOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AllowShortFunctionsOnASingleLine: None 8 | AllowShortIfStatementsOnASingleLine: false 9 | AllowShortLoopsOnASingleLine: false 10 | BinPackArguments: false 11 | BinPackParameters: false 12 | ColumnLimit: 100 13 | ContinuationIndentWidth: 2 14 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 15 | ConstructorInitializerIndentWidth: 2 16 | IndentWidth: 2 17 | IndentWrappedFunctionNames: false 18 | MaxEmptyLinesToKeep: 1 19 | SortIncludes: true 20 | SortUsingDeclarations: true 21 | SpacesInContainerLiterals: false 22 | -------------------------------------------------------------------------------- /include/okapi/api/control/controllerOutput.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | namespace okapi { 9 | template class ControllerOutput { 10 | public: 11 | /** 12 | * Writes the value of the controller output. This method might be automatically called in another 13 | * thread by the controller. The range of input values is expected to be `[-1, 1]`. 14 | * 15 | * @param ivalue the controller's output in the range `[-1, 1]` 16 | */ 17 | virtual void controllerSet(T ivalue) = 0; 18 | }; 19 | } // namespace okapi 20 | -------------------------------------------------------------------------------- /src/api/filter/emaFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/emaFilter.hpp" 7 | 8 | namespace okapi { 9 | EmaFilter::EmaFilter(const double ialpha) : alpha(ialpha) { 10 | } 11 | 12 | double EmaFilter::filter(const double ireading) { 13 | output = alpha * ireading + (1.0 - alpha) * lastOutput; 14 | lastOutput = output; 15 | return output; 16 | } 17 | 18 | double EmaFilter::getOutput() const { 19 | return output; 20 | } 21 | 22 | void EmaFilter::setGains(const double ialpha) { 23 | alpha = ialpha; 24 | } 25 | } // namespace okapi 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Versions** 17 | What version of OkapiLib and PROS are you on? Run `prosv5 conduct info-project` in your terminal. 18 | 19 | **Additional context** 20 | Add any other context about the feature request here. 21 | -------------------------------------------------------------------------------- /include/okapi/api/filter/filter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | namespace okapi { 9 | class Filter { 10 | public: 11 | virtual ~Filter(); 12 | 13 | /** 14 | * Filters a value, like a sensor reading. 15 | * 16 | * @param ireading new measurement 17 | * @return filtered result 18 | */ 19 | virtual double filter(double ireading) = 0; 20 | 21 | /** 22 | * Returns the previous output from filter. 23 | * 24 | * @return the previous output from filter 25 | */ 26 | virtual double getOutput() const = 0; 27 | }; 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /src/impl/util/rate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/util/rate.hpp" 7 | #include "api.h" 8 | 9 | namespace okapi { 10 | Rate::Rate() = default; 11 | 12 | void Rate::delay(const QFrequency ihz) { 13 | delayUntil(1000 / ihz.convert(Hz)); 14 | } 15 | 16 | void Rate::delayUntil(const QTime itime) { 17 | delayUntil(itime.convert(millisecond)); 18 | } 19 | 20 | void Rate::delayUntil(const uint32_t ims) { 21 | if (lastTime == 0) { 22 | // First call 23 | lastTime = pros::millis(); 24 | } 25 | 26 | pros::Task::delay_until(&lastTime, ims); 27 | } 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /src/api/device/motor/abstractMotor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/device/motor/abstractMotor.hpp" 7 | 8 | namespace okapi { 9 | AbstractMotor::~AbstractMotor() = default; 10 | 11 | AbstractMotor::GearsetRatioPair operator*(const AbstractMotor::gearset gearset, 12 | const double ratio) { 13 | return AbstractMotor::GearsetRatioPair(gearset, ratio); 14 | } 15 | 16 | double AbstractMotor::getPositionError() { 17 | return getTargetPosition() - getPosition(); 18 | } 19 | 20 | double AbstractMotor::getVelocityError() { 21 | return getTargetVelocity() - getActualVelocity(); 22 | } 23 | } // namespace okapi 24 | -------------------------------------------------------------------------------- /include/okapi/api/control/async/asyncController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/closedLoopController.hpp" 9 | 10 | namespace okapi { 11 | /** 12 | * Closed-loop controller that steps on its own in another thread and automatically writes to the 13 | * output. 14 | */ 15 | template 16 | class AsyncController : public ClosedLoopController { 17 | public: 18 | /** 19 | * Blocks the current task until the controller has settled. Determining what settling means is 20 | * implementation-dependent. 21 | */ 22 | virtual void waitUntilSettled() = 0; 23 | }; 24 | } // namespace okapi 25 | -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(external-dependencies NONE) 4 | 5 | include(ExternalProject) 6 | 7 | ExternalProject_Add(googletest 8 | GIT_REPOSITORY https://github.com/google/googletest.git 9 | GIT_TAG release-1.8.1 10 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 11 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 12 | CONFIGURE_COMMAND "" 13 | BUILD_COMMAND "" 14 | INSTALL_COMMAND "" 15 | TEST_COMMAND "" 16 | ) 17 | 18 | ExternalProject_Add(squiggles 19 | GIT_REPOSITORY https://github.com/baylessj/robotsquiggles.git 20 | GIT_TAG 1.1.1 21 | SOURCE_DIR "${CMAKE_BINARY_DIR}/squiggles-src" 22 | BINARY_DIR "${CMAKE_BINARY_DIR}/squiggles-build" 23 | CONFIGURE_COMMAND "" 24 | BUILD_COMMAND "" 25 | INSTALL_COMMAND "" 26 | TEST_COMMAND "" 27 | ) 28 | -------------------------------------------------------------------------------- /include/okapi/api/control/util/pathfinderUtil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/units/QAngle.hpp" 9 | #include "okapi/api/units/QLength.hpp" 10 | 11 | namespace okapi { 12 | struct PathfinderPoint { 13 | QLength x; // X coordinate relative to the start of the movement 14 | QLength y; // Y coordinate relative to the start of the movement 15 | QAngle theta; // Exit angle relative to the start of the movement 16 | }; 17 | 18 | struct PathfinderLimits { 19 | double maxVel; // Maximum robot velocity in m/s 20 | double maxAccel; // Maximum robot acceleration in m/s/s 21 | double maxJerk; // Maximum robot jerk in m/s/s/s 22 | }; 23 | } // namespace okapi 24 | -------------------------------------------------------------------------------- /include/pros/error.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file pros/error.h 3 | * 4 | * Contains macro definitions for return types, mostly errors 5 | * 6 | * This file should not be modified by users, since it gets replaced whenever 7 | * a kernel upgrade occurs. 8 | * 9 | * \copyright Copyright (c) 2017-2023, Purdue University ACM SIGBots. 10 | * 11 | * This Source Code Form is subject to the terms of the Mozilla Public 12 | * License, v. 2.0. If a copy of the MPL was not distributed with this 13 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 | */ 15 | #ifndef _PROS_ERROR_H_ 16 | #define _PROS_ERROR_H_ 17 | 18 | #include "limits.h" 19 | 20 | // Different Byte Size Errors 21 | #define PROS_ERR_BYTE (INT8_MAX) 22 | #define PROS_ERR_2_BYTE (INT16_MAX) 23 | #define PROS_ERR (INT32_MAX) 24 | #define PROS_ERR_F (INFINITY) 25 | 26 | // Return This on Success 27 | #define PROS_SUCCESS (1) 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /docs/tutorials/concepts/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | ## Enable the Default Logger 4 | 5 | OkapiLib has a [Logger](@ref okapi::Logger) class which is used internally by many of 6 | OkapiLib's classes. It is a good idea to use the warn 7 | [LogLevel](@ref okapi::Logger::LogLevel) all the time to catch warnings or errors as they arise 8 | during development (or during a match! you can log to a file on the SD card with the path 9 | `"/usd/test_logging.txt"`). Enable it with: 10 | ```cpp 11 | Logger::setDefaultLogger( 12 | std::make_shared( 13 | TimeUtilFactory::createDefault().getTimer(), // It needs a Timer 14 | "/ser/sout", // Output to the PROS terminal 15 | Logger::LogLevel::warn // Show errors and warnings 16 | ) 17 | ); 18 | ``` 19 | 20 | Place that code in a place where it will run before the code you are debugging. 21 | The first line of `initialize` is a good place. 22 | -------------------------------------------------------------------------------- /include/okapi/api/util/supplier.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace okapi { 11 | /** 12 | * A supplier of instances of T. 13 | * 14 | * @tparam T the type to supply 15 | */ 16 | template class Supplier { 17 | public: 18 | explicit Supplier(std::function ifunc) : func(ifunc) { 19 | } 20 | 21 | virtual ~Supplier() = default; 22 | 23 | /** 24 | * Get an instance of type T. This is usually a new instance, but it does not have to be. 25 | * @return an instance of T 26 | */ 27 | T get() const { 28 | return func(); 29 | } 30 | 31 | protected: 32 | std::function func; 33 | }; 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /docs/api/chassis.md: -------------------------------------------------------------------------------- 1 | # Chassis API 2 | 3 | ## Chassis Controller API 4 | 5 | - [(Abstract) Chassis Controller](@ref okapi::ChassisController) 6 | - [Chassis Controller Builder](@ref okapi::ChassisControllerBuilder) 7 | - [Chassis Controller Integrated](@ref okapi::ChassisControllerIntegrated) 8 | - [Chassis Controller PID](@ref okapi::ChassisControllerPID) 9 | - [(Abstract) Odom Chassis Controller](@ref okapi::OdomChassisController) 10 | - [Default Odom Chassis Controller](@ref okapi::DefaultOdomChassisController) 11 | - [Chassis Scales](@ref okapi::ChassisScales) 12 | 13 | ## Chassis Model API 14 | 15 | - [(Abstract) Chassis Model](@ref okapi::ChassisModel) 16 | - [(Abstract) Read-Only Chassis Model](@ref okapi::ReadOnlyChassisModel) 17 | - [Skid-Steer Model](@ref okapi::SkidSteerModel) 18 | - [Three Encoder Skid-Steer Model](@ref okapi::ThreeEncoderSkidSteerModel) 19 | - [X-Drive Model](@ref okapi::XDriveModel) -------------------------------------------------------------------------------- /src/impl/device/rotarysensor/potentiometer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/rotarysensor/potentiometer.hpp" 7 | 8 | namespace okapi { 9 | Potentiometer::Potentiometer(const std::uint8_t iport) : Potentiometer({INTERNAL_ADI_PORT, iport}) { 10 | } 11 | 12 | Potentiometer::Potentiometer(std::pair iports) 13 | : smartPort(std::get<0>(iports)), port(std::get<1>(iports)) { 14 | pros::c::ext_adi_port_set_config(smartPort, port, pros::E_ADI_ANALOG_IN); 15 | } 16 | 17 | double Potentiometer::get() const { 18 | return pros::c::ext_adi_analog_read(smartPort, port); 19 | } 20 | 21 | double Potentiometer::controllerGet() { 22 | return get(); 23 | } 24 | } // namespace okapi 25 | -------------------------------------------------------------------------------- /src/impl/util/configurableTimeUtilFactory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/util/configurableTimeUtilFactory.hpp" 7 | 8 | namespace okapi { 9 | ConfigurableTimeUtilFactory::ConfigurableTimeUtilFactory(const double iatTargetError, 10 | const double iatTargetDerivative, 11 | const QTime &iatTargetTime) 12 | : atTargetError(iatTargetError), 13 | atTargetDerivative(iatTargetDerivative), 14 | atTargetTime(iatTargetTime) { 15 | } 16 | 17 | TimeUtil ConfigurableTimeUtilFactory::create() { 18 | return withSettledUtilParams(atTargetError, atTargetDerivative, atTargetTime); 19 | } 20 | } // namespace okapi 21 | -------------------------------------------------------------------------------- /include/okapi/api/odometry/point.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/odometry/stateMode.hpp" 9 | #include "okapi/api/units/QLength.hpp" 10 | 11 | namespace okapi { 12 | struct Point { 13 | QLength x{0_m}; 14 | QLength y{0_m}; 15 | 16 | /** 17 | * Computes the value of this point in `StateMode::FRAME_TRANSFORMATION`. 18 | * 19 | * @param imode The StateMode this Point is currently specified in. 20 | * @return This point specified in `StateMode::FRAME_TRANSFORMATION`. 21 | */ 22 | Point inFT(const StateMode &imode) const { 23 | if (imode == StateMode::FRAME_TRANSFORMATION) { 24 | return *this; 25 | } else { 26 | return {y, x}; 27 | } 28 | } 29 | }; 30 | } // namespace okapi 31 | -------------------------------------------------------------------------------- /src/api/control/offsettableControllerInput.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/offsettableControllerInput.hpp" 7 | #include "okapi/api/util/mathUtil.hpp" 8 | 9 | namespace okapi { 10 | OffsetableControllerInput::OffsetableControllerInput( 11 | const std::shared_ptr> &iinput) 12 | : input(iinput) { 13 | } 14 | 15 | OffsetableControllerInput::~OffsetableControllerInput() = default; 16 | 17 | double OffsetableControllerInput::controllerGet() { 18 | return input->controllerGet() - offset; 19 | } 20 | 21 | void OffsetableControllerInput::tarePosition() { 22 | const auto reading = input->controllerGet(); 23 | if (reading != OKAPI_PROS_ERR) { 24 | offset = reading; 25 | } 26 | } 27 | } // namespace okapi 28 | -------------------------------------------------------------------------------- /include/okapi/impl/control/util/controllerRunnerFactory.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/util/controllerRunner.hpp" 9 | #include "okapi/impl/util/timeUtilFactory.hpp" 10 | 11 | namespace okapi { 12 | template class ControllerRunnerFactory { 13 | public: 14 | /** 15 | * A utility class that runs a closed-loop controller. 16 | * 17 | * @param ilogger The logger this instance will log to. 18 | * @return 19 | */ 20 | static ControllerRunner 21 | create(const std::shared_ptr &ilogger = Logger::getDefaultLogger()) { 22 | return ControllerRunner(TimeUtilFactory::createDefault(), ilogger); 23 | } 24 | }; 25 | } // namespace okapi 26 | -------------------------------------------------------------------------------- /include/okapi/api/units/QFrequency.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | 14 | namespace okapi { 15 | QUANTITY_TYPE(0, 0, -1, 0, QFrequency) 16 | 17 | constexpr QFrequency Hz(1.0); 18 | 19 | inline namespace literals { 20 | constexpr QFrequency operator"" _Hz(long double x) { 21 | return QFrequency(x); 22 | } 23 | constexpr QFrequency operator"" _Hz(unsigned long long int x) { 24 | return QFrequency(static_cast(x)); 25 | } 26 | } // namespace literals 27 | } // namespace okapi 28 | -------------------------------------------------------------------------------- /include/okapi/api/chassis/model/readOnlyChassisModel.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/coreProsAPI.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | /** 13 | * A version of the ChassisModel that only supports read methods, such as querying sensor values. 14 | * This class does not let you write to motors, so it supports having multiple owners and as a 15 | * result copying is enabled. 16 | */ 17 | class ReadOnlyChassisModel { 18 | public: 19 | virtual ~ReadOnlyChassisModel() = default; 20 | 21 | /** 22 | * Read the sensors. 23 | * 24 | * @return sensor readings (format is implementation dependent) 25 | */ 26 | virtual std::valarray getSensorVals() const = 0; 27 | }; 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /src/impl/device/rotarysensor/integratedEncoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/rotarysensor/integratedEncoder.hpp" 7 | 8 | namespace okapi { 9 | IntegratedEncoder::IntegratedEncoder(const okapi::Motor &imotor) 10 | : IntegratedEncoder(imotor.getPort(), imotor.isReversed()) { 11 | } 12 | 13 | IntegratedEncoder::IntegratedEncoder(const std::int8_t iport, const bool ireversed) 14 | : port(iport), reversed(ireversed ? -1 : 1) { 15 | } 16 | 17 | double IntegratedEncoder::get() const { 18 | return pros::c::motor_get_position(port) * reversed; 19 | } 20 | 21 | std::int32_t IntegratedEncoder::reset() { 22 | return pros::c::motor_tare_position(port); 23 | } 24 | 25 | double IntegratedEncoder::controllerGet() { 26 | return get(); 27 | } 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /src/api/filter/demaFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/demaFilter.hpp" 7 | 8 | namespace okapi { 9 | DemaFilter::DemaFilter(const double ialpha, const double ibeta) : alpha(ialpha), beta(ibeta) { 10 | } 11 | 12 | double DemaFilter::filter(const double ireading) { 13 | outputS = (alpha * ireading) + ((1.0 - alpha) * (lastOutputS + lastOutputB)); 14 | outputB = (beta * (outputS - lastOutputS)) + ((1.0 - beta) * lastOutputB); 15 | lastOutputS = outputS; 16 | lastOutputB = outputB; 17 | return outputS + outputB; 18 | } 19 | 20 | double DemaFilter::getOutput() const { 21 | return outputS + outputB; 22 | } 23 | 24 | void DemaFilter::setGains(const double ialpha, const double ibeta) { 25 | alpha = ialpha; 26 | beta = ibeta; 27 | } 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /src/api/filter/ekfFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/ekfFilter.hpp" 7 | 8 | namespace okapi { 9 | EKFFilter::EKFFilter(const double iQ, const double iR) : Q(iQ), R(iR) { 10 | } 11 | 12 | double EKFFilter::filter(const double ireading) { 13 | return filter(ireading, 0); 14 | } 15 | 16 | double EKFFilter::filter(const double ireading, const double icontrol) { 17 | // Time update 18 | xHatMinus = xHatPrev + icontrol; 19 | Pminus = Pprev + Q; 20 | 21 | // Measurement update 22 | K = Pminus / (Pminus + R); 23 | xHat = xHatMinus + K * (ireading - xHatMinus); 24 | P = (1 - K) * Pminus; 25 | 26 | xHatPrev = xHat; 27 | Pprev = P; 28 | 29 | return xHat; 30 | } 31 | 32 | double EKFFilter::getOutput() const { 33 | return xHat; 34 | } 35 | } // namespace okapi 36 | -------------------------------------------------------------------------------- /include/okapi/impl/util/timeUtilFactory.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/util/timeUtil.hpp" 9 | 10 | namespace okapi { 11 | class TimeUtilFactory { 12 | public: 13 | virtual ~TimeUtilFactory() = default; 14 | 15 | /** 16 | * Creates a default TimeUtil. 17 | */ 18 | virtual TimeUtil create(); 19 | 20 | /** 21 | * Creates a default TimeUtil. 22 | */ 23 | static TimeUtil createDefault(); 24 | 25 | /** 26 | * Creates a TimeUtil with custom SettledUtil params. See SettledUtil docs. 27 | */ 28 | static TimeUtil withSettledUtilParams(double iatTargetError = 50, 29 | double iatTargetDerivative = 5, 30 | const QTime &iatTargetTime = 250_ms); 31 | }; 32 | } // namespace okapi 33 | -------------------------------------------------------------------------------- /include/okapi/api/filter/passthroughFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/filter/filter.hpp" 9 | 10 | namespace okapi { 11 | class PassthroughFilter : public Filter { 12 | public: 13 | /** 14 | * A simple filter that does no filtering and just passes the input through. 15 | */ 16 | PassthroughFilter(); 17 | 18 | /** 19 | * Filters a value, like a sensor reading. 20 | * 21 | * @param ireading new measurement 22 | * @return filtered result 23 | */ 24 | double filter(double ireading) override; 25 | 26 | /** 27 | * Returns the previous output from filter. 28 | * 29 | * @return the previous output from filter 30 | */ 31 | double getOutput() const override; 32 | 33 | protected: 34 | double lastOutput = 0; 35 | }; 36 | } // namespace okapi 37 | -------------------------------------------------------------------------------- /src/api/device/button/buttonBase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/device/button/buttonBase.hpp" 7 | 8 | namespace okapi { 9 | ButtonBase::ButtonBase(const bool iinverted) : inverted(iinverted) { 10 | } 11 | 12 | bool ButtonBase::isPressed() { 13 | return currentlyPressed(); 14 | } 15 | 16 | bool ButtonBase::changed() { 17 | return changedImpl(wasPressedLast_c); 18 | } 19 | 20 | bool ButtonBase::changedToPressed() { 21 | return changedImpl(wasPressedLast_ctp) && wasPressedLast_ctp; 22 | } 23 | 24 | bool ButtonBase::changedToReleased() { 25 | return changedImpl(wasPressedLast_ctr) && !wasPressedLast_ctr; 26 | } 27 | 28 | bool ButtonBase::changedImpl(bool &prevState) { 29 | const bool pressed = currentlyPressed(); 30 | const bool out = pressed ^ prevState; 31 | prevState = pressed; 32 | return out; 33 | } 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /src/impl/device/button/adiButton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/button/adiButton.hpp" 7 | 8 | namespace okapi { 9 | ADIButton::ADIButton(const uint8_t iport, const bool iinverted) 10 | : ADIButton({INTERNAL_ADI_PORT, iport}, iinverted) { 11 | } 12 | 13 | ADIButton::ADIButton(std::pair iports, const bool iinverted) 14 | : ButtonBase(iinverted), smartPort(std::get<0>(iports)), port(std::get<1>(iports)) { 15 | pros::c::ext_adi_port_set_config(smartPort, port, pros::E_ADI_DIGITAL_IN); 16 | } 17 | 18 | bool ADIButton::currentlyPressed() { 19 | const std::int32_t state = pros::c::ext_adi_digital_read(smartPort, port); 20 | const bool pressed = state != 0 && state != PROS_ERR; 21 | if (inverted) { 22 | return !pressed; 23 | } else { 24 | return pressed; 25 | } 26 | } 27 | } // namespace okapi 28 | -------------------------------------------------------------------------------- /src/impl/device/button/controllerButton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/button/controllerButton.hpp" 7 | 8 | namespace okapi { 9 | ControllerButton::ControllerButton(const ControllerDigital ibtn, const bool iinverted) 10 | : ControllerButton(ControllerId::master, ibtn, iinverted) { 11 | } 12 | 13 | ControllerButton::ControllerButton(const ControllerId icontroller, 14 | const ControllerDigital ibtn, 15 | const bool iinverted) 16 | : ButtonBase(iinverted), 17 | id(ControllerUtil::idToProsEnum(icontroller)), 18 | btn(ControllerUtil::digitalToProsEnum(ibtn)) { 19 | } 20 | 21 | bool ControllerButton::currentlyPressed() { 22 | const bool pressed = pros::c::controller_get_digital(id, btn) != 0; 23 | return inverted == !pressed; 24 | } 25 | } // namespace okapi 26 | -------------------------------------------------------------------------------- /docs/tutorials/concepts/builders-and-tasks.md: -------------------------------------------------------------------------------- 1 | # A note about where to use builders 2 | 3 | OkapiLib's builders wire together any tasks internal to the object being built such that they are 4 | stopped when the calling task (the task the builder is run from) is deleted (to prevent runaway 5 | tasks). This means that if you run the builder's `build()` method in the `autonomous` task, the 6 | built object's internal tasks will be stopped once `autonomous` ends. If you put the built object in 7 | a variable in global scope and then you try to use it in `opcontrol`, **it will not work** because 8 | the object's internal tasks will be stopped when `autonomous` ends. 9 | 10 | This is the case for every task except for `initialize`. To work around this limitation, you can: 11 | - Use the builder in global scope and save the built object to a variable also in global scope 12 | - Use the builder in `initialize` and save the built object to a variable in global scope 13 | - Use the builder in a local scope and save the built object to a variable _also in the same local 14 | scope_ 15 | -------------------------------------------------------------------------------- /include/okapi/api/control/async/asyncPositionController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/async/asyncController.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | template 13 | class AsyncPositionController : virtual public AsyncController { 14 | public: 15 | /** 16 | * Sets the "absolute" zero position of the controller to its current position. 17 | */ 18 | virtual void tarePosition() = 0; 19 | 20 | /** 21 | * Sets a new maximum velocity (typically motor RPM [0-600]). The interpretation of the units 22 | * of this velocity and whether it will be respected is implementation-dependent. 23 | * 24 | * @param imaxVelocity The new maximum velocity. 25 | */ 26 | virtual void setMaxVelocity(std::int32_t imaxVelocity) = 0; 27 | }; 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /include/okapi/api/units/QArea.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QLength.hpp" 13 | #include "okapi/api/units/RQuantity.hpp" 14 | 15 | namespace okapi { 16 | QUANTITY_TYPE(0, 2, 0, 0, QArea) 17 | 18 | constexpr QArea kilometer2 = kilometer * kilometer; 19 | constexpr QArea meter2 = meter * meter; 20 | constexpr QArea decimeter2 = decimeter * decimeter; 21 | constexpr QArea centimeter2 = centimeter * centimeter; 22 | constexpr QArea millimeter2 = millimeter * millimeter; 23 | constexpr QArea inch2 = inch * inch; 24 | constexpr QArea foot2 = foot * foot; 25 | constexpr QArea mile2 = mile * mile; 26 | } // namespace okapi 27 | -------------------------------------------------------------------------------- /src/impl/device/distanceSensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/distanceSensor.hpp" 7 | 8 | namespace okapi { 9 | DistanceSensor::DistanceSensor(std::uint8_t iport, std::unique_ptr ifilter) 10 | : filter(std::move(ifilter)) { 11 | port = iport; 12 | } 13 | 14 | double DistanceSensor::get() { 15 | return filter->filter(pros::c::distance_get(port)); 16 | } 17 | 18 | double DistanceSensor::controllerGet() { 19 | return get(); 20 | } 21 | 22 | int32_t DistanceSensor::getConfidence() const { 23 | return pros::c::distance_get_confidence(port); 24 | } 25 | 26 | int32_t DistanceSensor::getObjectSize() const { 27 | return pros::c::distance_get_object_size(port); 28 | } 29 | 30 | double DistanceSensor::getObjectVelocity() const { 31 | return filter->filter(pros::c::distance_get_object_velocity(port)); 32 | } 33 | 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description of the Change 2 | 3 | 6 | 7 | ### Motivation 8 | 9 | 10 | 11 | ### Possible Drawbacks 12 | 13 | 14 | 15 | ### Verification Process 16 | 17 | 24 | 25 | ### Applicable Issues 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/api/devices.md: -------------------------------------------------------------------------------- 1 | # Devices API 2 | 3 | ## Button API 4 | 5 | - [(Abstract) Button](@ref okapi::AbstractButton) 6 | - [Button Base](@ref okapi::ButtonBase) 7 | - [Controller Button](@ref okapi::ControllerButton) 8 | - [ADI Button](@ref okapi::ADIButton) 9 | 10 | ## Motor API 11 | 12 | - [(Abstract) Abstract Motor](@ref okapi::AbstractMotor) 13 | - [Motor](@ref okapi::Motor) 14 | - [Motor Group](@ref okapi::MotorGroup) 15 | - [ADI Motor](@ref okapi::ADIMotor) 16 | 17 | ## RotarySensor API 18 | 19 | - [(Abstract) Continuous Rotary Sensor](@ref okapi::ContinuousRotarySensor) 20 | - [(Abstract) Rotary Sensor](@ref okapi::RotarySensor) 21 | - [Integrated Encoder](@ref okapi::IntegratedEncoder) 22 | - [ADI Encoder](@ref okapi::ADIEncoder) 23 | - [ADI Gyro](@ref okapi::ADIGyro) 24 | - [IMU](@ref okapi::IMU) 25 | - [Potentiometer](@ref okapi::Potentiometer) 26 | - [Rotation Sensor](@ref okapi::RotationSensor) 27 | 28 | ## Other API 29 | 30 | - [Controller](@ref okapi::Controller) 31 | - [ADI Ultrasonic](@ref okapi::ADIUltrasonic) 32 | - [Distance Sensor](@ref okapi::DistanceSensor) 33 | - [Optical Sensor](@ref okapi::OpticalSensor) 34 | -------------------------------------------------------------------------------- /include/okapi/api/units/QVolume.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QArea.hpp" 13 | #include "okapi/api/units/QLength.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(0, 3, 0, 0, QVolume) 18 | 19 | constexpr QVolume kilometer3 = kilometer2 * kilometer; 20 | constexpr QVolume meter3 = meter2 * meter; 21 | constexpr QVolume decimeter3 = decimeter2 * decimeter; 22 | constexpr QVolume centimeter3 = centimeter2 * centimeter; 23 | constexpr QVolume millimeter3 = millimeter2 * millimeter; 24 | constexpr QVolume inch3 = inch2 * inch; 25 | constexpr QVolume foot3 = foot2 * foot; 26 | constexpr QVolume mile3 = mile2 * mile; 27 | constexpr QVolume litre = decimeter3; 28 | } // namespace okapi 29 | -------------------------------------------------------------------------------- /include/okapi/api/filter/emaFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/filter/filter.hpp" 9 | 10 | namespace okapi { 11 | class EmaFilter : public Filter { 12 | public: 13 | /** 14 | * Exponential moving average filter. 15 | * 16 | * @param ialpha alpha gain 17 | */ 18 | explicit EmaFilter(double ialpha); 19 | 20 | /** 21 | * Filters a value, like a sensor reading. 22 | * 23 | * @param reading new measurement 24 | * @return filtered result 25 | */ 26 | double filter(double ireading) override; 27 | 28 | /** 29 | * Returns the previous output from filter. 30 | * 31 | * @return the previous output from filter 32 | */ 33 | double getOutput() const override; 34 | 35 | /** 36 | * Set filter gains. 37 | * 38 | * @param ialpha alpha gain 39 | */ 40 | virtual void setGains(double ialpha); 41 | 42 | protected: 43 | double alpha; 44 | double output = 0; 45 | double lastOutput = 0; 46 | }; 47 | } // namespace okapi 48 | -------------------------------------------------------------------------------- /src/api/filter/composableFilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/composableFilter.hpp" 7 | #include 8 | 9 | namespace okapi { 10 | ComposableFilter::ComposableFilter(const std::initializer_list> &ilist) { 11 | for (auto &&elem : ilist) { 12 | filters.push_back(elem); 13 | } 14 | } 15 | 16 | double ComposableFilter::filter(const double ireading) { 17 | if (filters.empty()) { 18 | return 0; 19 | } 20 | 21 | // Initial sensor reading 22 | filters.front()->filter(ireading); 23 | 24 | // Propagate signal 25 | for (std::size_t i = 1; i < filters.size(); i++) { 26 | filters[i]->filter(filters[i - 1]->getOutput()); 27 | } 28 | 29 | output = filters.back()->getOutput(); 30 | return output; 31 | } 32 | 33 | double ComposableFilter::getOutput() const { 34 | return output; 35 | } 36 | 37 | void ComposableFilter::addFilter(std::shared_ptr ifilter) { 38 | filters.push_back(std::move(ifilter)); 39 | } 40 | } // namespace okapi 41 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | docs: 10 | runs-on: ubuntu-latest 11 | container: 12 | image: okapilib/ubuntu-base:18.04 13 | steps: 14 | - name: Cancel Previous Runs 15 | uses: styfle/cancel-workflow-action@0.6.0 16 | with: 17 | access_token: ${{ github.token }} 18 | 19 | - uses: actions/checkout@v1 20 | 21 | - name: Update Submodules 22 | run: git submodule update --init --recursive 23 | 24 | - name: Build Docs 25 | run: ./run_doxygen.sh 26 | 27 | - name: Commit Changes 28 | run: | 29 | rm -rf /tmp/html 30 | cp -r html /tmp/ 31 | git config --local user.name "OkapiLibBot" 32 | git config --local user.email "octogonapus@okapilib.org" 33 | git checkout gh-pages 34 | rm -rf * 35 | cp -r /tmp/html/* . 36 | git add . 37 | git commit -m "Publishing GitHub Pages" 38 | 39 | - name: Push changes 40 | uses: ad-m/github-push-action@master 41 | with: 42 | github_token: ${{ secrets.GITHUB_TOKEN }} 43 | branch: gh-pages 44 | force: true 45 | -------------------------------------------------------------------------------- /include/okapi/impl/util/configurableTimeUtilFactory.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/impl/util/timeUtilFactory.hpp" 9 | 10 | namespace okapi { 11 | /** 12 | * A TimeUtilFactory that supplies the SettledUtil parameters passed in the constructor to every 13 | * new TimeUtil instance. 14 | */ 15 | class ConfigurableTimeUtilFactory : public TimeUtilFactory { 16 | public: 17 | ConfigurableTimeUtilFactory(double iatTargetError = 50, 18 | double iatTargetDerivative = 5, 19 | const QTime &iatTargetTime = 250_ms); 20 | 21 | /** 22 | * Creates a TimeUtil with the SettledUtil parameters specified in the constructor by 23 | * delegating to TimeUtilFactory::withSettledUtilParams. 24 | * 25 | * @return A TimeUtil with the SettledUtil parameters specified in the constructor. 26 | */ 27 | TimeUtil create() override; 28 | 29 | private: 30 | double atTargetError; 31 | double atTargetDerivative; 32 | QTime atTargetTime; 33 | }; 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /include/okapi/api/units/QAngle.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | #include 14 | 15 | namespace okapi { 16 | QUANTITY_TYPE(0, 0, 0, 1, QAngle) 17 | 18 | constexpr QAngle radian(1.0); 19 | constexpr QAngle degree = static_cast(2_pi / 360.0) * radian; 20 | 21 | inline namespace literals { 22 | constexpr QAngle operator"" _rad(long double x) { 23 | return QAngle(x); 24 | } 25 | constexpr QAngle operator"" _rad(unsigned long long int x) { 26 | return QAngle(static_cast(x)); 27 | } 28 | constexpr QAngle operator"" _deg(long double x) { 29 | return static_cast(x) * degree; 30 | } 31 | constexpr QAngle operator"" _deg(unsigned long long int x) { 32 | return static_cast(x) * degree; 33 | } 34 | } // namespace literals 35 | } // namespace okapi 36 | -------------------------------------------------------------------------------- /src/impl/device/adiUltrasonic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/adiUltrasonic.hpp" 7 | 8 | namespace okapi { 9 | ADIUltrasonic::ADIUltrasonic(const std::uint8_t iportPing, 10 | const std::uint8_t iportEcho, 11 | std::unique_ptr ifilter) 12 | : ADIUltrasonic({INTERNAL_ADI_PORT, iportPing, iportEcho}, std::move(ifilter)) { 13 | } 14 | 15 | ADIUltrasonic::ADIUltrasonic(std::tuple iports, 16 | std::unique_ptr ifilter) 17 | : ultra(pros::c::ext_adi_ultrasonic_init(std::get<0>(iports), 18 | std::get<1>(iports), 19 | std::get<2>(iports))), 20 | filter(std::move(ifilter)) { 21 | } 22 | 23 | ADIUltrasonic::~ADIUltrasonic() = default; 24 | 25 | double ADIUltrasonic::get() { 26 | return filter->filter(pros::c::ext_adi_ultrasonic_get(ultra)); 27 | } 28 | 29 | double ADIUltrasonic::controllerGet() { 30 | return get(); 31 | } 32 | } // namespace okapi 33 | -------------------------------------------------------------------------------- /src/impl/device/rotarysensor/adiGyro.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/rotarysensor/adiGyro.hpp" 7 | #include "okapi/api/util/mathUtil.hpp" 8 | 9 | namespace okapi { 10 | ADIGyro::ADIGyro(const std::uint8_t iport, const double imultiplier) 11 | : ADIGyro({INTERNAL_ADI_PORT, iport}, imultiplier) { 12 | } 13 | 14 | ADIGyro::ADIGyro(std::pair iports, const double imultiplier) 15 | : gyro(pros::c::ext_adi_gyro_init(std::get<0>(iports), std::get<1>(iports), imultiplier)) { 16 | } 17 | 18 | double ADIGyro::get() const { 19 | return pros::c::ext_adi_gyro_get(gyro); 20 | } 21 | 22 | double ADIGyro::getRemapped(const double iupperBound, const double ilowerBound) const { 23 | const auto value = get(); 24 | 25 | if (value == PROS_ERR) { 26 | return value; 27 | } 28 | 29 | return remapRange(value, -3600, 3600, ilowerBound, iupperBound); 30 | } 31 | 32 | std::int32_t ADIGyro::reset() { 33 | return pros::c::ext_adi_gyro_reset(gyro); 34 | } 35 | 36 | double ADIGyro::controllerGet() { 37 | return get(); 38 | } 39 | } // namespace okapi 40 | -------------------------------------------------------------------------------- /src/impl/device/rotarysensor/rotationSensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/rotarysensor/rotationSensor.hpp" 7 | 8 | namespace okapi { 9 | RotationSensor::RotationSensor(const std::uint8_t iport, const bool ireversed) 10 | : port(iport), reversed(ireversed ? -1 : 1) { 11 | } 12 | 13 | double RotationSensor::get() const { 14 | const double out = pros::c::rotation_get_position(port); 15 | if (out == PROS_ERR_F) { 16 | return PROS_ERR_F; 17 | } else { 18 | // Convert from centidegrees to degrees 19 | return out * 0.01 * reversed; 20 | } 21 | } 22 | 23 | double RotationSensor::getVelocity() const { 24 | const double out = pros::c::rotation_get_velocity(port); 25 | if (out == PROS_ERR_F) { 26 | return PROS_ERR_F; 27 | } else { 28 | // Convert from centidegrees per second to degrees per second 29 | return out * 0.01 * reversed; 30 | } 31 | } 32 | 33 | std::int32_t RotationSensor::reset() { 34 | return pros::c::rotation_reset(port); 35 | } 36 | 37 | double RotationSensor::controllerGet() { 38 | return get(); 39 | } 40 | } // namespace okapi 41 | -------------------------------------------------------------------------------- /src/impl/device/rotarysensor/adiEncoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/rotarysensor/adiEncoder.hpp" 7 | 8 | namespace okapi { 9 | ADIEncoder::ADIEncoder(const std::uint8_t iportTop, 10 | const std::uint8_t iportBottom, 11 | const bool ireversed) 12 | : ADIEncoder({INTERNAL_ADI_PORT, iportTop, iportBottom}, ireversed) { 13 | } 14 | 15 | ADIEncoder::ADIEncoder(std::tuple iports, 16 | const bool ireversed) 17 | : enc(pros::c::ext_adi_encoder_init(std::get<0>(iports), 18 | std::get<1>(iports), 19 | std::get<2>(iports), 20 | ireversed)) { 21 | } 22 | 23 | double ADIEncoder::get() const { 24 | return static_cast(pros::c::ext_adi_encoder_get(enc)); 25 | } 26 | 27 | std::int32_t ADIEncoder::reset() { 28 | return pros::c::ext_adi_encoder_reset(enc); 29 | } 30 | 31 | double ADIEncoder::controllerGet() { 32 | return get(); 33 | } 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /closed-loop-controller-diagram.xml: -------------------------------------------------------------------------------- 1 | 3ZrbbpswGICfJpebsM3xck27rdIqVaq0bpcuuMDm4MhxmmRPPwM2hAJRpRXjkZvgH+PDl/9oskLrzfELx9vsjiWErqCTHFfoegXlJwTyq5ScakngebUg5XlSi0AreMj/ECV0lHSfJ2TX6SgYoyLfdoUxKwoSi44Mc84O3W7PjHZn3eKU9AQPMaZ96WOeiKyWhp7Tyr+SPM30zMBRd55w/DvlbF+o+VYQPVef+vYG67FU/12GE3Y4E6GbFVpzxkR9tTmuCS3Zamz1c59H7jbr5qQQb3kgUMsQJ711kkgSqsm4yFjKCkxvWulVtT1SDuDIViY2VF4CeSnn5Kcfpfyjp5s/VbdfRIiT+qHxXjApakf/xthWjVGvp1zE6I6UaMf2PFa9oNIRzFOierkNTamlhG2IXIzswgnFIn/pjo6VuqRNvxaZvFDUhgmGiyXoGyKopn7BdK8GXVO2I0m5pzWTCBilhPcwdyEeslyQhy2u9nOQXqkLVk1BuCDHy1j6G1YPIF/ZrPJpQLcPrYcArvJz2Zl30P3+hRFwl6Fmbl/NADSkZ8BbLkNkiKHbs9VbQXg1h0WmCp2uqcJwwFR1CH5vU21+sv9czfwBNYtMhQS4WIZnad7EDBeSmQwxDAwx9Hvu7tPuVMTnrs6nclFXT+VVKppNzuj8PH9G5xeNx4d7tru/vbYoSnxoKChSbjRAyp+IlC51h1B9J9QuVOGcoMA4qDsmGJe0WJyLk0W8YPAGYOFUwPxlOH4wUJOaSkD03COOLBc5KyzSt54rQ0NOfzILXcgpUl1FvUp5TaVrYCHp2iDEEe19f4joYlC1LUyAcEarhe96pDSxUkV9pYKmzjzQQorRQYjGAmq/MqhKKQuDqacPcS9ZpTeVVS4kedP5SEfZPFMWu5DT8iGIyJTbg/0CtbJYC+NoNKvF9nMO7dluC0FSmX2QxCJYTS16qTYNJnqPBb1RpbISFnC8GWmhqd5YQdOOLJjRkaGpQmpgAUVjCZye/LXlPkqz21pgqUH0hlpqsnd+49mtXQe5ftSNlIPuzJ2IEhqJlPYddwfmKMlm+3ev6t7Zf+rQzV8= -------------------------------------------------------------------------------- /include/okapi/impl/device/button/controllerButton.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/device/button/buttonBase.hpp" 10 | #include "okapi/impl/device/controllerUtil.hpp" 11 | 12 | namespace okapi { 13 | class ControllerButton : public ButtonBase { 14 | public: 15 | /** 16 | * A button on a Controller. 17 | * 18 | * @param ibtn The button id. 19 | * @param iinverted Whether the button is inverted (default pressed instead of default released). 20 | */ 21 | ControllerButton(ControllerDigital ibtn, bool iinverted = false); 22 | 23 | /** 24 | * A button on a Controller. 25 | * 26 | * @param icontroller The Controller the button is on. 27 | * @param ibtn The button id. 28 | * @param iinverted Whether the button is inverted (default pressed instead of default released). 29 | */ 30 | ControllerButton(ControllerId icontroller, ControllerDigital ibtn, bool iinverted = false); 31 | 32 | protected: 33 | pros::controller_id_e_t id; 34 | pros::controller_digital_e_t btn; 35 | 36 | virtual bool currentlyPressed() override; 37 | }; 38 | } // namespace okapi 39 | -------------------------------------------------------------------------------- /include/okapi/api/units/QAcceleration.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QLength.hpp" 13 | #include "okapi/api/units/QTime.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(0, 1, -2, 0, QAcceleration) 18 | 19 | constexpr QAcceleration mps2 = meter / (second * second); 20 | constexpr QAcceleration G = 9.80665 * mps2; 21 | 22 | inline namespace literals { 23 | constexpr QAcceleration operator"" _mps2(long double x) { 24 | return QAcceleration(x); 25 | } 26 | constexpr QAcceleration operator"" _mps2(unsigned long long int x) { 27 | return QAcceleration(static_cast(x)); 28 | } 29 | constexpr QAcceleration operator"" _G(long double x) { 30 | return static_cast(x) * G; 31 | } 32 | constexpr QAcceleration operator"" _G(unsigned long long int x) { 33 | return static_cast(x) * G; 34 | } 35 | } // namespace literals 36 | } // namespace okapi 37 | -------------------------------------------------------------------------------- /include/okapi/impl/util/rate.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/util/abstractRate.hpp" 9 | 10 | namespace okapi { 11 | class Rate : public AbstractRate { 12 | public: 13 | Rate(); 14 | 15 | /** 16 | * Delay the current task such that it runs at the given frequency. The first delay will run for 17 | * 1000/(ihz). Subsequent delays will adjust according to the previous runtime of the task. 18 | * 19 | * @param ihz the frequency 20 | */ 21 | void delay(QFrequency ihz) override; 22 | 23 | /** 24 | * Delay the current task until itime has passed. This method can be used by periodic tasks to 25 | * ensure a consistent execution frequency. 26 | * 27 | * @param itime the time period 28 | */ 29 | void delayUntil(QTime itime) override; 30 | 31 | /** 32 | * Delay the current task until ims milliseconds have passed. This method can be used by 33 | * periodic tasks to ensure a consistent execution frequency. 34 | * 35 | * @param ims the time period 36 | */ 37 | void delayUntil(uint32_t ims) override; 38 | 39 | protected: 40 | std::uint32_t lastTime{0}; 41 | }; 42 | } // namespace okapi 43 | -------------------------------------------------------------------------------- /test/asyncVelPIDControllerTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/async/asyncVelPidController.hpp" 7 | #include "test/tests/api/implMocks.hpp" 8 | #include 9 | 10 | using namespace okapi; 11 | 12 | class AsyncVelPIDControllerTest : public ::testing::Test { 13 | protected: 14 | void SetUp() override { 15 | input = std::make_shared(); 16 | output = std::make_shared(); 17 | controller = new AsyncVelPIDController( 18 | input, 19 | output, 20 | createTimeUtil(), 21 | 0, 22 | 0, 23 | 0, 24 | 0, 25 | std::make_unique( 26 | 360, std::make_unique(), 10_ms, std::make_unique())); 27 | } 28 | 29 | void TearDown() override { 30 | delete controller; 31 | } 32 | 33 | std::shared_ptr input; 34 | std::shared_ptr output; 35 | AsyncVelPIDController *controller; 36 | }; 37 | 38 | TEST_F(AsyncVelPIDControllerTest, TestSetAndGetGains) { 39 | IterativeVelPIDController::Gains gains{1, 2, 3, 4}; 40 | controller->setGains(gains); 41 | EXPECT_EQ(controller->getGains(), gains); 42 | } 43 | -------------------------------------------------------------------------------- /include/okapi/api/filter/demaFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/filter/filter.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | class DemaFilter : public Filter { 13 | public: 14 | /** 15 | * Double exponential moving average filter. 16 | * 17 | * @param ialpha alpha gain 18 | * @param ibeta beta gain 19 | */ 20 | DemaFilter(double ialpha, double ibeta); 21 | 22 | /** 23 | * Filters a value, like a sensor reading. 24 | * 25 | * @param reading new measurement 26 | * @return filtered result 27 | */ 28 | double filter(double ireading) override; 29 | 30 | /** 31 | * Returns the previous output from filter. 32 | * 33 | * @return the previous output from filter 34 | */ 35 | double getOutput() const override; 36 | 37 | /** 38 | * Set filter gains. 39 | * 40 | * @param ialpha alpha gain 41 | * @param ibeta beta gain 42 | */ 43 | virtual void setGains(double ialpha, double ibeta); 44 | 45 | protected: 46 | double alpha, beta; 47 | double outputS = 0; 48 | double lastOutputS = 0; 49 | double outputB = 0; 50 | double lastOutputB = 0; 51 | }; 52 | } // namespace okapi 53 | -------------------------------------------------------------------------------- /include/okapi/api/util/abstractRate.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/coreProsAPI.hpp" 9 | #include "okapi/api/units/QFrequency.hpp" 10 | #include "okapi/api/units/QTime.hpp" 11 | 12 | namespace okapi { 13 | class AbstractRate { 14 | public: 15 | virtual ~AbstractRate(); 16 | 17 | /** 18 | * Delay the current task such that it runs at the given frequency. The first delay will run for 19 | * 1000/(ihz). Subsequent delays will adjust according to the previous runtime of the task. 20 | * 21 | * @param ihz the frequency 22 | */ 23 | virtual void delay(QFrequency ihz) = 0; 24 | 25 | /** 26 | * Delay the current task until itime has passed. This method can be used by periodic tasks to 27 | * ensure a consistent execution frequency. 28 | * 29 | * @param itime the time period 30 | */ 31 | virtual void delayUntil(QTime itime) = 0; 32 | 33 | /** 34 | * Delay the current task until ims milliseconds have passed. This method can be used by 35 | * periodic tasks to ensure a consistent execution frequency. 36 | * 37 | * @param ims the time period 38 | */ 39 | virtual void delayUntil(uint32_t ims) = 0; 40 | }; 41 | } // namespace okapi 42 | -------------------------------------------------------------------------------- /firmware/v5.ld: -------------------------------------------------------------------------------- 1 | /* This stack is used during initialization, but FreeRTOS tasks have their own 2 | stack allocated in BSS or Heap (kernel tasks in FreeRTOS .bss heap; user tasks 3 | in standard heap) */ 4 | _STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; 5 | 6 | _ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; 7 | _SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; 8 | _IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024; 9 | _FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; 10 | _UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; 11 | 12 | _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x02E00000; /* ~48 MB */ 13 | 14 | /* Define Memories in the system */ 15 | start_of_cold_mem = 0x03800000; 16 | _COLD_MEM_SIZE = 0x04800000; 17 | end_of_cold_mem = start_of_cold_mem + _COLD_MEM_SIZE; 18 | 19 | start_of_hot_mem = 0x07800000; 20 | _HOT_MEM_SIZE = 0x00800000; 21 | end_of_hot_mem = start_of_hot_mem + _HOT_MEM_SIZE; 22 | 23 | MEMORY 24 | { 25 | /* user code 72M */ 26 | COLD_MEMORY : ORIGIN = start_of_cold_mem, LENGTH = _COLD_MEM_SIZE /* Just under 19 MB */ 27 | HEAP : ORIGIN = 0x04A00000, LENGTH = _HEAP_SIZE 28 | HOT_MEMORY : ORIGIN = start_of_hot_mem, LENGTH = _HOT_MEM_SIZE /* Just over 8 MB */ 29 | } 30 | 31 | REGION_ALIAS("RAM", COLD_MEMORY); 32 | 33 | ENTRY(vexStartup) 34 | -------------------------------------------------------------------------------- /src/impl/device/motor/adiMotor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/motor/adiMotor.hpp" 7 | 8 | namespace okapi { 9 | ADIMotor::ADIMotor(const std::uint8_t iport, 10 | const bool ireverse, 11 | const std::shared_ptr &logger) 12 | : ADIMotor({INTERNAL_ADI_PORT, iport}, ireverse, logger) { 13 | } 14 | 15 | ADIMotor::ADIMotor(std::pair iports, 16 | const bool ireverse, 17 | const std::shared_ptr &logger) 18 | : smartPort(std::get<0>(iports)), 19 | port(transformADIPort(std::get<1>(iports))), 20 | reversed(ireverse ? -1 : 1) { 21 | if (port < 1 || port > 8) { 22 | std::string msg = "ADIMotor: The port number (" + std::to_string(port) + 23 | ") is outside the expected range of values [1, 8]."; 24 | LOG_ERROR(msg); 25 | } 26 | } 27 | 28 | void ADIMotor::moveVoltage(const std::int8_t ivoltage) const { 29 | pros::c::ext_adi_motor_set(smartPort, port, ivoltage * reversed); 30 | } 31 | 32 | void ADIMotor::controllerSet(const double ivalue) { 33 | pros::c::ext_adi_motor_set(smartPort, port, ivalue * reversed * 127); 34 | } 35 | } // namespace okapi 36 | -------------------------------------------------------------------------------- /firmware/v5-hot.ld: -------------------------------------------------------------------------------- 1 | /* This stack is used during initialization, but FreeRTOS tasks have their own 2 | stack allocated in BSS or Heap (kernel tasks in FreeRTOS .bss heap; user tasks 3 | in standard heap) */ 4 | _STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; 5 | 6 | _ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; 7 | _SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; 8 | _IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024; 9 | _FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; 10 | _UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; 11 | 12 | _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x02E00000; /* ~48 MB */ 13 | 14 | /* Define Memories in the system */ 15 | start_of_cold_mem = 0x03800000; 16 | _COLD_MEM_SIZE = 0x04800000; 17 | end_of_cold_mem = start_of_cold_mem + _COLD_MEM_SIZE; 18 | 19 | start_of_hot_mem = 0x07800000; 20 | _HOT_MEM_SIZE = 0x00800000; 21 | end_of_hot_mem = start_of_hot_mem + _HOT_MEM_SIZE; 22 | 23 | MEMORY 24 | { 25 | /* user code 72M */ 26 | COLD_MEMORY : ORIGIN = start_of_cold_mem, LENGTH = _COLD_MEM_SIZE /* Just under 19 MB */ 27 | HEAP : ORIGIN = 0x04A00000, LENGTH = _HEAP_SIZE 28 | HOT_MEMORY : ORIGIN = start_of_hot_mem, LENGTH = _HOT_MEM_SIZE /* Just over 8 MB */ 29 | } 30 | 31 | REGION_ALIAS("RAM", HOT_MEMORY); 32 | 33 | ENTRY(install_hot_table) 34 | -------------------------------------------------------------------------------- /include/okapi/api/control/offsettableControllerInput.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/controllerInput.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | class OffsetableControllerInput : public ControllerInput { 13 | public: 14 | /** 15 | * A ControllerInput which can be tared to change the zero position. 16 | * 17 | * @param iinput The ControllerInput to reference. 18 | */ 19 | explicit OffsetableControllerInput(const std::shared_ptr> &iinput); 20 | 21 | virtual ~OffsetableControllerInput(); 22 | 23 | /** 24 | * Get the sensor value for use in a control loop. This method might be automatically called in 25 | * another thread by the controller. 26 | * 27 | * @return the current sensor value, or PROS_ERR on a failure. 28 | */ 29 | double controllerGet() override; 30 | 31 | /** 32 | * Sets the "absolute" zero position of this controller input to its current position. This does 33 | * nothing if the underlying controller input returns PROS_ERR. 34 | */ 35 | virtual void tarePosition(); 36 | 37 | protected: 38 | std::shared_ptr> input; 39 | double offset{0}; 40 | }; 41 | } // namespace okapi 42 | -------------------------------------------------------------------------------- /src/test/allImplTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "test/tests/impl/allImplTests.hpp" 7 | #include "test/testRunner.hpp" 8 | #include "test/tests/impl/asyncMotionProfileControllerBuilderIntegrationTests.hpp" 9 | #include "test/tests/impl/asyncPosControllerBuilderIntegrationTests.hpp" 10 | #include "test/tests/impl/asyncPosIntegratedControllerTests.hpp" 11 | #include "test/tests/impl/asyncVelControllerBuilderIntegrationTests.hpp" 12 | #include "test/tests/impl/chassisControllerBuilderIntegrationTests.hpp" 13 | #include "test/tests/impl/chassisControllerIntegratedTests.hpp" 14 | #include "test/tests/impl/chassisControllerPidTests.hpp" 15 | #include "test/tests/impl/controllerTests.hpp" 16 | #include "test/tests/impl/utilTests.hpp" 17 | 18 | using namespace okapi; 19 | 20 | void runAllImplTests() { 21 | // runChassisControllerBuilderIntegrationTests(); 22 | // runAsyncPosControllerBuilderIntegrationTests(); 23 | // runAsyncMotionProfileControllerBuilderIntegrationTests(); 24 | // runAsyncPosIntegratedControllerTests(); 25 | // runAsyncVelControllerBuilderIntegrationTests(); 26 | // runUtilTests(); 27 | runControllerTests(); 28 | // runChassisControllerPidTests(); 29 | // runChassisControllerIntegratedTests(); 30 | test_print_report(); 31 | } 32 | -------------------------------------------------------------------------------- /test/offsettableControllerInputTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/offsettableControllerInput.hpp" 7 | #include "test/tests/api/implMocks.hpp" 8 | #include 9 | 10 | using namespace okapi; 11 | 12 | class OffsettableControllerInputTest : public ::testing::Test { 13 | protected: 14 | void SetUp() override { 15 | mockInput = std::make_shared(); 16 | input = new OffsetableControllerInput(mockInput); 17 | } 18 | 19 | void TearDown() override { 20 | delete input; 21 | } 22 | 23 | std::shared_ptr mockInput; 24 | OffsetableControllerInput *input; 25 | }; 26 | 27 | TEST_F(OffsettableControllerInputTest, TestTarePositionNormally) { 28 | mockInput->reading = 100; 29 | EXPECT_EQ(input->controllerGet(), 100); 30 | 31 | input->tarePosition(); 32 | 33 | mockInput->reading = 200; 34 | EXPECT_EQ(input->controllerGet(), 100); 35 | } 36 | 37 | TEST_F(OffsettableControllerInputTest, TestTarePositionDoesNothingWithError) { 38 | mockInput->reading = 100; 39 | EXPECT_EQ(input->controllerGet(), 100); 40 | 41 | mockInput->reading = OKAPI_PROS_ERR; 42 | input->tarePosition(); 43 | 44 | mockInput->reading = 200; 45 | EXPECT_EQ(input->controllerGet(), 200); 46 | } 47 | -------------------------------------------------------------------------------- /src/api/util/timeUtil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/util/timeUtil.hpp" 7 | 8 | namespace okapi { 9 | TimeUtil::TimeUtil(const okapi::Supplier> &itimerSupplier, 10 | const okapi::Supplier> &irateSupplier, 11 | const okapi::Supplier> &isettledUtilSupplier) 12 | : timerSupplier(itimerSupplier), 13 | rateSupplier(irateSupplier), 14 | settledUtilSupplier(isettledUtilSupplier) { 15 | } 16 | 17 | std::unique_ptr TimeUtil::getTimer() const { 18 | return timerSupplier.get(); 19 | } 20 | 21 | std::unique_ptr TimeUtil::getRate() const { 22 | return rateSupplier.get(); 23 | } 24 | 25 | std::unique_ptr TimeUtil::getSettledUtil() const { 26 | return settledUtilSupplier.get(); 27 | } 28 | 29 | Supplier> TimeUtil::getTimerSupplier() const { 30 | return timerSupplier; 31 | } 32 | 33 | Supplier> TimeUtil::getRateSupplier() const { 34 | return rateSupplier; 35 | } 36 | 37 | Supplier> TimeUtil::getSettledUtilSupplier() const { 38 | return settledUtilSupplier; 39 | } 40 | } // namespace okapi 41 | -------------------------------------------------------------------------------- /include/okapi/api/units/QAngularSpeed.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QAngle.hpp" 13 | #include "okapi/api/units/QFrequency.hpp" 14 | #include "okapi/api/units/QTime.hpp" 15 | #include "okapi/api/units/RQuantity.hpp" 16 | 17 | namespace okapi { 18 | QUANTITY_TYPE(0, 0, -1, 1, QAngularSpeed) 19 | 20 | constexpr QAngularSpeed radps = radian / second; 21 | constexpr QAngularSpeed rpm = (360 * degree) / minute; 22 | constexpr QAngularSpeed cps = (0.01 * degree) / second; // centidegree per second 23 | 24 | #pragma GCC diagnostic push 25 | #pragma GCC diagnostic ignored "-Wunused-function" 26 | static QAngularSpeed convertHertzToRadPerSec(QFrequency in) { 27 | return (in.convert(Hz) / 2_pi) * radps; 28 | } 29 | #pragma GCC diagnostic pop 30 | 31 | inline namespace literals { 32 | constexpr QAngularSpeed operator"" _rpm(long double x) { 33 | return x * rpm; 34 | } 35 | constexpr QAngularSpeed operator"" _rpm(unsigned long long int x) { 36 | return static_cast(x) * rpm; 37 | } 38 | } // namespace literals 39 | } // namespace okapi 40 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "test/tests/impl/allImplTests.hpp" 3 | 4 | using namespace okapi; 5 | 6 | std::shared_ptr drive; 7 | 8 | void printSensorVals(void *) { 9 | while (true) { 10 | auto state = drive->getState(); 11 | std::cout << state.str() << std::endl; 12 | pros::delay(50); 13 | } 14 | } 15 | 16 | void opcontrol() { 17 | pros::delay(100); 18 | 19 | Logger::setDefaultLogger(std::make_shared( 20 | TimeUtilFactory::createDefault().getTimer(), "/ser/sout", Logger::LogLevel::debug)); 21 | 22 | drive = ChassisControllerBuilder() 23 | .withMotors(-1, 2) 24 | .withDimensions(AbstractMotor::gearset::green, {{4_in, 11_in}, imev5GreenTPR}) 25 | .withMaxVelocity(60) 26 | .withOdometry(StateMode::FRAME_TRANSFORMATION) 27 | .buildOdometry(); 28 | 29 | pros::Task printSensorValsTask(printSensorVals, NULL, ""); 30 | 31 | // drive->driveToPoint({6_in, 2_in}); 32 | // drive->driveToPoint({6_in, 6_in}); 33 | // drive->driveToPoint({0_in, 0_in}, true); 34 | // drive->turnToPoint({6_in, 2_in}); 35 | // drive->turnToAngle(90_deg); 36 | // drive->moveDistance(6_in); 37 | // drive->turnAngle(90_deg); 38 | // drive->moveDistance(6_in); 39 | 40 | while (true) { 41 | pros::delay(50); 42 | } 43 | } 44 | 45 | void initialize() { 46 | } 47 | 48 | void disabled() { 49 | } 50 | 51 | void competition_initialize() { 52 | } 53 | 54 | void autonomous() { 55 | } 56 | -------------------------------------------------------------------------------- /include/okapi/api/device/button/abstractButton.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/controllerInput.hpp" 9 | 10 | namespace okapi { 11 | class AbstractButton : public ControllerInput { 12 | public: 13 | virtual ~AbstractButton(); 14 | 15 | /** 16 | * Return whether the button is currently pressed. 17 | **/ 18 | virtual bool isPressed() = 0; 19 | 20 | /** 21 | * Return whether the state of the button changed since the last time this method was 22 | * called. 23 | **/ 24 | virtual bool changed() = 0; 25 | 26 | /** 27 | * Return whether the state of the button changed to being pressed since the last time this method 28 | * was called. 29 | **/ 30 | virtual bool changedToPressed() = 0; 31 | 32 | /** 33 | * Return whether the state of the button to being not pressed changed since the last time this 34 | * method was called. 35 | **/ 36 | virtual bool changedToReleased() = 0; 37 | 38 | /** 39 | * Get the sensor value for use in a control loop. This method might be automatically called in 40 | * another thread by the controller. 41 | * 42 | * @return the current sensor value. This is the same as the output of the pressed() method. 43 | */ 44 | virtual bool controllerGet() override; 45 | }; 46 | } // namespace okapi 47 | -------------------------------------------------------------------------------- /include/okapi/api/filter/averageFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/filter/filter.hpp" 9 | #include 10 | #include 11 | 12 | namespace okapi { 13 | /** 14 | * A filter which returns the average of a list of values. 15 | * 16 | * @tparam n number of taps in the filter 17 | */ 18 | template class AverageFilter : public Filter { 19 | public: 20 | /** 21 | * Averaging filter. 22 | */ 23 | AverageFilter() = default; 24 | 25 | /** 26 | * Filters a value, like a sensor reading. 27 | * 28 | * @param ireading new measurement 29 | * @return filtered result 30 | */ 31 | double filter(const double ireading) override { 32 | data[index++] = ireading; 33 | if (index >= n) { 34 | index = 0; 35 | } 36 | 37 | output = 0.0; 38 | for (size_t i = 0; i < n; i++) 39 | output += data[i]; 40 | output /= (double)n; 41 | 42 | return output; 43 | } 44 | 45 | /** 46 | * Returns the previous output from filter. 47 | * 48 | * @return the previous output from filter 49 | */ 50 | double getOutput() const override { 51 | return output; 52 | } 53 | 54 | protected: 55 | std::array data{0}; 56 | std::size_t index = 0; 57 | double output = 0; 58 | }; 59 | } // namespace okapi 60 | -------------------------------------------------------------------------------- /src/api/odometry/odomState.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/odometry/odomState.hpp" 7 | #include 8 | 9 | namespace okapi { 10 | bool OdomState::operator==(const OdomState &rhs) const { 11 | return x == rhs.x && y == rhs.y && theta == rhs.theta; 12 | } 13 | 14 | bool OdomState::operator!=(const OdomState &rhs) const { 15 | return !(rhs == *this); 16 | } 17 | 18 | std::string OdomState::str(QLength idistanceUnit, 19 | std::string distUnitName, 20 | QAngle iangleUnit, 21 | std::string angleUnitName) const { 22 | char buf[150]; 23 | snprintf(buf, 24 | sizeof(buf), 25 | "OdomState(x=%.2f%s, y=%.2f%s, theta=%.2f%s)", 26 | x.convert(idistanceUnit), 27 | distUnitName.c_str(), 28 | y.convert(idistanceUnit), 29 | distUnitName.c_str(), 30 | theta.convert(iangleUnit), 31 | angleUnitName.c_str()); 32 | return std::string(buf); 33 | } 34 | 35 | std::string OdomState::str(QLength idistanceUnit, QAngle iangleUnit) const { 36 | return str(idistanceUnit, 37 | "_" + std::string(getShortUnitName(idistanceUnit)), 38 | iangleUnit, 39 | "_" + std::string(getShortUnitName(iangleUnit))); 40 | } 41 | 42 | } // namespace okapi 43 | -------------------------------------------------------------------------------- /src/api/util/logging.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/util/logging.hpp" 7 | 8 | namespace okapi { 9 | std::shared_ptr defaultLogger; 10 | 11 | int DefaultLoggerInitializer::count; 12 | 13 | Logger::Logger() noexcept : Logger(nullptr, nullptr, LogLevel::off) { 14 | } 15 | 16 | Logger::Logger(std::unique_ptr itimer, 17 | std::string_view ifileName, 18 | const Logger::LogLevel &ilevel) noexcept 19 | : Logger(std::move(itimer), 20 | fopen(ifileName.data(), isSerialStream(ifileName) ? "w" : "a"), 21 | ilevel) { 22 | } 23 | 24 | Logger::Logger(std::unique_ptr itimer, 25 | FILE *const ifile, 26 | const Logger::LogLevel &ilevel) noexcept 27 | : timer(std::move(itimer)), logLevel(ilevel), logfile(ifile) { 28 | } 29 | 30 | Logger::~Logger() { 31 | if (logfile) { 32 | fclose(logfile); 33 | logfile = nullptr; 34 | } 35 | } 36 | 37 | std::shared_ptr Logger::getDefaultLogger() { 38 | return defaultLogger; 39 | } 40 | 41 | void Logger::setDefaultLogger(std::shared_ptr ilogger) { 42 | defaultLogger = std::move(ilogger); 43 | } 44 | 45 | bool Logger::isSerialStream(std::string_view filename) { 46 | return filename.find("/ser/") != std::string::npos; 47 | } 48 | } // namespace okapi 49 | -------------------------------------------------------------------------------- /include/okapi/api/filter/composableFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/filter/filter.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace okapi { 15 | class ComposableFilter : public Filter { 16 | public: 17 | /** 18 | * A composable filter is a filter that consists of other filters. The input signal is passed 19 | * through each filter in sequence. The final output of this filter is the output of the last 20 | * filter. 21 | * 22 | * @param ilist The filters to use in sequence. 23 | */ 24 | ComposableFilter(const std::initializer_list> &ilist); 25 | 26 | /** 27 | * Filters a value. 28 | * 29 | * @param ireading A new measurement. 30 | * @return The filtered result. 31 | */ 32 | double filter(double ireading) override; 33 | 34 | /** 35 | * @return The previous output from filter. 36 | */ 37 | double getOutput() const override; 38 | 39 | /** 40 | * Adds a filter to the end of the sequence. 41 | * 42 | * @param ifilter The filter to add. 43 | */ 44 | virtual void addFilter(std::shared_ptr ifilter); 45 | 46 | protected: 47 | std::vector> filters; 48 | double output = 0; 49 | }; 50 | } // namespace okapi 51 | -------------------------------------------------------------------------------- /include/okapi/api/util/timeUtil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/util/settledUtil.hpp" 9 | #include "okapi/api/util/abstractRate.hpp" 10 | #include "okapi/api/util/abstractTimer.hpp" 11 | #include "okapi/api/util/supplier.hpp" 12 | 13 | namespace okapi { 14 | /** 15 | * Utility class for holding an AbstractTimer, AbstractRate, and SettledUtil together in one 16 | * class since they are commonly used together. 17 | */ 18 | class TimeUtil { 19 | public: 20 | TimeUtil(const Supplier> &itimerSupplier, 21 | const Supplier> &irateSupplier, 22 | const Supplier> &isettledUtilSupplier); 23 | 24 | std::unique_ptr getTimer() const; 25 | 26 | std::unique_ptr getRate() const; 27 | 28 | std::unique_ptr getSettledUtil() const; 29 | 30 | Supplier> getTimerSupplier() const; 31 | 32 | Supplier> getRateSupplier() const; 33 | 34 | Supplier> getSettledUtilSupplier() const; 35 | 36 | protected: 37 | Supplier> timerSupplier; 38 | Supplier> rateSupplier; 39 | Supplier> settledUtilSupplier; 40 | }; 41 | } // namespace okapi 42 | -------------------------------------------------------------------------------- /include/okapi/api/device/button/buttonBase.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/device/button/abstractButton.hpp" 9 | 10 | namespace okapi { 11 | class ButtonBase : public AbstractButton { 12 | public: 13 | /** 14 | * @param iinverted Whether the button is inverted (`true` meaning default pressed and `false` 15 | * meaning default not pressed). 16 | */ 17 | explicit ButtonBase(bool iinverted = false); 18 | 19 | /** 20 | * Return whether the button is currently pressed. 21 | **/ 22 | bool isPressed() override; 23 | 24 | /** 25 | * Return whether the state of the button changed since the last time this method was called. 26 | **/ 27 | bool changed() override; 28 | 29 | /** 30 | * Return whether the state of the button changed to pressed since the last time this method was 31 | *called. 32 | **/ 33 | bool changedToPressed() override; 34 | 35 | /** 36 | * Return whether the state of the button to not pressed since the last time this method was 37 | *called. 38 | **/ 39 | bool changedToReleased() override; 40 | 41 | protected: 42 | bool inverted{false}; 43 | bool wasPressedLast_c{false}; 44 | bool wasPressedLast_ctp{false}; 45 | bool wasPressedLast_ctr{false}; 46 | 47 | virtual bool currentlyPressed() = 0; 48 | 49 | private: 50 | bool changedImpl(bool &prevState); 51 | }; 52 | } // namespace okapi 53 | -------------------------------------------------------------------------------- /include/okapi/api/units/QForce.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QAcceleration.hpp" 13 | #include "okapi/api/units/QMass.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(1, 1, -2, 0, QForce) 18 | 19 | constexpr QForce newton = (kg * meter) / (second * second); 20 | constexpr QForce poundforce = pound * G; 21 | constexpr QForce kilopond = kg * G; 22 | 23 | inline namespace literals { 24 | constexpr QForce operator"" _n(long double x) { 25 | return QForce(x); 26 | } 27 | constexpr QForce operator"" _n(unsigned long long int x) { 28 | return QForce(static_cast(x)); 29 | } 30 | constexpr QForce operator"" _lbf(long double x) { 31 | return static_cast(x) * poundforce; 32 | } 33 | constexpr QForce operator"" _lbf(unsigned long long int x) { 34 | return static_cast(x) * poundforce; 35 | } 36 | constexpr QForce operator"" _kp(long double x) { 37 | return static_cast(x) * kilopond; 38 | } 39 | constexpr QForce operator"" _kp(unsigned long long int x) { 40 | return static_cast(x) * kilopond; 41 | } 42 | } // namespace literals 43 | } // namespace okapi 44 | -------------------------------------------------------------------------------- /src/impl/util/timeUtilFactory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/util/timeUtilFactory.hpp" 7 | #include "okapi/impl/util/rate.hpp" 8 | #include "okapi/impl/util/timer.hpp" 9 | 10 | namespace okapi { 11 | TimeUtil TimeUtilFactory::create() { 12 | return TimeUtilFactory::createDefault(); 13 | } 14 | 15 | TimeUtil TimeUtilFactory::createDefault() { 16 | return TimeUtil( 17 | Supplier>([]() { return std::make_unique(); }), 18 | Supplier>([]() { return std::make_unique(); }), 19 | Supplier>( 20 | []() { return std::make_unique(std::make_unique()); })); 21 | } 22 | 23 | TimeUtil TimeUtilFactory::withSettledUtilParams(const double iatTargetError, 24 | const double iatTargetDerivative, 25 | const QTime &iatTargetTime) { 26 | return TimeUtil( 27 | Supplier>([]() { return std::make_unique(); }), 28 | Supplier>([]() { return std::make_unique(); }), 29 | Supplier>([=]() { 30 | return std::make_unique( 31 | std::make_unique(), iatTargetError, iatTargetDerivative, iatTargetTime); 32 | })); 33 | } 34 | } // namespace okapi 35 | -------------------------------------------------------------------------------- /include/okapi/api/units/QSpeed.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QLength.hpp" 13 | #include "okapi/api/units/QTime.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(0, 1, -1, 0, QSpeed) 18 | 19 | constexpr QSpeed mps = meter / second; 20 | constexpr QSpeed miph = mile / hour; 21 | constexpr QSpeed kmph = kilometer / hour; 22 | 23 | inline namespace literals { 24 | constexpr QSpeed operator"" _mps(long double x) { 25 | return static_cast(x) * mps; 26 | } 27 | constexpr QSpeed operator"" _miph(long double x) { 28 | return static_cast(x) * mile / hour; 29 | } 30 | constexpr QSpeed operator"" _kmph(long double x) { 31 | return static_cast(x) * kilometer / hour; 32 | } 33 | constexpr QSpeed operator"" _mps(unsigned long long int x) { 34 | return static_cast(x) * mps; 35 | } 36 | constexpr QSpeed operator"" _miph(unsigned long long int x) { 37 | return static_cast(x) * mile / hour; 38 | } 39 | constexpr QSpeed operator"" _kmph(unsigned long long int x) { 40 | return static_cast(x) * kilometer / hour; 41 | } 42 | } // namespace literals 43 | } // namespace okapi 44 | -------------------------------------------------------------------------------- /include/okapi/api/units/QPressure.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QAcceleration.hpp" 13 | #include "okapi/api/units/QArea.hpp" 14 | #include "okapi/api/units/QMass.hpp" 15 | #include "okapi/api/units/RQuantity.hpp" 16 | 17 | namespace okapi { 18 | QUANTITY_TYPE(1, -1, -2, 0, QPressure) 19 | 20 | constexpr QPressure pascal(1.0); 21 | constexpr QPressure bar = 100000 * pascal; 22 | constexpr QPressure psi = pound * G / inch2; 23 | 24 | inline namespace literals { 25 | constexpr QPressure operator"" _Pa(long double x) { 26 | return QPressure(x); 27 | } 28 | constexpr QPressure operator"" _Pa(unsigned long long int x) { 29 | return QPressure(static_cast(x)); 30 | } 31 | constexpr QPressure operator"" _bar(long double x) { 32 | return static_cast(x) * bar; 33 | } 34 | constexpr QPressure operator"" _bar(unsigned long long int x) { 35 | return static_cast(x) * bar; 36 | } 37 | constexpr QPressure operator"" _psi(long double x) { 38 | return static_cast(x) * psi; 39 | } 40 | constexpr QPressure operator"" _psi(unsigned long long int x) { 41 | return static_cast(x) * psi; 42 | } 43 | } // namespace literals 44 | } // namespace okapi 45 | -------------------------------------------------------------------------------- /include/okapi/api/units/QTorque.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/QForce.hpp" 13 | #include "okapi/api/units/QLength.hpp" 14 | #include "okapi/api/units/RQuantity.hpp" 15 | 16 | namespace okapi { 17 | QUANTITY_TYPE(1, 2, -2, 0, QTorque) 18 | 19 | constexpr QTorque newtonMeter = newton * meter; 20 | constexpr QTorque footPound = 1.355817948 * newtonMeter; 21 | constexpr QTorque inchPound = 0.083333333 * footPound; 22 | 23 | inline namespace literals { 24 | constexpr QTorque operator"" _nM(long double x) { 25 | return QTorque(x); 26 | } 27 | constexpr QTorque operator"" _nM(unsigned long long int x) { 28 | return QTorque(static_cast(x)); 29 | } 30 | constexpr QTorque operator"" _inLb(long double x) { 31 | return static_cast(x) * inchPound; 32 | } 33 | constexpr QTorque operator"" _inLb(unsigned long long int x) { 34 | return static_cast(x) * inchPound; 35 | } 36 | constexpr QTorque operator"" _ftLb(long double x) { 37 | return static_cast(x) * footPound; 38 | } 39 | constexpr QTorque operator"" _ftLb(unsigned long long int x) { 40 | return static_cast(x) * footPound; 41 | } 42 | } // namespace literals 43 | } // namespace okapi 44 | -------------------------------------------------------------------------------- /include/pros/device.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file pros/device.h 3 | * 4 | * Contains functions for interacting with VEX devices. 5 | * 6 | * 7 | * 8 | * This file should not be modified by users, since it gets replaced whenever 9 | * a kernel upgrade occurs. 10 | * 11 | * \copyright (c) 2017-2021, Purdue University ACM SIGBots. 12 | * 13 | * This Source Code Form is subject to the terms of the Mozilla Public 14 | * License, v. 2.0. If a copy of the MPL was not distributed with this 15 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 16 | */ 17 | 18 | #ifndef _PROS_DEVICE_H_ 19 | #define _PROS_DEVICE_H_ 20 | 21 | #include 22 | 23 | #ifdef __cplusplus 24 | namespace pros::c { 25 | extern "C" { 26 | #endif 27 | 28 | /* 29 | * List of possible v5 devices 30 | * 31 | * This list contains all current V5 Devices, and mirrors V5_DeviceType from the 32 | * api. 33 | */ 34 | typedef enum v5_device_e { 35 | E_DEVICE_NONE = 0, 36 | E_DEVICE_MOTOR = 2, 37 | E_DEVICE_ROTATION = 4, 38 | E_DEVICE_IMU = 6, 39 | E_DEVICE_DISTANCE = 7, 40 | E_DEVICE_RADIO = 8, 41 | E_DEVICE_VISION = 11, 42 | E_DEVICE_ADI = 12, 43 | E_DEVICE_OPTICAL = 16, 44 | E_DEVICE_GPS = 20, 45 | E_DEVICE_SERIAL = 129, 46 | E_DEVICE_GENERIC __attribute__((deprecated("use E_DEVICE_SERIAL instead"))) = E_DEVICE_SERIAL, 47 | E_DEVICE_UNDEFINED = 255 48 | } v5_device_e_t; 49 | 50 | /** 51 | * Gets the type of device on given port. 52 | * 53 | * \return The device type as an enum. 54 | */ 55 | v5_device_e_t get_plugged_type(uint8_t port); 56 | 57 | #ifdef __cplusplus 58 | } // namespace c 59 | } // namespace pros 60 | #endif 61 | 62 | #endif // _PROS_DEVICE_H_ 63 | -------------------------------------------------------------------------------- /include/okapi/impl/device/controllerUtil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | 10 | namespace okapi { 11 | /** 12 | * Which controller role this has. 13 | */ 14 | enum class ControllerId { 15 | master = 0, ///< master 16 | partner = 1 ///< partner 17 | }; 18 | 19 | /** 20 | * The analog sticks. 21 | */ 22 | enum class ControllerAnalog { 23 | leftX = 0, ///< leftX 24 | leftY = 1, ///< leftY 25 | rightX = 2, ///< rightX 26 | rightY = 3 ///< rightY 27 | }; 28 | 29 | /** 30 | * Various buttons. 31 | */ 32 | enum class ControllerDigital { 33 | L1 = 6, ///< L1 34 | L2 = 7, ///< L2 35 | R1 = 8, ///< R1 36 | R2 = 9, ///< R2 37 | up = 10, ///< up 38 | down = 11, ///< down 39 | left = 12, ///< left 40 | right = 13, ///< right 41 | X = 14, ///< X 42 | B = 15, ///< B 43 | Y = 16, ///< Y 44 | A = 17 ///< A 45 | }; 46 | 47 | class ControllerUtil { 48 | public: 49 | /** 50 | * Maps an `id` to the PROS enum equivalent. 51 | */ 52 | static pros::controller_id_e_t idToProsEnum(ControllerId in); 53 | 54 | /** 55 | * Maps an `analog` to the PROS enum equivalent. 56 | */ 57 | static pros::controller_analog_e_t analogToProsEnum(ControllerAnalog in); 58 | 59 | /** 60 | * Maps a `digital` to the PROS enum equivalent. 61 | */ 62 | static pros::controller_digital_e_t digitalToProsEnum(ControllerDigital in); 63 | }; 64 | } // namespace okapi 65 | -------------------------------------------------------------------------------- /src/api/control/util/settledUtil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/util/settledUtil.hpp" 7 | #include 8 | 9 | namespace okapi { 10 | SettledUtil::SettledUtil(std::unique_ptr iatTargetTimer, 11 | const double iatTargetError, 12 | const double iatTargetDerivative, 13 | const QTime iatTargetTime) 14 | : atTargetError(iatTargetError), 15 | atTargetDerivative(iatTargetDerivative), 16 | atTargetTime(iatTargetTime), 17 | atTargetTimer(std::move(iatTargetTimer)) { 18 | } 19 | 20 | SettledUtil::~SettledUtil() = default; 21 | 22 | bool SettledUtil::isSettled(const double ierror) { 23 | if (std::fabs(ierror) <= atTargetError && std::fabs(ierror - lastError) <= atTargetDerivative) { 24 | /* 25 | * Timer::getDtFromhardMark() returns 0_ms if there is no hard mark set, so this needs to be 26 | * special-cased. Setting atTargetTime to 0_ms means that the user wants to exit immediately 27 | * when in range of the target. 28 | */ 29 | if (atTargetTime == 0_ms) { 30 | return true; 31 | } 32 | 33 | atTargetTimer->placeHardMark(); 34 | } else { 35 | atTargetTimer->clearHardMark(); 36 | } 37 | 38 | lastError = ierror; 39 | 40 | return atTargetTimer->getDtFromHardMark() > atTargetTime; 41 | } 42 | 43 | void SettledUtil::reset() { 44 | atTargetTimer->clearHardMark(); 45 | lastError = 0; 46 | } 47 | } // namespace okapi 48 | -------------------------------------------------------------------------------- /src/api/chassis/model/threeEncoderSkidSteerModel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/chassis/model/threeEncoderSkidSteerModel.hpp" 7 | 8 | namespace okapi { 9 | ThreeEncoderSkidSteerModel::ThreeEncoderSkidSteerModel( 10 | std::shared_ptr ileftSideMotor, 11 | std::shared_ptr irightSideMotor, 12 | std::shared_ptr ileftEnc, 13 | std::shared_ptr irightEnc, 14 | std::shared_ptr imiddleEnc, 15 | const double imaxVelocity, 16 | const double imaxVoltage) 17 | : SkidSteerModel(std::move(ileftSideMotor), 18 | std::move(irightSideMotor), 19 | std::move(ileftEnc), 20 | std::move(irightEnc), 21 | imaxVelocity, 22 | imaxVoltage), 23 | middleSensor(std::move(imiddleEnc)) { 24 | } 25 | 26 | std::valarray ThreeEncoderSkidSteerModel::getSensorVals() const { 27 | // Return the middle sensor last so this is compatible with SkidSteerModel::getSensorVals() 28 | return std::valarray{static_cast(leftSensor->get()), 29 | static_cast(rightSensor->get()), 30 | static_cast(middleSensor->get())}; 31 | } 32 | 33 | void ThreeEncoderSkidSteerModel::resetSensors() { 34 | SkidSteerModel::resetSensors(); 35 | middleSensor->reset(); 36 | } 37 | } // namespace okapi 38 | -------------------------------------------------------------------------------- /test/asyncPosPIDControllerTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/async/asyncPosPidController.hpp" 7 | #include "test/tests/api/implMocks.hpp" 8 | #include 9 | 10 | using namespace okapi; 11 | 12 | class AsyncPosPIDControllerTest : public ::testing::Test { 13 | protected: 14 | void SetUp() override { 15 | input = std::make_shared(); 16 | output = std::make_shared(); 17 | controller = new AsyncPosPIDController(input, output, createTimeUtil(), 0, 0, 0); 18 | } 19 | 20 | void TearDown() override { 21 | delete controller; 22 | } 23 | 24 | std::shared_ptr input; 25 | std::shared_ptr output; 26 | AsyncPosPIDController *controller; 27 | }; 28 | 29 | TEST_F(AsyncPosPIDControllerTest, TestTarePosition) { 30 | controller->startThread(); 31 | auto rate = createTimeUtil().getRate(); 32 | 33 | input->reading = 0; 34 | controller->setTarget(100); 35 | rate->delayUntil(100_ms); 36 | EXPECT_EQ(controller->getError(), 100); 37 | 38 | input->reading = 100; 39 | rate->delayUntil(100_ms); 40 | EXPECT_EQ(controller->getError(), 0); 41 | 42 | controller->tarePosition(); 43 | rate->delayUntil(100_ms); 44 | EXPECT_EQ(controller->getError(), 100); 45 | } 46 | 47 | TEST_F(AsyncPosPIDControllerTest, TestSetAndGetGains) { 48 | IterativePosPIDController::Gains gains{1, 2, 3, 4}; 49 | controller->setGains(gains); 50 | EXPECT_EQ(controller->getGains(), gains); 51 | } 52 | -------------------------------------------------------------------------------- /include/okapi/api/filter/filteredControllerInput.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/controllerInput.hpp" 9 | #include "okapi/api/filter/filter.hpp" 10 | #include 11 | 12 | namespace okapi { 13 | /** 14 | * A ControllerInput with a filter built in. 15 | * 16 | * @tparam InputType the type of the ControllerInput 17 | * @tparam FilterType the type of the Filter 18 | */ 19 | template 20 | class FilteredControllerInput : public ControllerInput { 21 | public: 22 | /** 23 | * A filtered controller input. Applies a filter to the controller input. Useful if you want to 24 | * place a filter between a control input and a control loop. 25 | * 26 | * @param iinput ControllerInput type 27 | * @param ifilter Filter type 28 | */ 29 | FilteredControllerInput(std::unique_ptr> iinput, 30 | std::unique_ptr ifilter) 31 | : input(std::move(iinput)), filter(std::move(ifilter)) { 32 | } 33 | 34 | /** 35 | * Gets the sensor value for use in a control loop. This method might be automatically called in 36 | * another thread by the controller. 37 | * 38 | * @return the current filtered sensor value. 39 | */ 40 | double controllerGet() override { 41 | return filter->filter(input->controllerGet()); 42 | } 43 | 44 | protected: 45 | std::unique_ptr> input; 46 | std::unique_ptr filter; 47 | }; 48 | } // namespace okapi 49 | -------------------------------------------------------------------------------- /include/okapi/impl/device/button/adiButton.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/device/button/buttonBase.hpp" 10 | 11 | namespace okapi { 12 | class ADIButton : public ButtonBase { 13 | public: 14 | /** 15 | * A button in an ADI port. 16 | * 17 | * ```cpp 18 | * auto btn = ADIButton('A', false); 19 | * auto invertedBtn = ADIButton('A', true); 20 | * ``` 21 | * 22 | * @param iport The ADI port number (``[1, 8]``, ``[a, h]``, ``[A, H]``). 23 | * @param iinverted Whether the button is inverted (``true`` meaning default pressed and ``false`` 24 | * meaning default not pressed). 25 | */ 26 | ADIButton(std::uint8_t iport, bool iinverted = false); 27 | 28 | /** 29 | * A button in an ADI port. 30 | * 31 | * ```cpp 32 | * auto btn = ADIButton({1, 'A'}, false); 33 | * auto invertedBtn = ADIButton({1, 'A'}, true); 34 | * ``` 35 | * 36 | * @param iports The ports the button is plugged in to in the order ``{smart port, button port}``. 37 | * The smart port is the smart port number (``[1, 21]``). The button port is the ADI port number 38 | * (``[1, 8]``, ``[a, h]``, ``[A, H]``). 39 | * @param iinverted Whether the button is inverted (``true`` meaning default pressed and ``false`` 40 | * meaning default not pressed). 41 | */ 42 | ADIButton(std::pair iports, bool iinverted = false); 43 | 44 | protected: 45 | std::uint8_t smartPort; 46 | std::uint8_t port; 47 | 48 | virtual bool currentlyPressed() override; 49 | }; 50 | } // namespace okapi 51 | -------------------------------------------------------------------------------- /include/okapi/api/units/RQuantityName.hpp: -------------------------------------------------------------------------------- 1 | #include "okapi/api/units/QAngle.hpp" 2 | #include "okapi/api/units/QLength.hpp" 3 | #include "okapi/api/units/QSpeed.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma once 9 | 10 | namespace okapi { 11 | 12 | /** 13 | * Returns a short name for a unit. 14 | * For example: `str(1_ft)` will return "ft", so will `1 * foot` or `0.3048_m`. 15 | * Throws std::domain_error when `q` is a unit not defined in this function. 16 | * 17 | * @param q Your unit. Currently only QLength and QAngle are supported. 18 | * @return The short string suffix for that unit. 19 | */ 20 | template std::string getShortUnitName(QType q) { 21 | const std::unordered_map> shortNameMap = 22 | {{typeid(meter), 23 | { 24 | {meter.getValue(), "m"}, 25 | {decimeter.getValue(), "dm"}, 26 | {centimeter.getValue(), "cm"}, 27 | {millimeter.getValue(), "mm"}, 28 | {kilometer.getValue(), "km"}, 29 | {inch.getValue(), "in"}, 30 | {foot.getValue(), "ft"}, 31 | {yard.getValue(), "yd"}, 32 | {mile.getValue(), "mi"}, 33 | {tile.getValue(), "tile"}, 34 | }}, 35 | {typeid(degree), {{degree.getValue(), "deg"}, {radian.getValue(), "rad"}}}}; 36 | 37 | try { 38 | return shortNameMap.at(typeid(q)).at(q.getValue()); 39 | } catch (const std::out_of_range &e) { 40 | throw std::domain_error( 41 | "You have requested the shortname of an unknown unit somewhere (likely odometry strings). " 42 | "Shortname for provided unit is unspecified. You can override this function to add more " 43 | "names or manually specify the name instead."); 44 | } 45 | } 46 | } // namespace okapi 47 | -------------------------------------------------------------------------------- /include/okapi/impl/device/rotarysensor/integratedEncoder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/device/rotarysensor/continuousRotarySensor.hpp" 10 | #include "okapi/impl/device/motor/motor.hpp" 11 | 12 | namespace okapi { 13 | class IntegratedEncoder : public ContinuousRotarySensor { 14 | public: 15 | /** 16 | * Integrated motor encoder. Uses the encoder inside the V5 motor. 17 | * 18 | * @param imotor The motor to use the encoder from. 19 | */ 20 | IntegratedEncoder(const okapi::Motor &imotor); 21 | 22 | /** 23 | * Integrated motor encoder. Uses the encoder inside the V5 motor. 24 | * 25 | * @param iport The motor's port number in the range [1, 21]. 26 | * @param ireversed Whether the encoder is reversed. 27 | */ 28 | IntegratedEncoder(std::int8_t iport, bool ireversed = false); 29 | 30 | /** 31 | * Get the current sensor value. 32 | * 33 | * @return the current sensor value, or ``PROS_ERR`` on a failure. 34 | */ 35 | virtual double get() const override; 36 | 37 | /** 38 | * Reset the sensor to zero. 39 | * 40 | * @return `1` on success, `PROS_ERR` on fail 41 | */ 42 | virtual std::int32_t reset() override; 43 | 44 | /** 45 | * Get the sensor value for use in a control loop. This method might be automatically called in 46 | * another thread by the controller. 47 | * 48 | * @return the current sensor value, or ``PROS_ERR`` on a failure. 49 | */ 50 | virtual double controllerGet() override; 51 | 52 | protected: 53 | std::uint8_t port; 54 | std::int8_t reversed{1}; 55 | }; 56 | } // namespace okapi 57 | -------------------------------------------------------------------------------- /docs/tutorials/walkthrough/basicAutonomousMovement.md: -------------------------------------------------------------------------------- 1 | # Moving Autonomously 2 | 3 | Arguably the most fundamental task with regard to creating a good autonomous 4 | routine is ensuring consistent and accurate movement of the chassis. Robotic 5 | autonomous movement is an unsolved problem even among professional engineers, 6 | so it is obviously a difficult task. OkapiLib makes it easy to get reasonably 7 | accurate autonomous movements. 8 | 9 | The basis for this autonomous movement is the 10 | [ChassisController](@ref okapi::ChassisController) class. Take a look at its API 11 | for more detailed info on it. We'll use a 12 | [ChassisControllerIntegrated](@ref okapi::ChassisControllerIntegrated) for this 13 | tutorial; using the V5 motors' onboard PID makes setup a much quicker and easier 14 | process (No PID tuning needed!). 15 | 16 | Let's start by creating the 17 | [ChassisControllerIntegrated](@ref okapi::ChassisControllerIntegrated) with 18 | drive motors in ports 1 and 2: 19 | 20 | ```cpp 21 | std::shared_ptr chassis = 22 | ChassisControllerBuilder() 23 | .withMotors(1, -2) 24 | // Green gearset, 4 in wheel diam, 11.5 in wheel track 25 | .withDimensions(AbstractMotor::gearset::green, {{4_in, 11.5_in}, imev5GreenTPR}) 26 | .build(); 27 | ``` 28 | 29 | Now that we've created a ChassisController, let's start moving around. There are 30 | two fundamental movement types: 31 | [moveDistance](@ref okapi::ChassisController::moveDistance) and 32 | [turnAngle](@ref okapi::ChassisController::turnAngle), for moving 33 | forward/backward and turning on a point. 34 | 35 | ```cpp 36 | // Move 1 meter to the first goal 37 | chassis->moveDistance(1_m); 38 | // Turn 90 degrees to face second goal 39 | chassis->turnAngle(90_deg); 40 | // Drive 1 and a half feet toward second goal 41 | chassis->moveDistance(1.5_ft); 42 | ``` 43 | -------------------------------------------------------------------------------- /include/okapi/impl/device/rotarysensor/potentiometer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/device/rotarysensor/rotarySensor.hpp" 10 | 11 | namespace okapi { 12 | class Potentiometer : public RotarySensor { 13 | public: 14 | /** 15 | * A potentiometer in an ADI port. 16 | * 17 | * ```cpp 18 | * auto pot = Potentiometer('A'); 19 | * ``` 20 | * 21 | * @param iport The ADI port number (``[1, 8]``, ``[a, h]``, ``[A, H]``). 22 | */ 23 | Potentiometer(std::uint8_t iport); 24 | 25 | /** 26 | * A potentiometer in an ADI port. 27 | * 28 | * ```cpp 29 | * auto pot = Potentiometer({1, 'A'}); 30 | * ``` 31 | * 32 | * @param iports The ports the potentiometer is plugged in to in the order ``{smart port, 33 | * potentiometer port}``. The smart port is the smart port number (``[1, 21]``). The potentiometer 34 | * port is the ADI port number (``[1, 8]``, ``[a, h]``, ``[A, H]``). 35 | */ 36 | Potentiometer(std::pair iports); 37 | 38 | /** 39 | * Get the current sensor value. 40 | * 41 | * @return the current sensor value, or ``PROS_ERR`` on a failure. 42 | */ 43 | virtual double get() const override; 44 | 45 | /** 46 | * Get the sensor value for use in a control loop. This method might be automatically called in 47 | * another thread by the controller. 48 | * 49 | * @return the current sensor value, or ``PROS_ERR`` on a failure. 50 | */ 51 | virtual double controllerGet() override; 52 | 53 | protected: 54 | std::uint8_t smartPort; 55 | std::uint8_t port; 56 | }; 57 | } // namespace okapi 58 | -------------------------------------------------------------------------------- /src/api/control/async/asyncVelPidController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/control/async/asyncVelPidController.hpp" 7 | #include "okapi/api/util/mathUtil.hpp" 8 | 9 | namespace okapi { 10 | AsyncVelPIDController::AsyncVelPIDController( 11 | const std::shared_ptr> &iinput, 12 | const std::shared_ptr> &ioutput, 13 | const TimeUtil &itimeUtil, 14 | const double ikP, 15 | const double ikD, 16 | const double ikF, 17 | const double ikSF, 18 | std::unique_ptr ivelMath, 19 | const double iratio, 20 | std::unique_ptr iderivativeFilter, 21 | const std::shared_ptr &ilogger) 22 | : AsyncWrapper( 23 | iinput, 24 | ioutput, 25 | std::make_shared(ikP, 26 | ikD, 27 | ikF, 28 | ikSF, 29 | std::move(ivelMath), 30 | itimeUtil, 31 | std::move(iderivativeFilter)), 32 | itimeUtil.getRateSupplier(), 33 | iratio, 34 | ilogger), 35 | internalController(std::static_pointer_cast(controller)) { 36 | } 37 | 38 | void AsyncVelPIDController::setGains(const IterativeVelPIDController::Gains &igains) { 39 | internalController->setGains(igains); 40 | } 41 | 42 | IterativeVelPIDController::Gains AsyncVelPIDController::getGains() const { 43 | return internalController->getGains(); 44 | } 45 | } // namespace okapi 46 | -------------------------------------------------------------------------------- /include/okapi/api/control/util/settledUtil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/units/QTime.hpp" 9 | #include "okapi/api/util/abstractTimer.hpp" 10 | #include 11 | 12 | namespace okapi { 13 | class SettledUtil { 14 | public: 15 | /** 16 | * A utility class to determine if a control loop has settled based on error. A control loop is 17 | * settled if the error is within `iatTargetError` and `iatTargetDerivative` for `iatTargetTime`. 18 | * 19 | * @param iatTargetTimer A timer used to track `iatTargetTime`. 20 | * @param iatTargetError The minimum error to be considered settled. 21 | * @param iatTargetDerivative The minimum error derivative to be considered settled. 22 | * @param iatTargetTime The minimum time within atTargetError to be considered settled. 23 | */ 24 | explicit SettledUtil(std::unique_ptr iatTargetTimer, 25 | double iatTargetError = 50, 26 | double iatTargetDerivative = 5, 27 | QTime iatTargetTime = 250_ms); 28 | 29 | virtual ~SettledUtil(); 30 | 31 | /** 32 | * Returns whether the controller is settled. 33 | * 34 | * @param ierror The current error. 35 | * @return Whether the controller is settled. 36 | */ 37 | virtual bool isSettled(double ierror); 38 | 39 | /** 40 | * Resets the "at target" timer and clears the previous error. 41 | */ 42 | virtual void reset(); 43 | 44 | protected: 45 | double atTargetError = 50; 46 | double atTargetDerivative = 5; 47 | QTime atTargetTime = 250_ms; 48 | std::unique_ptr atTargetTimer; 49 | double lastError = 0; 50 | }; 51 | } // namespace okapi 52 | -------------------------------------------------------------------------------- /include/test/testRunner.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api.hpp" 10 | #include "test/FakeIt/single_header/standalone/fakeit.hpp" 11 | #include "test/snowhouse/snowhouse.h" 12 | #include 13 | #include 14 | 15 | #define TEST_PRINT_RED "\x1B[31m" 16 | #define TEST_PRINT_GRN "\x1B[32m" 17 | #define TEST_PRINT_YEL "\x1B[33m" 18 | #define TEST_PRINT_BLU "\x1B[34m" 19 | #define TEST_PRINT_MAG "\x1B[35m" 20 | #define TEST_PRINT_CYN "\x1B[36m" 21 | #define TEST_PRINT_WHT "\x1B[37m" 22 | #define TEST_PRINT_RESET "\x1B[0m" 23 | 24 | #define TEST_BODY(FUNCTION, ...) [&]() { FUNCTION(__VA_ARGS__); } 25 | 26 | namespace okapi { 27 | static const int MOTOR_1_PORT = 18; 28 | static const int MOTOR_2_PORT = 19; 29 | static const auto MOTOR_GEARSET = AbstractMotor::gearset::green; 30 | 31 | /** 32 | * Setup hardware before a test. 33 | */ 34 | void resetHardware(); 35 | 36 | /** 37 | * Print the input string with an underline made from hyphens ("-"). 38 | * 39 | * @param istring string to print 40 | */ 41 | void test_printf(const std::string &istring); 42 | 43 | /** 44 | * Run a test. The lambda can have any body, or it can be a single function call. In that case, use 45 | * TEST_BODY for a more succinct way of writing the test. 46 | * 47 | * @param iname test name (describe what it does) 48 | * @param ifunc test body 49 | */ 50 | void test(const std::string &iname, std::function ifunc); 51 | 52 | /** 53 | * Print out a test report detailing how long the tests took to run; how many tests passed; how many 54 | * tests failed; and if any failed, which ones. 55 | */ 56 | void test_print_report(); 57 | } // namespace okapi 58 | -------------------------------------------------------------------------------- /include/okapi/api/chassis/model/threeEncoderSkidSteerModel.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/chassis/model/skidSteerModel.hpp" 9 | 10 | namespace okapi { 11 | class ThreeEncoderSkidSteerModel : public SkidSteerModel { 12 | public: 13 | /** 14 | * Model for a skid steer drive (wheels parallel with robot's direction of motion). When all 15 | * motors are powered +127, the robot should move forward in a straight line. 16 | * 17 | * @param ileftSideMotor left side motor 18 | * @param irightSideMotor right side motor 19 | * @param ileftEnc left side encoder 20 | * @param imiddleEnc middle encoder (mounted perpendicular to the left and right side encoders) 21 | * @param irightEnc right side encoder 22 | */ 23 | ThreeEncoderSkidSteerModel(std::shared_ptr ileftSideMotor, 24 | std::shared_ptr irightSideMotor, 25 | std::shared_ptr ileftEnc, 26 | std::shared_ptr irightEnc, 27 | std::shared_ptr imiddleEnc, 28 | double imaxVelocity, 29 | double imaxVoltage); 30 | 31 | /** 32 | * Read the sensors. 33 | * 34 | * @return sensor readings in the format {left, right, middle} 35 | */ 36 | std::valarray getSensorVals() const override; 37 | 38 | /** 39 | * Reset the sensors to their zero point. 40 | */ 41 | void resetSensors() override; 42 | 43 | protected: 44 | std::shared_ptr middleSensor; 45 | }; 46 | } // namespace okapi 47 | -------------------------------------------------------------------------------- /docs/tutorials/index.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | 3 | Learning a new coding platform is often a rather daunting task. For this 4 | reason we prepared some small tutorials with examples on how to interact 5 | with core components of OkapiLib on your VEX V5 system. 6 | 7 | The OkapiLib tutorials are split into two sections: 8 | 9 | ## Walkthrough Tutorials 10 | 11 | If you want to write full sample programs to learn to use OkapiLib through 12 | practical applications, or teach students to use OkapiLib in a classroom 13 | environment, the **Walkthrough Tutorials** are a great resource to go step by 14 | step through the process of writing an OkapiLib program. 15 | 16 | - [Getting Started](docs/tutorials/walkthrough/gettingStarted.md) 17 | - [Clawbot](docs/tutorials/walkthrough/clawbot.md) 18 | - [Basic Autonomous Movement](docs/tutorials/walkthrough/basicAutonomousMovement.md) 19 | - [Async Autonomous Movement](docs/tutorials/walkthrough/asyncAutonomousMovement.md) 20 | - [Lift Movement](docs/tutorials/walkthrough/liftMovement.md) 21 | - [Chassis Controller Builder](docs/tutorials/walkthrough/chassisControllerBuilder.md) 22 | - [Odometry](docs/tutorials/walkthrough/odometry.md) 23 | 24 | ## Topical Tutorials 25 | 26 | If you want more information about using a particular part of the OkapiLib API 27 | after reading through the API reference, the **Topical Tutorials** offer longer 28 | examples and more detail about using the API. 29 | 30 | - [Where to use builders](docs/tutorials/concepts/builders-and-tasks.md) 31 | - [Logging](docs/tutorials/concepts/logging.md) 32 | - [2D Motion Profiling](docs/tutorials/concepts/twodmotionprofiling.md) 33 | - [Controller Inputs and Outputs](docs/tutorials/concepts/controller-io.md) 34 | - [Filtering](docs/tutorials/concepts/filtering.md) 35 | - [Iterative and Async Controllers](docs/tutorials/concepts/iterative-async-controllers.md) 36 | - [SettledUtil](docs/tutorials/concepts/settled-util.md) 37 | -------------------------------------------------------------------------------- /include/okapi/api/odometry/threeEncoderOdometry.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/chassis/model/threeEncoderSkidSteerModel.hpp" 9 | #include "okapi/api/odometry/twoEncoderOdometry.hpp" 10 | #include "okapi/api/util/timeUtil.hpp" 11 | #include 12 | 13 | namespace okapi { 14 | class ThreeEncoderOdometry : public TwoEncoderOdometry { 15 | public: 16 | /** 17 | * Odometry. Tracks the movement of the robot and estimates its position in coordinates 18 | * relative to the start (assumed to be (0, 0)). 19 | * 20 | * @param itimeUtil The TimeUtil. 21 | * @param imodel The chassis model for reading sensors. 22 | * @param ichassisScales See ChassisScales docs (the middle wheel scale is the third member) 23 | * @param iwheelVelDelta The maximum delta between wheel velocities to consider the robot as 24 | * driving straight. 25 | * @param ilogger The logger this instance will log to. 26 | */ 27 | ThreeEncoderOdometry(const TimeUtil &itimeUtil, 28 | const std::shared_ptr &imodel, 29 | const ChassisScales &ichassisScales, 30 | const std::shared_ptr &ilogger = Logger::getDefaultLogger()); 31 | 32 | protected: 33 | /** 34 | * Does the math, side-effect free, for one odom step. 35 | * 36 | * @param itickDiff The tick difference from the previous step to this step. 37 | * @param ideltaT The time difference from the previous step to this step. 38 | * @return The newly computed OdomState. 39 | */ 40 | OdomState odomMathStep(const std::valarray &itickDiff, 41 | const QTime &ideltaT) override; 42 | }; 43 | } // namespace okapi 44 | -------------------------------------------------------------------------------- /docs/tutorials/concepts/controller-io.md: -------------------------------------------------------------------------------- 1 | # Controller Inputs and Outputs 2 | 3 | Fundamentally, a feedback control system needs both an input and an 4 | output. In OkapiLib, Iterative Controllers do not require Controller 5 | Inputs and Outputs to be passed into the constructor like Async 6 | Controllers do, but they are still necessary for making use of the 7 | controller. This tutorial will explain more about what Controller Inputs 8 | and Outputs are, and what classes can be used for such purposes. 9 | 10 | ## General Usage 11 | 12 | Using Controller Inputs and Outputs is quite simple, they each only have 13 | one function. Controller Inputs will return their current state with the 14 | [controllerGet](@ref okapi::ControllerInput::controllerGet) function, and 15 | Controller Outputs can be given a desired output with 16 | [controllerSet](@ref okapi::ControllerOutput::controllerSet). The exact 17 | implementation of these functions varies depending on what device you are using, 18 | but all Controller Outputs will accept a range of `[-1, 1]` as the input to 19 | [controllerSet](@ref okapi::ControllerOutput::controllerSet) for the sake of 20 | standardization across multiple different devices and configurations (i.e. 21 | motors with different gearings). 22 | 23 | ## What Classes are of Each Type? 24 | 25 | **Controller Inputs:** 26 | 27 | - [ADIButton](@ref okapi::ADIButton) 28 | - [ADIEncoder](@ref okapi::ADIEncoder) 29 | - [ADIGyro](@ref okapi::ADIGyro) 30 | - [ADIUltrasonic](@ref okapi::ADIUltrasonic) 31 | - [ControllerButton](@ref okapi::ControllerButton) 32 | - [FilteredControllerInput](@ref okapi::FilteredControllerInput) 33 | - [IntegratedEncoder](@ref okapi::IntegratedEncoder) 34 | - [OffsetableControllerInput](@ref okapi::OffsetableControllerInput) 35 | - [Potentiometer](@ref okapi::Potentiometer) 36 | 37 | **Controller Outputs:** 38 | 39 | - [ADIMotor](@ref okapi::ADIMotor) 40 | - [Motor](@ref okapi::Motor) 41 | - [MotorGroup](@ref okapi::MotorGroup) 42 | -------------------------------------------------------------------------------- /docs/tutorials/walkthrough/gettingStarted.md: -------------------------------------------------------------------------------- 1 | # Getting Started with OkapiLib 2 | 3 | ## Introduction 4 | 5 | OkapiLib is installed in all new PROS projects by default. If you are unsure if 6 | OkapiLib is installed, you can check the output of `prosv5 conduct info-project`. 7 | Additionally, OkapiLib's header files reside in ` include/okapi`. Once you know 8 | that OkapiLib is installed, you can start using it by uncommenting OkapiLib's 9 | API header include statement in the file `include/main.h` : 10 | 11 | ``` cpp 12 | #include "api.h" 13 | 14 | /** 15 | * You should add more #includes here 16 | */ 17 | #include "okapi/api.hpp" // <-- UNCOMMENT THIS LINE 18 | //#include "pros/api_legacy.h" 19 | ``` 20 | 21 | All okapi methods are located in the okapi namespace. 22 | To avoid typing `okapi::` in front of every command, uncomment the `using` statement in the file `include/main.h` : 23 | 24 | ``` cpp 25 | /** 26 | * If you find doing pros::Motor() to be tedious and you'd prefer just to do 27 | * Motor, you can use the namespace with the following commented out line. 28 | * 29 | * IMPORTANT: Only the okapi or pros namespace may be used, not both 30 | * concurrently! The okapi namespace will export all symbols inside the pros 31 | * namespace. 32 | */ 33 | // using namespace pros; 34 | // using namespace pros::literals; 35 | using namespace okapi; // <-- UNCOMMENT THIS LINE 36 | ``` 37 | 38 | If you don't want to use the okapi namespace, but you want to use unit literals such as `1_in`, add the following line to the file `include/main.h` : 39 | 40 | ``` cpp 41 | using namespace okapi::literals; 42 | ``` 43 | 44 | ## Troubleshooting 45 | 46 | If OkapiLib is not getting downloaded from GitHub correctly during 47 | installation, you can 48 | [download it manually here](https://github.com/OkapiLib/OkapiLib/releases) and 49 | then install it by running `prosv5 conduct fetch path/to/okapilib.zip`. Once 50 | OkapiLib is installed, try creating a PROS project again. 51 | -------------------------------------------------------------------------------- /include/okapi/api/odometry/odometry.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/chassis/controller/chassisScales.hpp" 9 | #include "okapi/api/chassis/model/readOnlyChassisModel.hpp" 10 | #include "okapi/api/odometry/odomState.hpp" 11 | #include "okapi/api/odometry/stateMode.hpp" 12 | 13 | namespace okapi { 14 | class Odometry { 15 | public: 16 | /** 17 | * Odometry. Tracks the movement of the robot and estimates its position in coordinates 18 | * relative to the start (assumed to be (0, 0, 0)). 19 | */ 20 | explicit Odometry() = default; 21 | 22 | virtual ~Odometry() = default; 23 | 24 | /** 25 | * Sets the drive and turn scales. 26 | */ 27 | virtual void setScales(const ChassisScales &ichassisScales) = 0; 28 | 29 | /** 30 | * Do one odometry step. 31 | */ 32 | virtual void step() = 0; 33 | 34 | /** 35 | * Returns the current state. 36 | * 37 | * @param imode The mode to return the state in. 38 | * @return The current state in the given format. 39 | */ 40 | virtual OdomState getState(const StateMode &imode = StateMode::FRAME_TRANSFORMATION) const = 0; 41 | 42 | /** 43 | * Sets a new state to be the current state. 44 | * 45 | * @param istate The new state in the given format. 46 | * @param imode The mode to treat the input state as. 47 | */ 48 | virtual void setState(const OdomState &istate, 49 | const StateMode &imode = StateMode::FRAME_TRANSFORMATION) = 0; 50 | 51 | /** 52 | * @return The internal ChassisModel. 53 | */ 54 | virtual std::shared_ptr getModel() = 0; 55 | 56 | /** 57 | * @return The internal ChassisScales. 58 | */ 59 | virtual ChassisScales getScales() = 0; 60 | }; 61 | } // namespace okapi 62 | -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file main.h 3 | * 4 | * Contains common definitions and header files used throughout your PROS 5 | * project. 6 | * 7 | * Copyright (c) 2017-2018, Purdue University ACM SIGBots. 8 | * All rights reserved. 9 | * 10 | * This Source Code Form is subject to the terms of the Mozilla Public 11 | * License, v. 2.0. If a copy of the MPL was not distributed with this 12 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 13 | */ 14 | 15 | #ifndef _PROS_MAIN_H_ 16 | #define _PROS_MAIN_H_ 17 | 18 | /** 19 | * If defined, some commonly used enums will have preprocessor macros which give 20 | * a shorter, 21 | * more convenient naming pattern. If this isn't desired, simply comment the 22 | * following line out 23 | * 24 | * For instance, E_CONTROLLER_MASTER has a shorter name: CONTROLLER_MASTER. 25 | * E_CONTROLLER_MASTER 26 | * is pedantically correct within the PROS styleguide, but not convienent for 27 | * most student 28 | * programmers. 29 | */ 30 | #define PROS_USE_SIMPLE_NAMES 31 | 32 | /** 33 | * If defined, C++ literals will be available for use. 34 | * 35 | * For instance, you can do `4_m = 50` to set motor 4's target velocity to 50 36 | */ 37 | #define PROS_USE_LITERALS 38 | 39 | #include "api.h" 40 | 41 | /** 42 | * If you find doing pros::Motor() to be tedious and you'd prefer just to do 43 | * Motor, 44 | * you can use the namespace with the following commented out line. 45 | * 46 | * IMPORTANT: Only the okapi or pros namespace may be used, not both 47 | * concurrently! 48 | * The okapi namespace will export all symbols inside the pros 49 | * namespace. 50 | */ 51 | // using namespace pros; 52 | // using namespace okapi; 53 | 54 | /** 55 | * You should add more #includes here 56 | */ 57 | #include "okapi/api.hpp" 58 | //#include "pros/api_legacy.h" 59 | 60 | #ifdef __cplusplus 61 | /** 62 | * You can add C++-only headers here 63 | */ 64 | //#include 65 | #endif 66 | 67 | #endif // _PROS_MAIN_H_ 68 | -------------------------------------------------------------------------------- /include/okapi/api/units/QTime.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | 14 | namespace okapi { 15 | QUANTITY_TYPE(0, 0, 1, 0, QTime) 16 | 17 | constexpr QTime second(1.0); // SI base unit 18 | constexpr QTime millisecond = second / 1000; 19 | constexpr QTime minute = 60 * second; 20 | constexpr QTime hour = 60 * minute; 21 | constexpr QTime day = 24 * hour; 22 | 23 | inline namespace literals { 24 | constexpr QTime operator"" _s(long double x) { 25 | return QTime(x); 26 | } 27 | constexpr QTime operator"" _ms(long double x) { 28 | return static_cast(x) * millisecond; 29 | } 30 | constexpr QTime operator"" _min(long double x) { 31 | return static_cast(x) * minute; 32 | } 33 | constexpr QTime operator"" _h(long double x) { 34 | return static_cast(x) * hour; 35 | } 36 | constexpr QTime operator"" _day(long double x) { 37 | return static_cast(x) * day; 38 | } 39 | constexpr QTime operator"" _s(unsigned long long int x) { 40 | return QTime(static_cast(x)); 41 | } 42 | constexpr QTime operator"" _ms(unsigned long long int x) { 43 | return static_cast(x) * millisecond; 44 | } 45 | constexpr QTime operator"" _min(unsigned long long int x) { 46 | return static_cast(x) * minute; 47 | } 48 | constexpr QTime operator"" _h(unsigned long long int x) { 49 | return static_cast(x) * hour; 50 | } 51 | constexpr QTime operator"" _day(unsigned long long int x) { 52 | return static_cast(x) * day; 53 | } 54 | } // namespace literals 55 | } // namespace okapi 56 | -------------------------------------------------------------------------------- /include/pros/llemu.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROS_LLEMU_H_ 2 | #define _PROS_LLEMU_H_ 3 | 4 | // TODO:? Should there be weak symbols for the C api in here as well? 5 | 6 | #include "stdint.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | namespace pros { 11 | namespace c { 12 | #endif//__cplusplus 13 | 14 | /** 15 | * Displays a formatted string on the emulated three-button LCD screen. 16 | * 17 | * This function uses the following values of errno when an error state is 18 | * reached: 19 | * ENXIO - The LCD has not been initialized. Call lcd_initialize() first. 20 | * EINVAL - The line number specified is not in the range [0-7] 21 | * 22 | * \param line 23 | * The line on which to display the text [0-7] 24 | * \param fmt 25 | * Format string 26 | * \param ... 27 | * Optional list of arguments for the format string 28 | * 29 | * \return True if the operation was successful, or false otherwise, setting 30 | * errno values as specified above. 31 | */ 32 | bool __attribute__((weak)) lcd_print(int16_t line, const char* fmt, ...) { 33 | return false; 34 | } 35 | 36 | #ifdef __cplusplus 37 | } // namespace c 38 | } // namespace pros 39 | } // extern "C" 40 | #endif//__cplusplus 41 | 42 | 43 | /******************************************************************************/ 44 | /** LLEMU Conditional Include **/ 45 | /** **/ 46 | /** When the libvgl versions of llemu.h is present, common.mk will **/ 47 | /** define a macro which lets this file know that liblvgl's llemu.h is **/ 48 | /** present. If it is, we conditionally include it so that it gets **/ 49 | /** included into api.h. **/ 50 | /******************************************************************************/ 51 | #ifdef _PROS_INCLUDE_LIBLVGL_LLEMU_H 52 | #include "liblvgl/llemu.h" 53 | #endif 54 | 55 | #endif // _PROS_LLEMU_H_ 56 | -------------------------------------------------------------------------------- /include/okapi/impl/control/util/pidTunerFactory.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/util/pidTuner.hpp" 9 | #include 10 | 11 | namespace okapi { 12 | class PIDTunerFactory { 13 | public: 14 | static PIDTuner create(const std::shared_ptr> &iinput, 15 | const std::shared_ptr> &ioutput, 16 | QTime itimeout, 17 | std::int32_t igoal, 18 | double ikPMin, 19 | double ikPMax, 20 | double ikIMin, 21 | double ikIMax, 22 | double ikDMin, 23 | double ikDMax, 24 | std::int32_t inumIterations = 5, 25 | std::int32_t inumParticles = 16, 26 | double ikSettle = 1, 27 | double ikITAE = 2, 28 | const std::shared_ptr &ilogger = Logger::getDefaultLogger()); 29 | 30 | static std::unique_ptr 31 | createPtr(const std::shared_ptr> &iinput, 32 | const std::shared_ptr> &ioutput, 33 | QTime itimeout, 34 | std::int32_t igoal, 35 | double ikPMin, 36 | double ikPMax, 37 | double ikIMin, 38 | double ikIMax, 39 | double ikDMin, 40 | double ikDMax, 41 | std::int32_t inumIterations = 5, 42 | std::int32_t inumParticles = 16, 43 | double ikSettle = 1, 44 | double ikITAE = 2, 45 | const std::shared_ptr &ilogger = Logger::getDefaultLogger()); 46 | }; 47 | } // namespace okapi 48 | -------------------------------------------------------------------------------- /include/okapi/api/chassis/model/threeEncoderXDriveModel.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/chassis/model/xDriveModel.hpp" 9 | 10 | namespace okapi { 11 | class ThreeEncoderXDriveModel : public XDriveModel { 12 | public: 13 | /** 14 | * Model for an x drive (wheels at 45 deg from a skid steer drive). When all motors are powered 15 | * +100%, the robot should move forward in a straight line. 16 | * 17 | * @param itopLeftMotor The top left motor. 18 | * @param itopRightMotor The top right motor. 19 | * @param ibottomRightMotor The bottom right motor. 20 | * @param ibottomLeftMotor The bottom left motor. 21 | * @param ileftEnc The left side encoder. 22 | * @param irightEnc The right side encoder. 23 | * @param imiddleEnc The middle encoder. 24 | */ 25 | ThreeEncoderXDriveModel(std::shared_ptr itopLeftMotor, 26 | std::shared_ptr itopRightMotor, 27 | std::shared_ptr ibottomRightMotor, 28 | std::shared_ptr ibottomLeftMotor, 29 | std::shared_ptr ileftEnc, 30 | std::shared_ptr irightEnc, 31 | std::shared_ptr imiddleEnc, 32 | double imaxVelocity, 33 | double imaxVoltage); 34 | 35 | /** 36 | * Read the sensors. 37 | * 38 | * @return sensor readings in the format {left, right, middle} 39 | */ 40 | std::valarray getSensorVals() const override; 41 | 42 | /** 43 | * Reset the sensors to their zero point. 44 | */ 45 | void resetSensors() override; 46 | 47 | protected: 48 | std::shared_ptr middleSensor; 49 | }; 50 | } // namespace okapi 51 | -------------------------------------------------------------------------------- /include/okapi/impl/device/rotarysensor/rotationSensor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/device/rotarysensor/continuousRotarySensor.hpp" 10 | 11 | namespace okapi { 12 | class RotationSensor : public ContinuousRotarySensor { 13 | public: 14 | /** 15 | * A rotation sensor in a V5 port. 16 | * 17 | * ```cpp 18 | * auto r = RotationSensor(1); 19 | * auto reversedR = RotationSensor(1, true); 20 | * ``` 21 | * 22 | * @param iport The V5 port the device uses. 23 | * @param ireversed Whether the sensor is reversed. This will set the reversed state in the 24 | * kernel. 25 | */ 26 | RotationSensor(std::uint8_t iport, bool ireversed = false); 27 | 28 | /** 29 | * Get the current rotation in degrees. 30 | * 31 | * @return The current rotation in degrees or ``PROS_ERR_F`` if the operation failed, setting 32 | * ``errno``. 33 | */ 34 | double get() const override; 35 | 36 | /** 37 | * Reset the sensor to zero. 38 | * 39 | * @return ``1`` if the operation was successful or ``PROS_ERR`` if the operation failed, setting 40 | * ``errno``. 41 | */ 42 | std::int32_t reset() override; 43 | 44 | /** 45 | * Get the sensor value for use in a control loop. This method might be automatically called in 46 | * another thread by the controller. 47 | * 48 | * @return The same as [get](@ref okapi::RotationSensor::get). 49 | */ 50 | double controllerGet() override; 51 | 52 | /** 53 | * Get the current rotational velocity estimate in degrees per second. 54 | * 55 | * @return The current rotational velocity estimate in degrees per second or ``PROS_ERR_F`` if the 56 | * operation failed, setting ``errno``. 57 | */ 58 | double getVelocity() const; 59 | 60 | protected: 61 | std::uint8_t port; 62 | std::int8_t reversed{1}; 63 | }; 64 | } // namespace okapi 65 | -------------------------------------------------------------------------------- /src/api/util/abstractTimer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/util/abstractTimer.hpp" 7 | 8 | namespace okapi { 9 | AbstractTimer::AbstractTimer(const QTime ifirstCalled) 10 | : firstCalled(ifirstCalled), lastCalled(firstCalled), mark(firstCalled) { 11 | } 12 | 13 | AbstractTimer::~AbstractTimer() = default; 14 | 15 | QTime AbstractTimer::getDt() { 16 | const QTime currTime = millis(); 17 | const QTime dt = currTime - lastCalled; 18 | lastCalled = currTime; 19 | return dt; 20 | } 21 | 22 | QTime AbstractTimer::readDt() const { 23 | return millis() - lastCalled; 24 | } 25 | 26 | QTime AbstractTimer::getStartingTime() const { 27 | return firstCalled; 28 | } 29 | 30 | QTime AbstractTimer::getDtFromStart() const { 31 | return millis() - firstCalled; 32 | } 33 | 34 | void AbstractTimer::placeMark() { 35 | mark = millis(); 36 | } 37 | 38 | QTime AbstractTimer::clearMark() { 39 | const QTime old = mark; 40 | mark = 0_ms; 41 | return old; 42 | } 43 | 44 | void AbstractTimer::placeHardMark() { 45 | if (hardMark == 0_ms) 46 | hardMark = millis(); 47 | } 48 | 49 | QTime AbstractTimer::clearHardMark() { 50 | const QTime old = hardMark; 51 | hardMark = 0_ms; 52 | return old; 53 | } 54 | 55 | QTime AbstractTimer::getDtFromMark() const { 56 | return mark == 0_ms ? 0_ms : millis() - mark; 57 | } 58 | 59 | QTime AbstractTimer::getDtFromHardMark() const { 60 | return hardMark == 0_ms ? 0_ms : millis() - hardMark; 61 | } 62 | 63 | bool AbstractTimer::repeat(const QTime time) { 64 | if (repeatMark == 0_ms) { 65 | repeatMark = millis(); 66 | return false; 67 | } 68 | 69 | if (millis() - repeatMark >= time) { 70 | repeatMark = 0_ms; 71 | return true; 72 | } 73 | 74 | return false; 75 | } 76 | 77 | bool AbstractTimer::repeat(const QFrequency frequency) { 78 | return repeat(QTime(1 / frequency.convert(Hz))); 79 | } 80 | } // namespace okapi 81 | -------------------------------------------------------------------------------- /src/api/filter/velMath.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/filter/velMath.hpp" 7 | #include "okapi/api/filter/averageFilter.hpp" 8 | #include "okapi/api/filter/medianFilter.hpp" 9 | #include "okapi/api/util/mathUtil.hpp" 10 | #include 11 | 12 | namespace okapi { 13 | VelMath::VelMath(const double iticksPerRev, 14 | std::unique_ptr ifilter, 15 | QTime isampleTime, 16 | std::unique_ptr iloopDtTimer, 17 | std::shared_ptr ilogger) 18 | : logger(std::move(ilogger)), 19 | ticksPerRev(iticksPerRev), 20 | sampleTime(isampleTime), 21 | loopDtTimer(std::move(iloopDtTimer)), 22 | filter(std::move(ifilter)) { 23 | if (iticksPerRev == 0) { 24 | std::string msg( 25 | "VelMath: The ticks per revolution cannot be zero! Check if you are using integer division."); 26 | LOG_ERROR(msg); 27 | throw std::invalid_argument(msg); 28 | } 29 | } 30 | 31 | VelMath::~VelMath() = default; 32 | 33 | QAngularSpeed VelMath::step(const double inewPos) { 34 | if (loopDtTimer->readDt() >= sampleTime) { 35 | const QTime dt = loopDtTimer->getDt(); 36 | 37 | vel = filter->filter(((inewPos - lastPos) * (60 / ticksPerRev)) / dt.convert(second)) * rpm; 38 | accel = (vel - lastVel) / dt; 39 | 40 | lastVel = vel; 41 | lastPos = inewPos; 42 | } 43 | 44 | return vel; 45 | } 46 | 47 | void VelMath::setTicksPerRev(const double iTPR) { 48 | if (iTPR == 0) { 49 | std::string msg( 50 | "VelMath: The ticks per revolution cannot be zero! Check if you are using integer division."); 51 | LOG_ERROR(msg); 52 | throw std::invalid_argument(msg); 53 | } 54 | 55 | ticksPerRev = iTPR; 56 | } 57 | 58 | QAngularSpeed VelMath::getVelocity() const { 59 | return vel; 60 | } 61 | 62 | QAngularAcceleration VelMath::getAccel() const { 63 | return accel; 64 | } 65 | } // namespace okapi 66 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: okapilib/ubuntu-base:18.04 10 | steps: 11 | - name: Cancel Previous Runs 12 | uses: styfle/cancel-workflow-action@0.6.0 13 | with: 14 | access_token: ${{ github.token }} 15 | 16 | - uses: actions/checkout@v1 17 | 18 | - name: Update Submodules 19 | run: git submodule update --init --recursive 20 | 21 | - name: Make 22 | run: make 23 | 24 | - name: Make Template 25 | run: | 26 | make template 27 | mkdir template 28 | cp okapilib@*.zip template/ 29 | 30 | - name: Verify Template 31 | run: | 32 | prosv5 c f okapilib@*.zip 33 | prosv5 c n verify-template 34 | 35 | - name: Archive production artifacts 36 | uses: actions/upload-artifact@v1 37 | with: 38 | name: template 39 | path: template 40 | 41 | - name: Build and Test 42 | env: 43 | CC: gcc-7 44 | CXX: g++-7 45 | run: | 46 | mkdir -p cmake-build-debug && cd cmake-build-debug 47 | cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" .. 48 | cmake --build . --target OkapiLibV5 -- -j 2 49 | 50 | - name: Test 51 | working-directory: cmake-build-debug 52 | run: ./OkapiLibV5 53 | 54 | - name: Valgrind 55 | working-directory: cmake-build-debug 56 | run: valgrind --tool=memcheck --leak-check=full --leak-resolution=med --show-leak-kinds=all --undef-value-errors=yes --track-origins=yes --error-exitcode=1 --show-reachable=no ./OkapiLibV5 57 | 58 | - name: Collect Coverage 59 | run: | 60 | lcov --directory . --capture --output-file coverage.info 61 | lcov --remove coverage.info '/usr/*' --output-file coverage.info 62 | lcov --list coverage.info 63 | 64 | - name: Upload Coverage 65 | uses: codecov/codecov-action@v1 66 | with: 67 | fail_ci_if_error: true 68 | 69 | - name: Build Docs 70 | run: ./run_doxygen.sh 71 | -------------------------------------------------------------------------------- /src/api/chassis/model/threeEncoderXDriveModel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/chassis/model/threeEncoderXDriveModel.hpp" 7 | 8 | namespace okapi { 9 | ThreeEncoderXDriveModel::ThreeEncoderXDriveModel(std::shared_ptr itopLeftMotor, 10 | std::shared_ptr itopRightMotor, 11 | std::shared_ptr ibottomRightMotor, 12 | std::shared_ptr ibottomLeftMotor, 13 | std::shared_ptr ileftEnc, 14 | std::shared_ptr irightEnc, 15 | std::shared_ptr imiddleEnc, 16 | const double imaxVelocity, 17 | const double imaxVoltage) 18 | : XDriveModel(std::move(itopLeftMotor), 19 | std::move(itopRightMotor), 20 | std::move(ibottomRightMotor), 21 | std::move(ibottomLeftMotor), 22 | std::move(ileftEnc), 23 | std::move(irightEnc), 24 | imaxVelocity, 25 | imaxVoltage), 26 | middleSensor(std::move(imiddleEnc)) { 27 | } 28 | 29 | std::valarray ThreeEncoderXDriveModel::getSensorVals() const { 30 | // Return the middle sensor last so this is compatible with XDriveModel::getSensorVals() 31 | return std::valarray{static_cast(leftSensor->get()), 32 | static_cast(rightSensor->get()), 33 | static_cast(middleSensor->get())}; 34 | } 35 | 36 | void ThreeEncoderXDriveModel::resetSensors() { 37 | XDriveModel::resetSensors(); 38 | middleSensor->reset(); 39 | } 40 | } // namespace okapi 41 | -------------------------------------------------------------------------------- /include/okapi/api/units/QMass.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post 3 | * here: 4 | * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | 12 | #include "okapi/api/units/RQuantity.hpp" 13 | 14 | namespace okapi { 15 | QUANTITY_TYPE(1, 0, 0, 0, QMass) 16 | 17 | constexpr QMass kg(1.0); // SI base unit 18 | constexpr QMass gramme = 0.001 * kg; 19 | constexpr QMass tonne = 1000 * kg; 20 | constexpr QMass ounce = 0.028349523125 * kg; 21 | constexpr QMass pound = 16 * ounce; 22 | constexpr QMass stone = 14 * pound; 23 | 24 | inline namespace literals { 25 | constexpr QMass operator"" _kg(long double x) { 26 | return QMass(x); 27 | } 28 | constexpr QMass operator"" _g(long double x) { 29 | return static_cast(x) * gramme; 30 | } 31 | constexpr QMass operator"" _t(long double x) { 32 | return static_cast(x) * tonne; 33 | } 34 | constexpr QMass operator"" _oz(long double x) { 35 | return static_cast(x) * ounce; 36 | } 37 | constexpr QMass operator"" _lb(long double x) { 38 | return static_cast(x) * pound; 39 | } 40 | constexpr QMass operator"" _st(long double x) { 41 | return static_cast(x) * stone; 42 | } 43 | constexpr QMass operator"" _kg(unsigned long long int x) { 44 | return QMass(static_cast(x)); 45 | } 46 | constexpr QMass operator"" _g(unsigned long long int x) { 47 | return static_cast(x) * gramme; 48 | } 49 | constexpr QMass operator"" _t(unsigned long long int x) { 50 | return static_cast(x) * tonne; 51 | } 52 | constexpr QMass operator"" _oz(unsigned long long int x) { 53 | return static_cast(x) * ounce; 54 | } 55 | constexpr QMass operator"" _lb(unsigned long long int x) { 56 | return static_cast(x) * pound; 57 | } 58 | constexpr QMass operator"" _st(unsigned long long int x) { 59 | return static_cast(x) * stone; 60 | } 61 | } // namespace literals 62 | } // namespace okapi 63 | -------------------------------------------------------------------------------- /src/impl/device/controllerUtil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/controllerUtil.hpp" 7 | 8 | namespace okapi { 9 | pros::controller_id_e_t ControllerUtil::idToProsEnum(ControllerId in) { 10 | switch (in) { 11 | case ControllerId::master: 12 | return pros::E_CONTROLLER_MASTER; 13 | case ControllerId::partner: 14 | default: 15 | return pros::E_CONTROLLER_PARTNER; 16 | } 17 | } 18 | 19 | pros::controller_analog_e_t ControllerUtil::analogToProsEnum(ControllerAnalog in) { 20 | switch (in) { 21 | case ControllerAnalog::leftX: 22 | return pros::E_CONTROLLER_ANALOG_LEFT_X; 23 | case ControllerAnalog::leftY: 24 | return pros::E_CONTROLLER_ANALOG_LEFT_Y; 25 | case ControllerAnalog::rightX: 26 | return pros::E_CONTROLLER_ANALOG_RIGHT_X; 27 | case ControllerAnalog::rightY: 28 | default: 29 | return pros::E_CONTROLLER_ANALOG_RIGHT_Y; 30 | } 31 | } 32 | 33 | pros::controller_digital_e_t ControllerUtil::digitalToProsEnum(ControllerDigital in) { 34 | switch (in) { 35 | case ControllerDigital::L1: 36 | return pros::E_CONTROLLER_DIGITAL_L1; 37 | case ControllerDigital::L2: 38 | return pros::E_CONTROLLER_DIGITAL_L2; 39 | case ControllerDigital::R1: 40 | return pros::E_CONTROLLER_DIGITAL_R1; 41 | case ControllerDigital::R2: 42 | return pros::E_CONTROLLER_DIGITAL_R2; 43 | case ControllerDigital::up: 44 | return pros::E_CONTROLLER_DIGITAL_UP; 45 | case ControllerDigital::down: 46 | return pros::E_CONTROLLER_DIGITAL_DOWN; 47 | case ControllerDigital::left: 48 | return pros::E_CONTROLLER_DIGITAL_LEFT; 49 | case ControllerDigital::right: 50 | return pros::E_CONTROLLER_DIGITAL_RIGHT; 51 | case ControllerDigital::X: 52 | return pros::E_CONTROLLER_DIGITAL_X; 53 | case ControllerDigital::B: 54 | return pros::E_CONTROLLER_DIGITAL_B; 55 | case ControllerDigital::Y: 56 | return pros::E_CONTROLLER_DIGITAL_Y; 57 | case ControllerDigital::A: 58 | default: 59 | return pros::E_CONTROLLER_DIGITAL_A; 60 | } 61 | } 62 | } // namespace okapi 63 | -------------------------------------------------------------------------------- /src/api/odometry/odomMath.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/odometry/odomMath.hpp" 7 | #include "okapi/api/util/mathUtil.hpp" 8 | #include 9 | 10 | namespace okapi { 11 | OdomMath::OdomMath() = default; 12 | 13 | OdomMath::~OdomMath() = default; 14 | 15 | QLength OdomMath::computeDistanceToPoint(const Point &ipoint, const OdomState &istate) { 16 | const auto [xDiff, yDiff] = computeDiffs(ipoint, istate); 17 | return computeDistance(xDiff, yDiff) * meter; 18 | } 19 | 20 | QAngle OdomMath::computeAngleToPoint(const Point &ipoint, const OdomState &istate) { 21 | const auto [xDiff, yDiff] = computeDiffs(ipoint, istate); 22 | return computeAngle(xDiff, yDiff, istate.theta.convert(radian)) * radian; 23 | } 24 | 25 | std::pair OdomMath::computeDistanceAndAngleToPoint(const Point &ipoint, 26 | const OdomState &istate) { 27 | const auto [xDiff, yDiff] = computeDiffs(ipoint, istate); 28 | return std::make_pair(computeDistance(xDiff, yDiff) * meter, 29 | computeAngle(xDiff, yDiff, istate.theta.convert(radian)) * radian); 30 | } 31 | 32 | std::pair OdomMath::computeDiffs(const Point &ipoint, const OdomState &istate) { 33 | const double xDiff = (ipoint.x - istate.x).convert(meter); 34 | const double yDiff = (ipoint.y - istate.y).convert(meter); 35 | return std::make_pair(xDiff, yDiff); 36 | } 37 | 38 | double OdomMath::computeDistance(double xDiff, double yDiff) { 39 | return std::sqrt((xDiff * xDiff) + (yDiff * yDiff)); 40 | } 41 | 42 | double OdomMath::computeAngle(double xDiff, double yDiff, double theta) { 43 | return std::atan2(yDiff, xDiff) - theta; 44 | } 45 | 46 | QAngle OdomMath::constrainAngle360(const QAngle &theta) { 47 | return theta - 360_deg * std::floor(theta.convert(degree) / 360.0); 48 | } 49 | 50 | QAngle OdomMath::constrainAngle180(const QAngle &theta) { 51 | return theta - 360_deg * std::floor((theta.convert(degree) + 180.0) / 360.0); 52 | } 53 | } // namespace okapi 54 | -------------------------------------------------------------------------------- /docs/tutorials/concepts/filtering.md: -------------------------------------------------------------------------------- 1 | # Filtering 2 | 3 | OkapiLib makes it easy to use any one of a number of various types of 4 | filters on sensors and controllers. The specifics of how each filter 5 | works and should be initialized will be left to its API reference, but 6 | this guide will help provide the general knowledge necessary to make the 7 | most out of OkapiLib's filtering functionality. 8 | 9 | ## Filtering Generic Sensor Input 10 | 11 | It's possible with OkapiLib to filter any value that you want, which 12 | makes it easy to filter sensors. The example below gives an example of 13 | filtering a sensor value. 14 | 15 | ```cpp 16 | const int POTENTIOMETER_PORT = 1; 17 | const int NUM_AVE_POINTS = 5; 18 | 19 | Potentiometer exampleSensor(POTENTIOMETER_PORT); 20 | AverageFilter exampleFilter; 21 | 22 | void opcontrol() { 23 | while (true) { 24 | std::cout << "Current Sensor Reading: " << exampleFilter.filter(exampleSensor.get()); 25 | pros::Task::delay(10); 26 | } 27 | } 28 | ``` 29 | 30 | The above example will print out the average of the last five readings 31 | of the potentiometer. 32 | 33 | ## Adding a Filter to a Controller 34 | 35 | Velocity PID Controllers often benefit from filtering the velocity 36 | reading. As a result, it is possible to pass in a filter as an argument 37 | to the constructor for a Velocity PID Controller. Note \-- filtering 38 | will not have a positive impact on position PID movements, and is not 39 | supported as a result. 40 | 41 | Using a filter with a velocity PID Controller can be done in the 42 | following manner: 43 | 44 | ```cpp 45 | const double kP = 0.001; 46 | const double kD = 0.0001; 47 | const double kF = 0.0; 48 | const double kSF = 0.0; 49 | const int NUM_AVE_POINTS = 5; 50 | 51 | auto exampleController = IterativeControllerFactory::velPID( 52 | kP, kD, kF, kSF, 53 | VelMathFactory::createPtr( 54 | imev5GreenTPR, 55 | std::make_unique>() 56 | ) 57 | ); 58 | 59 | void opcontrol() { 60 | } 61 | ``` 62 | 63 | This will create a velocity PID controller which uses an 64 | [AverageFilter](@ref okapi::AverageFilter). 65 | -------------------------------------------------------------------------------- /src/impl/filter/velMathFactory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/filter/velMathFactory.hpp" 7 | #include "okapi/api/filter/averageFilter.hpp" 8 | #include "okapi/impl/util/timer.hpp" 9 | 10 | namespace okapi { 11 | VelMath VelMathFactory::create(const double iticksPerRev, 12 | const QTime isampleTime, 13 | const std::shared_ptr &ilogger) { 14 | return VelMath(iticksPerRev, 15 | std::make_unique>(), 16 | isampleTime, 17 | std::make_unique(), 18 | ilogger); 19 | } 20 | 21 | std::unique_ptr VelMathFactory::createPtr(const double iticksPerRev, 22 | const QTime isampleTime, 23 | const std::shared_ptr &ilogger) { 24 | return std::make_unique(iticksPerRev, 25 | std::make_unique>(), 26 | isampleTime, 27 | std::make_unique(), 28 | ilogger); 29 | } 30 | 31 | VelMath VelMathFactory::create(const double iticksPerRev, 32 | std::unique_ptr ifilter, 33 | const QTime isampleTime, 34 | const std::shared_ptr &ilogger) { 35 | return VelMath(iticksPerRev, std::move(ifilter), isampleTime, std::make_unique(), ilogger); 36 | } 37 | 38 | std::unique_ptr VelMathFactory::createPtr(const double iticksPerRev, 39 | std::unique_ptr ifilter, 40 | const QTime isampleTime, 41 | const std::shared_ptr &ilogger) { 42 | return std::make_unique( 43 | iticksPerRev, std::move(ifilter), isampleTime, std::make_unique(), ilogger); 44 | } 45 | } // namespace okapi 46 | -------------------------------------------------------------------------------- /test/utilTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/util/mathUtil.hpp" 7 | #include 8 | 9 | using namespace okapi; 10 | 11 | TEST(IpowTest, IntegerTests) { 12 | EXPECT_EQ(ipow(0, 0), 1); 13 | EXPECT_EQ(ipow(0, 1), 0); 14 | EXPECT_EQ(ipow(1, 0), 1); 15 | EXPECT_EQ(ipow(1, 1), 1); 16 | EXPECT_EQ(ipow(2, 1), 2); 17 | EXPECT_EQ(ipow(2, 2), 4); 18 | } 19 | 20 | TEST(IpowTest, FloatingPointTests) { 21 | EXPECT_FLOAT_EQ(ipow(0.5, 1), 0.5); 22 | EXPECT_FLOAT_EQ(ipow(2.5, 2), 6.25); 23 | } 24 | 25 | TEST(CutRangeTest, Tests) { 26 | EXPECT_DOUBLE_EQ(cutRange(1, -2, 2), 2) << "1 : [-2, 2] -> 2"; 27 | EXPECT_DOUBLE_EQ(cutRange(2, -2, 2), 2) << "2 : [-2, 2] -> 2"; 28 | EXPECT_DOUBLE_EQ(cutRange(0, -2, 2), 2) << "0 : [-2, 2] -> 2"; 29 | EXPECT_DOUBLE_EQ(cutRange(-2, -2, 2), -2) << "-2 : [-2, 2] -> -2"; 30 | EXPECT_DOUBLE_EQ(cutRange(-3, -2, 2), -3) << "-3 : [-2, 2] -> -3"; 31 | EXPECT_DOUBLE_EQ(cutRange(3, -2, 2), 3) << "3 : [-2, 2] -> 3"; 32 | } 33 | 34 | TEST(DeadbandTest, Tests) { 35 | EXPECT_DOUBLE_EQ(deadband(0, -2, 2), 0) << "0 : [-2, 2] -> 0"; 36 | EXPECT_DOUBLE_EQ(deadband(1, -2, 2), 0) << "1 : [-2, 2] -> 0"; 37 | EXPECT_DOUBLE_EQ(deadband(2, -2, 2), 0) << "2 : [-2, 2] -> 0"; 38 | EXPECT_DOUBLE_EQ(deadband(-2, -2, 2), 0) << "-2 : [-2, 2] -> 0"; 39 | EXPECT_DOUBLE_EQ(deadband(3, -2, 2), 3) << "3 : [-2, 2] -> 3"; 40 | EXPECT_DOUBLE_EQ(deadband(-3, -2, 2), -3) << "-3 : [-2, 2] -> -3"; 41 | } 42 | 43 | TEST(RemapRangeTest, Tests) { 44 | EXPECT_FLOAT_EQ(remapRange(0, -1, 1, -2, 2), 0) << "0 : [-1, 1] -> [-2, 2] -> 0"; 45 | EXPECT_FLOAT_EQ(remapRange(0.1, -1, 1, -2, 2), 0.2) << "0.1 : [-1, 1] -> [-2, 2] -> 0.2"; 46 | EXPECT_FLOAT_EQ(remapRange(-0.1, -1, 1, 2, -2), 0.2) << "-0.1 : [-1, 1] -> [2, -2] -> 0.2"; 47 | EXPECT_FLOAT_EQ(remapRange(0, -1, 1, -5, 2), -1.5) << "0 : [-1, 1] -> [-5, 2] -> -1.5"; 48 | } 49 | 50 | TEST(TrueModTest, Tests) { 51 | EXPECT_EQ(modulus(0, 1), 0); 52 | EXPECT_EQ(modulus(1, 2), 1); 53 | EXPECT_EQ(modulus(-2, 5), 3); 54 | EXPECT_EQ(modulus(-1800, 3600), 1800); 55 | EXPECT_EQ(modulus(1, -3), -2); 56 | } 57 | -------------------------------------------------------------------------------- /src/impl/device/opticalSensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/opticalSensor.hpp" 7 | 8 | namespace okapi { 9 | OpticalSensor::OpticalSensor(const std::uint8_t iport, 10 | const OpticalSensorOutput ioutput, 11 | const bool idisableGestures, 12 | std::unique_ptr ifilter) 13 | : port(iport), output(ioutput), filter(std::move(ifilter)) { 14 | if (idisableGestures) { 15 | disableGestures(); 16 | } 17 | } 18 | 19 | double OpticalSensor::getSelectedOutput() { 20 | switch (output) { 21 | case OpticalSensorOutput::hue: 22 | return getHue(); 23 | case OpticalSensorOutput::saturation: 24 | return getSaturation(); 25 | case OpticalSensorOutput::brightness: 26 | return getBrightness(); 27 | } 28 | 29 | // This should not run 30 | return PROS_ERR_F; 31 | } 32 | 33 | double OpticalSensor::get() { 34 | return filter->filter(getSelectedOutput()); 35 | } 36 | 37 | double OpticalSensor::controllerGet() { 38 | return get(); 39 | } 40 | 41 | double OpticalSensor::getHue() const { 42 | return pros::c::optical_get_hue(port); 43 | } 44 | 45 | double OpticalSensor::getBrightness() const { 46 | return pros::c::optical_get_brightness(port); 47 | } 48 | 49 | double OpticalSensor::getSaturation() const { 50 | return pros::c::optical_get_saturation(port); 51 | } 52 | 53 | int32_t OpticalSensor::setLedPWM(const std::uint8_t ivalue) const { 54 | return pros::c::optical_set_led_pwm(port, ivalue); 55 | } 56 | 57 | int32_t OpticalSensor::getLedPWM() const { 58 | return pros::c::optical_get_led_pwm(port); 59 | } 60 | 61 | int32_t OpticalSensor::getProximity() const { 62 | return pros::c::optical_get_proximity(port); 63 | } 64 | 65 | int32_t OpticalSensor::enableGestures() const { 66 | return pros::c::optical_enable_gesture(port); 67 | } 68 | 69 | int32_t OpticalSensor::disableGestures() const { 70 | return pros::c::optical_disable_gesture(port); 71 | } 72 | 73 | pros::c::optical_rgb_s_t OpticalSensor::getRGB() const { 74 | return pros::c::optical_get_rgb(port); 75 | } 76 | } // namespace okapi 77 | -------------------------------------------------------------------------------- /include/api.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file api.h 3 | * 4 | * PROS API header provides high-level user functionality 5 | * 6 | * Contains declarations for use by typical VEX programmers using PROS. 7 | * 8 | * This file should not be modified by users, since it gets replaced whenever 9 | * a kernel upgrade occurs. 10 | * 11 | * \copyright Copyright (c) 2017-2023, Purdue University ACM SIGBots. 12 | * All rights reserved. 13 | * 14 | * This Source Code Form is subject to the terms of the Mozilla Public 15 | * License, v. 2.0. If a copy of the MPL was not distributed with this 16 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 17 | */ 18 | 19 | #ifndef _PROS_API_H_ 20 | #define _PROS_API_H_ 21 | 22 | #ifdef __cplusplus 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #else /* (not) __cplusplus */ 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #endif /* __cplusplus */ 41 | 42 | #define PROS_VERSION_MAJOR 4 43 | #define PROS_VERSION_MINOR 0 44 | #define PROS_VERSION_PATCH 3 45 | #define PROS_VERSION_STRING "4.0.3" 46 | 47 | #include "pros/adi.h" 48 | #include "pros/colors.h" 49 | #include "pros/device.h" 50 | #include "pros/distance.h" 51 | #include "pros/error.h" 52 | #include "pros/ext_adi.h" 53 | #include "pros/gps.h" 54 | #include "pros/imu.h" 55 | #include "pros/link.h" 56 | #include "pros/llemu.h" 57 | #include "pros/misc.h" 58 | #include "pros/motors.h" 59 | #include "pros/optical.h" 60 | #include "pros/rotation.h" 61 | #include "pros/rtos.h" 62 | #include "pros/screen.h" 63 | #include "pros/vision.h" 64 | 65 | #ifdef __cplusplus 66 | #include "pros/adi.hpp" 67 | #include "pros/colors.hpp" 68 | #include "pros/device.hpp" 69 | #include "pros/distance.hpp" 70 | #include "pros/gps.hpp" 71 | #include "pros/imu.hpp" 72 | #include "pros/link.hpp" 73 | #include "pros/llemu.hpp" 74 | #include "pros/misc.hpp" 75 | #include "pros/motor_group.hpp" 76 | #include "pros/motors.hpp" 77 | #include "pros/optical.hpp" 78 | #include "pros/rotation.hpp" 79 | #include "pros/rtos.hpp" 80 | #include "pros/screen.hpp" 81 | #include "pros/vision.hpp" 82 | #endif 83 | 84 | #endif // _PROS_API_H_ 85 | -------------------------------------------------------------------------------- /docs/api/control.md: -------------------------------------------------------------------------------- 1 | # Control API 2 | 3 | ## Async Controller API 4 | 5 | - [(Abstract) Async Controller](@ref okapi::AsyncController) 6 | - [(Abstract) Async Position Controller](@ref okapi::AsyncPositionController) 7 | - [(Abstract) Async Velocity Controller](@ref okapi::AsyncVelocityController) 8 | - [Async Position Controller Builder](@ref okapi::AsyncPosControllerBuilder) 9 | - [Async Velocity Controller Builder](@ref okapi::AsyncVelControllerBuilder) 10 | - [Async Linear Motion Profile Controller](@ref okapi::AsyncLinearMotionProfileController) 11 | - [Async Motion Profile Controller](@ref okapi::AsyncMotionProfileController) 12 | - [Async Motion Profile Controller Builder](@ref okapi::AsyncMotionProfileControllerBuilder) 13 | - [Async Pos Integrated Controller](@ref okapi::AsyncPosIntegratedController) 14 | - [Async Pos PID Controller](@ref okapi::AsyncPosPIDController) 15 | - [Async Vel Integrated Controller](@ref okapi::AsyncVelIntegratedController) 16 | - [Async Vel PID Controller](@ref okapi::AsyncVelPIDController) 17 | - [Async Wrapper](@ref okapi::AsyncWrapper) 18 | 19 | ## Iterative Controller API 20 | 21 | - [(Abstract) Iterative Controller](@ref okapi::IterativeController) 22 | - [(Abstract) Iterative Position Controller](@ref okapi::IterativePositionController) 23 | - [(Abstract) Iterative Velocity Controller](@ref okapi::IterativeVelocityController) 24 | - [Iterative Controller Factory](@ref okapi::IterativeControllerFactory) 25 | - [Iterative Motor Velocity Controller](@ref okapi::IterativeMotorVelocityController) 26 | - [Iterative Pos PID Controller](@ref okapi::IterativePosPIDController) 27 | - [Iterative Vel PID Controller](@ref okapi::IterativeVelPIDController) 28 | 29 | ## Controller Utilities API 30 | 31 | - [Controller Runner](@ref okapi::ControllerRunner) 32 | - [Controller Runner Factory](@ref okapi::ControllerRunnerFactory) 33 | - [PID Tuner](@ref okapi::PIDTuner) 34 | - [PID Tuner Factory](@ref okapi::PIDTunerFactory) 35 | - [Settled Utility](@ref okapi::SettledUtil) 36 | - [Flywheel Simulator](@ref okapi::FlywheelSimulator) 37 | 38 | ## Controller Interfaces 39 | 40 | - [(Abstract) Closed-loop Controller](@ref okapi::ClosedLoopController) 41 | - [(Abstract) Controller Input](@ref okapi::ControllerInput) 42 | - [(Abstract) Controller Output](@ref okapi::ControllerOutput) 43 | - [(Abstract) Offsetable Controller Output](@ref okapi::OffsetableControllerInput) 44 | -------------------------------------------------------------------------------- /test/odomMathTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/api/odometry/odomMath.hpp" 7 | #include 8 | 9 | using namespace okapi; 10 | 11 | TEST(OdomMathTests, ComputeDistanceToPoint) { 12 | EXPECT_FLOAT_EQ( 13 | OdomMath::computeDistanceToPoint({2_m, 3_m}, OdomState{1_m, -2_m, 75_deg}).convert(meter), 14 | sqrt(26)); 15 | } 16 | 17 | TEST(OdomMathTests, ComputeAngleToPoint) { 18 | EXPECT_FLOAT_EQ( 19 | OdomMath::computeAngleToPoint({2_m, 3_m}, OdomState{1_m, -2_m, 75_deg}).convert(degree), 20 | atan2(5, 1) * radianToDegree - 75); 21 | } 22 | 23 | TEST(OdomMathTests, ComputeDistanceAndAngleToPoint) { 24 | auto [dist, angle] = 25 | OdomMath::computeDistanceAndAngleToPoint({2_m, 3_m}, OdomState{1_m, -2_m, 75_deg}); 26 | EXPECT_FLOAT_EQ(sqrt(26), dist.convert(meter)); 27 | EXPECT_FLOAT_EQ(atan2(5, 1) * radianToDegree - 75, angle.convert(degree)); 28 | } 29 | 30 | TEST(OdomMathTests, ConstrainAngle360) { 31 | QAngle angle = OdomMath::constrainAngle360(75.0_deg); 32 | EXPECT_FLOAT_EQ(75.0, angle.convert(degree)); 33 | angle = OdomMath::constrainAngle360(0.0_deg); 34 | EXPECT_FLOAT_EQ(0.0, angle.convert(degree)); 35 | angle = OdomMath::constrainAngle360(360.0_deg); 36 | EXPECT_FLOAT_EQ(0.0, angle.convert(degree)); 37 | angle = OdomMath::constrainAngle360(720.0_deg); 38 | EXPECT_FLOAT_EQ(0.0, angle.convert(degree)); 39 | angle = OdomMath::constrainAngle360(-90.0_deg); 40 | EXPECT_FLOAT_EQ(270.0, angle.convert(degree)); 41 | } 42 | 43 | TEST(OdomMathTests, ConstrainAngle180) { 44 | QAngle angle = OdomMath::constrainAngle180(75.0_deg); 45 | EXPECT_FLOAT_EQ(75.0, angle.convert(degree)); 46 | angle = OdomMath::constrainAngle180(-75.0_deg); 47 | EXPECT_FLOAT_EQ(-75.0, angle.convert(degree)); 48 | angle = OdomMath::constrainAngle180(270.0_deg); 49 | EXPECT_FLOAT_EQ(-90.0, angle.convert(degree)); 50 | angle = OdomMath::constrainAngle180(270.0_deg + 360.0_deg); 51 | EXPECT_FLOAT_EQ(-90.0, angle.convert(degree)); 52 | angle = OdomMath::constrainAngle180(180.0_deg); 53 | EXPECT_FLOAT_EQ(-180.0, angle.convert(degree)); 54 | angle = OdomMath::constrainAngle180(181.0_deg); 55 | EXPECT_FLOAT_EQ(-179.0, angle.convert(degree)); 56 | } 57 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ######################### User configurable parameters ######################### 3 | # filename extensions 4 | CEXTS:=c 5 | ASMEXTS:=s S 6 | CXXEXTS:=cpp c++ cc 7 | 8 | # probably shouldn't modify these, but you may need them below 9 | ROOT=. 10 | FWDIR:=$(ROOT)/firmware 11 | BINDIR=$(ROOT)/bin 12 | SRCDIR=$(ROOT)/src 13 | INCDIR=$(ROOT)/include 14 | 15 | WARNFLAGS+=-Wall -Wextra -Wno-implicit-fallthrough -Wno-psabi -pedantic -Werror=return-type # -Wconversion #-Wmissing-include-dirs 16 | EXTRA_CFLAGS= 17 | EXTRA_CXXFLAGS= 18 | 19 | # Set to 1 to enable hot/cold linking 20 | USE_PACKAGE:=0 21 | 22 | # Set this to 1 to add additional rules to compile your project as a PROS library template 23 | IS_LIBRARY:=1 24 | LIBNAME:=okapilib 25 | VERSION:=4.8.0 26 | EXCLUDE_SRC_FROM_LIB=$(call rwildcard,$(SRCDIR)/test,*.*) 27 | # this line excludes opcontrol.c and similar files 28 | EXCLUDE_SRC_FROM_LIB+= $(foreach file, $(SRCDIR)/opcontrol $(SRCDIR)/initialize $(SRCDIR)/autonomous $(SRCDIR)/main,$(foreach cext,$(CEXTS),$(file).$(cext)) $(foreach cxxext,$(CXXEXTS),$(file).$(cxxext))) 29 | 30 | # Added to the local makefile to ensure that we don't try to download the 31 | # Squiggles source files for projects including Okapi as a template 32 | DOWNLOAD_SQUIGGLES:=$(shell mkdir -p cmake-build-debug && cd cmake-build-debug && cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" .. && rm -r ../src/squiggles 2> /dev/null || true && mkdir -p ../src/squiggles && find ./squiggles-src/main/src -type f -name '*.cpp' ! -name 'main.cpp' | xargs cp -t ../src/squiggles && rm -r ../include/okapi/squiggles 2> /dev/null || true && cp -r ./squiggles-src/main/include ../include/okapi/squiggles && cd ..) 33 | 34 | # files that get distributed to every user (beyond your source archive) - add 35 | # whatever files you want here. This line is configured to add all header files 36 | # that are in the the include directory get exported 37 | TEMPLATE_FILES=$(INCDIR)/okapi/**/*.h $(INCDIR)/okapi/**/*.hpp $(FWDIR)/squiggles.mk 38 | 39 | .DEFAULT_GOAL=quick 40 | 41 | ################################################################################ 42 | ################################################################################ 43 | ########## Nothing below this line should be edited by typical users ########### 44 | -include ./common.mk 45 | -------------------------------------------------------------------------------- /include/okapi/api/control/async/asyncVelPidController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/async/asyncVelocityController.hpp" 9 | #include "okapi/api/control/async/asyncWrapper.hpp" 10 | #include "okapi/api/control/controllerInput.hpp" 11 | #include "okapi/api/control/controllerOutput.hpp" 12 | #include "okapi/api/control/iterative/iterativeVelPidController.hpp" 13 | #include "okapi/api/util/timeUtil.hpp" 14 | #include 15 | 16 | namespace okapi { 17 | class AsyncVelPIDController : public AsyncWrapper, 18 | public AsyncVelocityController { 19 | public: 20 | /** 21 | * An async velocity PID controller. 22 | * 23 | * @param iinput The controller input. 24 | * @param ioutput The controller output. 25 | * @param itimeUtil The TimeUtil. 26 | * @param ikP The proportional gain. 27 | * @param ikD The derivative gain. 28 | * @param ikF The feed-forward gain. 29 | * @param ikSF A feed-forward gain to counteract static friction. 30 | * @param ivelMath The VelMath used for calculating velocity. 31 | * @param iratio Any external gear ratio. 32 | * @param iderivativeFilter The derivative filter. 33 | */ 34 | AsyncVelPIDController( 35 | const std::shared_ptr> &iinput, 36 | const std::shared_ptr> &ioutput, 37 | const TimeUtil &itimeUtil, 38 | double ikP, 39 | double ikD, 40 | double ikF, 41 | double ikSF, 42 | std::unique_ptr ivelMath, 43 | double iratio = 1, 44 | std::unique_ptr iderivativeFilter = std::make_unique(), 45 | const std::shared_ptr &ilogger = Logger::getDefaultLogger()); 46 | 47 | /** 48 | * Set controller gains. 49 | * 50 | * @param igains The new gains. 51 | */ 52 | void setGains(const IterativeVelPIDController::Gains &igains); 53 | 54 | /** 55 | * Gets the current gains. 56 | * 57 | * @return The current gains. 58 | */ 59 | IterativeVelPIDController::Gains getGains() const; 60 | 61 | protected: 62 | std::shared_ptr internalController; 63 | }; 64 | } // namespace okapi 65 | -------------------------------------------------------------------------------- /docs/tutorials/walkthrough/liftMovement.md: -------------------------------------------------------------------------------- 1 | # Lift Movement 2 | 3 | A large majority of vex games require the use of a lift, and it serves as a 4 | great example for controlling non-chassis systems. The easiest place to start 5 | with controlling a lift is having it move with joystick buttons, as is common in 6 | a lot of opcontrol code for the subject. The following code snippet shows how 7 | to do this with a [ControllerButton](@ref okapi::ControllerButton): 8 | 9 | ```cpp 10 | const int LIFT_MOTOR_PORT = 1; // Controlling a lift with a single motor on port 1 11 | 12 | ControllerButton btnUp(ControllerDigital::R1); 13 | ControllerButton btnDown(ControllerDigital::R2); 14 | Motor lift(LIFT_MOTOR_PORT); 15 | 16 | void opcontrol() { 17 | while (true) { 18 | if (btnUp.isPressed()) { 19 | lift.moveVoltage(12000); 20 | } else if (btnDown.isPressed()) { 21 | lift.moveVoltage(-12000); 22 | } else { 23 | lift.moveVoltage(0); 24 | } 25 | 26 | pros::delay(10); 27 | } 28 | } 29 | ``` 30 | 31 | One improvement that can be made to this lift code would be a switch to using 32 | set heights and a 33 | [position PID controller](@ref okapi::AsyncPosControllerBuilder). This is a 34 | common approach used for stacking games where movement to precise heights is 35 | important. 36 | 37 | ```cpp 38 | const int LIFT_MOTOR_PORT = 1; // Controlling a lift with a single motor on port 1 39 | 40 | const int NUM_HEIGHTS = 4; 41 | const int height1 = 20; 42 | const int height2 = 60; 43 | const int height3 = 100; 44 | const int height4 = 140; 45 | 46 | const int heights[NUM_HEIGHTS] = {height1, height2, height3, height4}; 47 | 48 | ControllerButton btnUp(ControllerDigital::R1); 49 | ControllerButton btnDown(ControllerDigital::R2); 50 | std::shared_ptr> liftControl = 51 | AsyncPosControllerBuilder().withMotor(LIFT_MOTOR_PORT).build(); 52 | 53 | void opcontrol() { 54 | int goalHeight = 0; 55 | 56 | while (true) { 57 | if (btnUp.changedToPressed() && goalHeight < NUM_HEIGHTS - 1) { 58 | // If the goal height is not at maximum and the up button is pressed, increase the setpoint 59 | goalHeight++; 60 | liftControl->setTarget(heights[goalHeight]); 61 | } else if (btnDown.changedToPressed() && goalHeight > 0) { 62 | goalHeight--; 63 | liftControl->setTarget(heights[goalHeight]); 64 | } 65 | 66 | pros::delay(10); 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /src/impl/device/controller.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #include "okapi/impl/device/controller.hpp" 7 | #include "okapi/api/util/mathUtil.hpp" 8 | #include "okapi/impl/device/controllerUtil.hpp" 9 | 10 | namespace okapi { 11 | Controller::Controller(const ControllerId iid) 12 | : okapiId(iid), prosId(ControllerUtil::idToProsEnum(iid)) { 13 | } 14 | 15 | Controller::~Controller() = default; 16 | 17 | bool Controller::isConnected() { 18 | const std::int32_t state = pros::c::controller_is_connected(prosId); 19 | return state == 1 || state == 2; 20 | } 21 | 22 | float Controller::getAnalog(const ControllerAnalog ichannel) { 23 | const auto val = 24 | pros::c::controller_get_analog(prosId, ControllerUtil::analogToProsEnum(ichannel)); 25 | 26 | if (val == PROS_ERR) { 27 | return 0; 28 | } 29 | 30 | return static_cast(val) / static_cast(127); 31 | } 32 | 33 | bool Controller::getDigital(const ControllerDigital ibutton) { 34 | return pros::c::controller_get_digital(prosId, ControllerUtil::digitalToProsEnum(ibutton)) == 1; 35 | } 36 | 37 | ControllerButton &Controller::operator[](const ControllerDigital ibtn) { 38 | const auto index = toUnderlyingType(ibtn) - toUnderlyingType(ControllerDigital::L1); 39 | 40 | if (buttonArray[index] == nullptr) { 41 | buttonArray[index] = new ControllerButton(okapiId, ibtn); 42 | } 43 | 44 | return *buttonArray[index]; 45 | } 46 | 47 | std::int32_t Controller::setText(std::uint8_t iline, std::uint8_t icol, std::string itext) { 48 | return pros::c::controller_set_text(prosId, iline, icol, itext.c_str()); 49 | } 50 | 51 | std::int32_t Controller::clear() { 52 | return pros::c::controller_clear(prosId); 53 | } 54 | 55 | std::int32_t Controller::clearLine(std::uint8_t iline) { 56 | return pros::c::controller_clear_line(prosId, iline); 57 | } 58 | 59 | std::int32_t Controller::rumble(std::string irumblePattern) { 60 | return pros::c::controller_rumble(prosId, irumblePattern.c_str()); 61 | } 62 | 63 | std::int32_t Controller::getBatteryCapacity() { 64 | return pros::c::controller_get_battery_capacity(prosId); 65 | } 66 | 67 | std::int32_t Controller::getBatteryLevel() { 68 | return pros::c::controller_get_battery_level(prosId); 69 | } 70 | } // namespace okapi 71 | -------------------------------------------------------------------------------- /include/okapi/api/control/iterative/iterativeController.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "okapi/api/control/closedLoopController.hpp" 9 | #include "okapi/api/units/QTime.hpp" 10 | 11 | namespace okapi { 12 | /** 13 | * Closed-loop controller that steps iteratively using the step method below. 14 | * 15 | * `ControllerOutput::controllerSet()` should set the controller's target to the input scaled by 16 | * the output bounds. 17 | */ 18 | template 19 | class IterativeController : public ClosedLoopController { 20 | public: 21 | /** 22 | * Do one iteration of the controller. 23 | * 24 | * @param ireading A new measurement. 25 | * @return The controller output. 26 | */ 27 | virtual Output step(Input ireading) = 0; 28 | 29 | /** 30 | * Returns the last calculated output of the controller. 31 | */ 32 | virtual Output getOutput() const = 0; 33 | 34 | /** 35 | * Set controller output bounds. 36 | * 37 | * @param imax max output 38 | * @param imin min output 39 | */ 40 | virtual void setOutputLimits(Output imax, Output imin) = 0; 41 | 42 | /** 43 | * Sets the (soft) limits for the target range that controllerSet() scales into. The target 44 | * computed by `controllerSet()` is scaled into the range `[-itargetMin, itargetMax]`. 45 | * 46 | * @param itargetMax The new max target for `controllerSet()`. 47 | * @param itargetMin The new min target for `controllerSet()`. 48 | */ 49 | virtual void setControllerSetTargetLimits(Output itargetMax, Output itargetMin) = 0; 50 | 51 | /** 52 | * Get the upper output bound. 53 | * 54 | * @return the upper output bound 55 | */ 56 | virtual Output getMaxOutput() = 0; 57 | 58 | /** 59 | * Get the lower output bound. 60 | * 61 | * @return the lower output bound 62 | */ 63 | virtual Output getMinOutput() = 0; 64 | 65 | /** 66 | * Set time between loops. 67 | * 68 | * @param isampleTime time between loops 69 | */ 70 | virtual void setSampleTime(QTime isampleTime) = 0; 71 | 72 | /** 73 | * Get the last set sample time. 74 | * 75 | * @return sample time 76 | */ 77 | virtual QTime getSampleTime() const = 0; 78 | }; 79 | } // namespace okapi 80 | -------------------------------------------------------------------------------- /include/okapi/api/filter/medianFilter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Uses the median filter algorithm from N. Wirth’s book, implementation by N. Devillard. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #pragma once 9 | 10 | #include "okapi/api/filter/filter.hpp" 11 | #include 12 | #include 13 | #include 14 | 15 | namespace okapi { 16 | /** 17 | * A filter which returns the median value of list of values. 18 | * 19 | * @tparam n number of taps in the filter 20 | */ 21 | template class MedianFilter : public Filter { 22 | public: 23 | MedianFilter() : middleIndex((((n)&1) ? ((n) / 2) : (((n) / 2) - 1))) { 24 | } 25 | 26 | /** 27 | * Filters a value, like a sensor reading. 28 | * 29 | * @param ireading new measurement 30 | * @return filtered result 31 | */ 32 | double filter(const double ireading) override { 33 | data[index++] = ireading; 34 | if (index >= n) { 35 | index = 0; 36 | } 37 | 38 | output = kth_smallset(); 39 | return output; 40 | } 41 | 42 | /** 43 | * Returns the previous output from filter. 44 | * 45 | * @return the previous output from filter 46 | */ 47 | double getOutput() const override { 48 | return output; 49 | } 50 | 51 | protected: 52 | std::array data{0}; 53 | std::size_t index = 0; 54 | double output = 0; 55 | const size_t middleIndex; 56 | 57 | /** 58 | * Algorithm from N. Wirth’s book, implementation by N. Devillard. 59 | */ 60 | double kth_smallset() { 61 | std::array dataCopy = data; 62 | size_t j, l, m; 63 | l = 0; 64 | m = n - 1; 65 | 66 | while (l < m) { 67 | double x = dataCopy[middleIndex]; 68 | size_t i = l; 69 | j = m; 70 | do { 71 | while (dataCopy[i] < x) { 72 | i++; 73 | } 74 | while (x < dataCopy[j]) { 75 | j--; 76 | } 77 | if (i <= j) { 78 | const double t = dataCopy[i]; 79 | dataCopy[i] = dataCopy[j]; 80 | dataCopy[j] = t; 81 | i++; 82 | j--; 83 | } 84 | } while (i <= j); 85 | if (j < middleIndex) 86 | l = i; 87 | if (middleIndex < i) 88 | m = j; 89 | } 90 | 91 | return dataCopy[middleIndex]; 92 | } 93 | }; 94 | } // namespace okapi 95 | -------------------------------------------------------------------------------- /include/okapi/impl/device/motor/adiMotor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | #pragma once 7 | 8 | #include "api.h" 9 | #include "okapi/api/control/controllerOutput.hpp" 10 | #include "okapi/api/util/logging.hpp" 11 | 12 | namespace okapi { 13 | class ADIMotor : public ControllerOutput { 14 | public: 15 | /** 16 | * A motor in an ADI port. 17 | * 18 | * ```cpp 19 | * auto mtr = ADIMotor('A'); 20 | * auto reversedMtr = ADIMotor('A', true); 21 | * ``` 22 | * 23 | * @param iport The ADI port number (``[1, 8]``, ``[a, h]``, ``[A, H]``). 24 | * @param ireverse Whether the motor is reversed. 25 | * @param logger The logger that initialization warnings will be logged to. 26 | */ 27 | ADIMotor(std::uint8_t iport, 28 | bool ireverse = false, 29 | const std::shared_ptr &logger = Logger::getDefaultLogger()); 30 | 31 | /** 32 | * A motor in an ADI port. 33 | * 34 | * ```cpp 35 | * auto mtr = ADIMotor({1, 'A'}, false); 36 | * auto reversedMtr = ADIMotor({1, 'A'}, true); 37 | * ``` 38 | * 39 | * @param iports The ports the motor is plugged in to in the order ``{smart port, motor port}``. 40 | * The smart port is the smart port number (``[1, 21]``). The motor port is the ADI port number 41 | * (``[1, 8]``, ``[a, h]``, ``[A, H]``). 42 | * @param ireverse Whether the motor is reversed. 43 | * @param logger The logger that initialization warnings will be logged to. 44 | */ 45 | ADIMotor(std::pair iports, 46 | bool ireverse = false, 47 | const std::shared_ptr &logger = Logger::getDefaultLogger()); 48 | 49 | /** 50 | * Set the voltage to the motor. 51 | * 52 | * @param ivoltage voltage in the range [-127, 127]. 53 | */ 54 | virtual void moveVoltage(std::int8_t ivoltage) const; 55 | 56 | /** 57 | * Writes the value of the controller output. This method might be automatically called in another 58 | * thread by the controller. The range of input values is expected to be [-1, 1]. 59 | * 60 | * @param ivalue the controller's output in the range [-1, 1] 61 | */ 62 | void controllerSet(double ivalue) override; 63 | 64 | protected: 65 | std::uint8_t smartPort; 66 | std::uint8_t port; 67 | std::int8_t reversed; 68 | }; 69 | } // namespace okapi 70 | -------------------------------------------------------------------------------- /docs/tutorials/walkthrough/asyncAutonomousMovement.md: -------------------------------------------------------------------------------- 1 | # Asynchronous Autonomous Movement 2 | 3 | Oftentimes the fastest way to move in autonomous involves actuating multiple 4 | subsystems at once (i.e. driving and raising/lowering a lift). This is made 5 | possible with Async Controllers. 6 | 7 | To create a [ChassisController](@ref okapi::ChassisController) for a given system, 8 | modify the below example to fit your subsystem. 9 | 10 | ```cpp 11 | std::shared_ptr driveController = 12 | ChassisControllerBuilder() 13 | .withMotors(1, -2) 14 | // Green gearset, 4 in wheel diam, 11.5 in wheel track 15 | .withDimensions(AbstractMotor::gearset::green, {{4_in, 11.5_in}, imev5GreenTPR}) 16 | .build(); 17 | ``` 18 | 19 | And then we'll add a lift subsystem as an Async Controller: 20 | 21 | ```cpp 22 | const double liftkP = 0.001; 23 | const double liftkI = 0.0001; 24 | const double liftkD = 0.0001; 25 | 26 | std::shared_ptr> liftController = 27 | AsyncPosControllerBuilder() 28 | .withMotor(3) // lift motor port 3 29 | .withGains({liftkP, liftkI, liftkD}) 30 | .build(); 31 | ``` 32 | 33 | Now that we have two subsystems to run, let's execute a few different movements. 34 | If we want both systems to move, and the next movement in the autonomous routine 35 | only depends on the drive completing its movement (and it doesn't care about the 36 | lift's status), we'll run 37 | [waitUntilSettled()](@ref okapi::ChassisController::waitUntilSettled) with just 38 | the drive controller. 39 | 40 | ```cpp 41 | // Begin movements 42 | driveController->moveDistanceAsync(10_in); // Move 10 inches forward 43 | liftController->setTarget(200); // Move 200 motor degrees upward 44 | driveController->waitUntilSettled(); 45 | 46 | // Then the next movement will execute after the drive movement finishes 47 | ``` 48 | 49 | If blocking the next movement with regard only to the lift is desired, swap 50 | `driveController` for `liftController` in the last line. If both movements need 51 | to finish before executing the next movement, then call 52 | [waitUntilSettled()](@ref okapi::ChassisController::waitUntilSettled) on both 53 | controllers. 54 | 55 | ```cpp 56 | // Begin movements 57 | driveController->moveDistanceAsync(10_in); // Move 10 inches forward 58 | liftController->setTarget(200); // Move 200 motor degrees upward 59 | driveController->waitUntilSettled(); 60 | liftController->waitUntilSettled(); 61 | 62 | // Then the next movement will execute after both movements finish 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/tutorials/concepts/settled-util.md: -------------------------------------------------------------------------------- 1 | # SettledUtil 2 | 3 | One of the most important factors in utilizing feedback controllers is 4 | establishing proper exit conditions. If a feedback controller decides 5 | that it has reached its target and exits too early, then the target will 6 | never be met. Alternatively, if the controller takes too long to realize 7 | that it's at its goal, the best case scenario is that the movement is 8 | just a bit slower, but in the worst case the movement may never be 9 | considered complete, leaving the motors humming indefinitely. 10 | 11 | OkapiLib's [SettledUtil](@ref okapi::SettledUtil) provides three different 12 | parameters for tuning this exit condition: `atTargetError`, 13 | `atTargetDerivative`, and `atTargetTime`. We'll take a further look at tuning 14 | these three parameters. 15 | 16 | ## atTargetError 17 | 18 | `atTargetError` is the maximum error value for the controller to 19 | consider itself \"settled\", or conclusively at its target. This value 20 | can also be thought of as the precision for the controller's movement; 21 | if a controller with an `atTargetError` value of `10` is moving towards a 22 | target of `100`, then the movement could be considered done at any value 23 | between `90` and `110`. 24 | 25 | It would be ideal to be able to set `0` for this value and theoretically 26 | ensure that the controller ends up exactly at its target with every 27 | movement, but this is a very good way to end up in the indefinite motor 28 | humming scenario detailed at the top of the tutorial. At the very least 29 | this value needs to be greater than the mechanical slop in the system 30 | (the amount that the sensor can move without encountering resistance 31 | from the motors), but typically this value will be closer to the default 32 | of `50`. 33 | 34 | To disable this check, set the maximum value for a double. 35 | 36 | ## atTargetDerivative 37 | 38 | `atTargetDerivative` is the maximum *change in error* that the 39 | controller can be observing and still consider itself settled. This 40 | value helps prevent scenarios where the controller considers itself 41 | settled while quickly passing by the target and oscillating. 42 | 43 | To disable this check, set the maximum value for a double. 44 | 45 | ## atTargetTime 46 | 47 | `atTargetTime` is the minimum amount of time that the controller must 48 | stay in the range of `Target +- atTargetError` to be considered settled. 49 | This also helps prevent a false settled scenario when oscillating around 50 | the target. 51 | 52 | To disable this check, set `0`. 53 | -------------------------------------------------------------------------------- /include/pros/device.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file pros/device.hpp 3 | * 4 | * Base class for all smart devices. 5 | * 6 | * This file should not be modified by users, since it gets replaced whenever 7 | * a kernel upgrade occurs. 8 | * 9 | * \copyright (c) 2017-2021, Purdue University ACM SIGBots. 10 | * 11 | * This Source Code Form is subject to the terms of the Mozilla Public 12 | * License, v. 2.0. If a copy of the MPL was not distributed with this 13 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 | */ 15 | 16 | #ifndef _PROS_DEVICE_HPP_ 17 | #define _PROS_DEVICE_HPP_ 18 | 19 | #include "pros/misc.hpp" 20 | #include "pros/rtos.hpp" 21 | 22 | namespace pros { 23 | inline namespace v5 { 24 | 25 | /* 26 | * Enum of possible v5 devices. 27 | * 28 | * Contains all current V5 Devices. 29 | */ 30 | enum class DeviceType { 31 | none = 0, 32 | motor = 2, 33 | rotation = 4, 34 | imu = 6, 35 | distance = 7, 36 | radio = 8, 37 | vision = 11, 38 | adi = 12, 39 | optical = 16, 40 | gps = 20, 41 | serial = 129, 42 | undefined = 255 43 | }; 44 | 45 | class Device { 46 | public: 47 | /** 48 | * Creates a Device object. 49 | * 50 | * \param port The V5 port number from 1-21 51 | */ 52 | explicit Device(const std::uint8_t port); 53 | 54 | /** 55 | * Gets the port number of the Smart Device. 56 | * 57 | * \return The smart device's port number. 58 | */ 59 | std::uint8_t get_port(void); 60 | 61 | /** 62 | * Checks if the device is installed. 63 | * 64 | * \return true if the corresponding device is installed, false otherwise. 65 | */ 66 | virtual bool is_installed(); 67 | 68 | /** 69 | * Gets the type of device. 70 | * 71 | * This function uses the following values of errno when an error state is 72 | * reached: 73 | * EACCES - Mutex of port cannot be taken (access denied). 74 | * 75 | * \return The device type as an enum. 76 | */ 77 | pros::DeviceType get_plugged_type() const; 78 | 79 | 80 | protected: 81 | /** 82 | * Creates a Device object. 83 | * 84 | * \param port The V5 port number from 1-21 85 | * 86 | * \param deviceType The type of the constructed device 87 | */ 88 | Device(const std::uint8_t port, const enum DeviceType deviceType) : 89 | _port(port), 90 | _deviceType(deviceType) {} 91 | 92 | protected: 93 | const std::uint8_t _port; 94 | const enum DeviceType _deviceType = pros::DeviceType::none; 95 | }; 96 | } // namespace v5 97 | } // namespace pros 98 | 99 | #endif 100 | --------------------------------------------------------------------------------