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

Add embed::linear_potentiometer #276

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ 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/linear_potentiometer/interface.test.cpp

tests/timer/stub.test.cpp
tests/adc/stub.test.cpp
tests/dac/stub.test.cpp
tests/serial/stub.test.cpp

tests/i2c/util.test.cpp
tests/spi/util.test.cpp
Expand Down
70 changes: 70 additions & 0 deletions include/libembeddedhal/linear_potentiometer/interface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#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()
{
return units::aliases::isq::si::nm<std::int64_t>(linear_map(
static_cast<std::int64_t>(m_adc->read().value().raw_value()),
static_cast<std::int64_t>(m_settings.p_min_adc_output.raw_value()),
static_cast<std::int64_t>(m_settings.p_max_adc_output.raw_value()),
m_settings.p_min_distance.number(),
m_settings.p_max_distance.number()));
}

private:
embed::adc* m_adc = nullptr;
settings m_settings{};
};
} // namespace embed
26 changes: 26 additions & 0 deletions include/libembeddedhal/math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,30 @@ template<typename T>

return quotient;
}

/**
* @brief Remap a value from one range to another without constraining values
* within the given ranges.
*
* @tparam T
* @param p_in_value - Value to convert.
* @param p_in_min - Minimum value of the range to convert from.
* @param p_in_max - Maximum value of the range to convert from.
* @param p_out_min - Minimum value of the range to convert to.
* @param p_out_max - Maximum value of the range to convert to.
* @return constexpr T - Value after being remapped to out range
*/
template<typename T>
[[nodiscard]] constexpr T linear_map(T p_in_value,
T p_in_min,
T p_in_max,
T p_out_min,
T p_out_max) noexcept
{
auto numerator = multiply_with_overflow_detection((p_in_value - p_in_min),
(p_out_max - p_out_min))
.value();
auto denominator = p_in_max - p_in_min;
return rounding_division(numerator, denominator) + p_out_min;
}
} // namespace embed
123 changes: 123 additions & 0 deletions tests/linear_potentiometer/interface.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include <boost/ut.hpp>
#include <libembeddedhal/adc/mock.hpp>
#include <libembeddedhal/linear_potentiometer/interface.hpp>

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

"embed::linear_potentiometer::read() basic test"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(1, 2));
embed::linear_potentiometer::settings settings;
settings.p_min_distance = units::aliases::isq::si::nm<std::int64_t>(20);
settings.p_max_distance = units::aliases::isq::si::nm<std::int64_t>(120);
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(70 == test.read().value().number());
mock.set(embed::percent::from_ratio(1, 1));
expect(120 == test.read().value().number());
mock.set(embed::percent::from_ratio(0, 1));
expect(20 == test.read().value().number());
};

"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(0 == test.read().value().number());
};

"embed::linear_potentiometer::read() max percent"_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(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(100 == test.read().value().number());
};

"embed::linear_potentiometer::read() min percent"_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>(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(0 == test.read().value().number());
};

"embed::linear_potentiometer::read() negative percents"_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(0 == test.read().value().number());
};

"embed::linear_potentiometer::read() negative distances"_test = []() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(1, 2));
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(-150 == test.read().value().number());
};

"embed::linear_potentiometer::read() adc percent bigger than max percent"_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(0, 1);
settings.p_max_adc_output = embed::percent::from_ratio(1, 2);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(200 == test.read().value().number());
};

"embed::linear_potentiometer::read() adc percent smaller than min percent"_test =
[]() {
// Setup
embed::mock::adc mock(embed::percent::from_ratio(1, 4));
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, 2);
settings.p_max_adc_output = embed::percent::from_ratio(1, 1);
embed::linear_potentiometer test(mock, settings);

// Exercise + Verify
expect(-50 == test.read().value().number());
};
};
} // namespace embed