Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Add embed:linear_potentiometer
Browse files Browse the repository at this point in the history
Resolves #201
MaliaLabor authored and kammce committed Jun 29, 2022
1 parent 800c57f commit c7f4b75
Showing 3 changed files with 313 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@ add_executable(${TEST_NAME}
tests/output_pin/interface.test.cpp
tests/serial/interface.test.cpp
tests/rotary_encoder/interface.test.cpp
tests/linear_potentiometer/interface.test.cpp

tests/i2c/util.test.cpp
tests/spi/util.test.cpp
71 changes: 71 additions & 0 deletions include/libembeddedhal/linear_potentiometer/interface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

#include <units/isq/si/length.h>

#include "../adc/interface.hpp"
#include "../math.hpp"

namespace embed {
/**
* @brief Linear potentiometer driver that takes an embed::adc and a
* settings struct, and reads the distance the linear potentiometer has traveled
* in nanometres.
*
*/
class linear_potentiometer
{
public:
/// Universal unit for length
using length =
units::isq::si::length<units::isq::si::nanometre, std::int64_t>;
/**
* @brief Settings for a linear potentiometer object. Contains
* variables for minimum and maximum travel distance, as well as variables for
* minimum and maximum percentages for the ADC.
*
*/
struct settings
{
/// Minimum travel distance in nanometres
length p_min_distance = units::aliases::isq::si::nm<std::int64_t>(0);
/// Maximum travel distance in nanometres
length p_max_distance = units::aliases::isq::si::nm<std::int64_t>(0);
/// Minimum percent that can be read from ADC
embed::percent p_min_adc_output = embed::percent::from_ratio(0, 1);
/// Maximum percent that can be read from ADC
embed::percent p_max_adc_output = embed::percent::from_ratio(1, 1);
};

/**
* @brief Construct a new linear potentiometer object.
*
* @param p_adc ADC of linear potentiometer.
* @param p_settings Settings for linear potentiometer.
*/
linear_potentiometer(embed::adc& p_adc, settings p_settings)
: m_adc(&p_adc)
, m_settings(p_settings)
{
}

/**
* @brief Read the distance the linear potentiometer has traveled.
*
* @return boost::leaf::result<length> - the distance traveled of the
* potentiometer in nanometres.
*/
boost::leaf::result<length> read()
{
auto adc_percent = BOOST_LEAF_CHECK(m_adc->read());
auto scaled_percent = adc_percent.scale(m_settings.p_min_adc_output,
m_settings.p_max_adc_output);
auto scaled_distance = scaled_percent.scale(
m_settings.p_min_distance.number(), m_settings.p_max_distance.number());
return units::aliases::isq::si::nm<std::int64_t>(scaled_distance);
}

private:
embed::adc* m_adc = nullptr;
settings m_settings{};
};
} // namespace embed
241 changes: 241 additions & 0 deletions tests/linear_potentiometer/interface.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
#include <boost/ut.hpp>
#include <libembeddedhal/adc/mock.hpp>
#include <libembeddedhal/linear_potentiometer/interface.hpp>
#include <units/isq/si/international/length.h>

namespace embed {
boost::ut::suite linear_pot_test = []() {
using namespace boost::ut;

"embed::linear_potentiometer::read() default settings"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(1, 2));
embed::linear_potentiometer::settings settings;
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 0 == test.read().value().number());
};

"embed::linear_potentiometer::read() positive percent range"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::nm<std::int64_t>(1);
settings.p_max_distance = units::aliases::isq::si::nm<std::int64_t>(100);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 1 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
expect(that % 25 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % 50 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % 75 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 100 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 100 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % 1 == test.read().value().number());
};

"embed::linear_potentiometer::read() negative percent range"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(-1, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::nm<std::int64_t>(0);
settings.p_max_distance = units::aliases::isq::si::nm<std::int64_t>(100);
settings.p_min_adc_output = embed::percent::from_ratio(-1, 1);
settings.p_max_adc_output = embed::percent::from_ratio(0, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 0 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 4));
expect(that % 75 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % 50 == test.read().value().number());
mock.set(embed::percent::from_ratio(-3, 4));
expect(that % 25 == test.read().value().number());
mock.set(embed::percent::from_ratio(0, 1));
expect(that % 100 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % 100 == test.read().value().number());
mock.set(embed::percent::from_ratio(-2, 1));
expect(that % 0 == test.read().value().number());
};

"embed::linear_potentiometer::read() negative distances"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::nm<std::int64_t>(-200);
settings.p_max_distance = units::aliases::isq::si::nm<std::int64_t>(-100);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % -200 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
expect(that % -175 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % -150 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % -125 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % -100 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % -200 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % -100 == test.read().value().number());
};

"embed::linear_potentiometer::read() large positive range"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::nm<std::int64_t>(0);
settings.p_max_distance =
units::aliases::isq::si::nm<std::int64_t>(4000000000000000000);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 0 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
// TODO: Fix with issue #273, expected value adjusted to work with current
// division arithmetic
expect(that % 999999998603016137 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % 1999999999068677424 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % 2999999999534338712 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % 0 == test.read().value().number());
};

"embed::linear_potentiometer::read() large negative range"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance =
units::aliases::isq::si::nm<std::int64_t>(-4000000000000000000);
settings.p_max_distance = units::aliases::isq::si::nm<std::int64_t>(0);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % -4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
// TODO: Fix with issue #273, expected value adjusted to work with current
// division arithmetic
expect(that % -3000000001396983863 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % -2000000000931322576 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % -1000000000465661288 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 0 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 0 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % -4000000000000000000 == test.read().value().number());
};

"embed::linear_potentiometer::read() range exceeds max integer value"_test =
[]() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance =
units::aliases::isq::si::nm<std::int64_t>(-4000000000000000000);
settings.p_max_distance =
units::aliases::isq::si::nm<std::int64_t>(4000000000000000000);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % -4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
// TODO: Fix with issue #273, expected value adjusted to work with current
// division arithmetic
expect(that % -2000000002793967726 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % -1862645152 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % 1999999999068677424 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 4000000000000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % -4000000000000000000 == test.read().value().number());
};

"embed::linear_potentiometer::read() range in meters"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::m<std::int64_t>(1);
settings.p_max_distance = units::aliases::isq::si::m<std::int64_t>(2);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 1000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
expect(that % 1250000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % 1500000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % 1750000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 2000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 2000000000 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % 1000000000 == test.read().value().number());
};

"embed::linear_potentiometer::read() range in inches"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(0, 1));
embed::linear_potentiometer::settings settings;
settings.p_min_distance =
units::aliases::isq::si::international::in<std::int64_t>(1);
settings.p_max_distance =
units::aliases::isq::si::international::in<std::int64_t>(4);
settings.p_min_adc_output = embed::percent::from_ratio(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(that % 25400000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 4));
expect(that % 44450000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 2));
expect(that % 63500000 == test.read().value().number());
mock.set(embed::percent::from_ratio(3, 4));
expect(that % 82550000 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(that % 101600000 == test.read().value().number());
mock.set(embed::percent::from_ratio(2, 1));
expect(that % 101600000 == test.read().value().number());
mock.set(embed::percent::from_ratio(-1, 2));
expect(that % 25400000 == test.read().value().number());
};
};
} // namespace embed

0 comments on commit c7f4b75

Please sign in to comment.