From e00184195720bc2887ae8f90e6a2db6ff63b529f Mon Sep 17 00:00:00 2001 From: audiobird Date: Fri, 11 Apr 2025 17:02:43 -0700 Subject: [PATCH 1/6] Add quadrature encoder decoder --- util/quadrature_encoder.hh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 util/quadrature_encoder.hh diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh new file mode 100644 index 0000000..7fb2e9f --- /dev/null +++ b/util/quadrature_encoder.hh @@ -0,0 +1,28 @@ +#pragma once + +#include + +template +class QuadratureEncoder { + static constexpr uint8_t valid_ccw = 0b10000111 ^ invert ? 0xff : 0x00; + static constexpr uint8_t valid_cw = 0b01001011 ^ invert ? 0xff : 0x00; + + uint8_t prev_state{}; + +public: + enum class State { Undefined, Right, Left }; + + State update(bool state_a, bool state_b) { + const auto cur_state = state_a | (state_b << 1u); + prev_state <<= 2; + prev_state |= cur_state; + switch (prev_state) { + case valid_cw: + return State::Right; + case valid_ccw: + return State::Left; + default: + return State::Undefined; + } + } +}; From a5cc30d9c48d4af495c8d4ae6ae546fd591d3710 Mon Sep 17 00:00:00 2001 From: audiobird Date: Fri, 11 Apr 2025 18:03:26 -0700 Subject: [PATCH 2/6] Fix xor --- util/quadrature_encoder.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh index 7fb2e9f..26d2643 100644 --- a/util/quadrature_encoder.hh +++ b/util/quadrature_encoder.hh @@ -4,8 +4,8 @@ template class QuadratureEncoder { - static constexpr uint8_t valid_ccw = 0b10000111 ^ invert ? 0xff : 0x00; - static constexpr uint8_t valid_cw = 0b01001011 ^ invert ? 0xff : 0x00; + static constexpr uint8_t valid_ccw = 0b10000111 ^ (invert ? 0xff : 0x00); + static constexpr uint8_t valid_cw = 0b01001011 ^ (invert ? 0xff : 0x00); uint8_t prev_state{}; From 3566d1c4f6216e9e5125128e4c0c5e943ca60afa Mon Sep 17 00:00:00 2001 From: Dan Green Date: Tue, 15 Apr 2025 10:21:21 -0700 Subject: [PATCH 3/6] Refactor to check for changed pins, and return enum with signed int values. --- util/quadrature_encoder.hh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh index 26d2643..fc28be0 100644 --- a/util/quadrature_encoder.hh +++ b/util/quadrature_encoder.hh @@ -10,19 +10,24 @@ class QuadratureEncoder { uint8_t prev_state{}; public: - enum class State { Undefined, Right, Left }; + enum Direction { None = 0, CW = 1, CCW = -1 }; - State update(bool state_a, bool state_b) { + Direction get_motion(bool state_a, bool state_b) { const auto cur_state = state_a | (state_b << 1u); + + if ((prev_state & 0b11) == cur_state) + return None; + prev_state <<= 2; prev_state |= cur_state; + switch (prev_state) { case valid_cw: - return State::Right; + return CW; case valid_ccw: - return State::Left; + return CCW; default: - return State::Undefined; + return None; } } }; From d82a4d8cc9e83f06e31592541a7bddb995fdfdc8 Mon Sep 17 00:00:00 2001 From: audiobird Date: Tue, 15 Apr 2025 11:00:28 -0700 Subject: [PATCH 4/6] Add quadrature encoder test --- tests/quadrature_encoder_tests.cc | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/quadrature_encoder_tests.cc diff --git a/tests/quadrature_encoder_tests.cc b/tests/quadrature_encoder_tests.cc new file mode 100644 index 0000000..097e1a6 --- /dev/null +++ b/tests/quadrature_encoder_tests.cc @@ -0,0 +1,55 @@ +#include "doctest.h" +#include "util/quadrature_encoder.hh" + +TEST_CASE("Quadrature Encoder") { + + using Enc = QuadratureEncoder<>; + + Enc encoder{}; + + // check clockwise + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::CW); + + // check clockwise no state change + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + + CHECK(encoder.get_motion(true, true) == Enc::CW); + CHECK(encoder.get_motion(true, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::None); + + // check counter-clockwise + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::CCW); + + // check with no state change + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + + CHECK(encoder.get_motion(true, true) == Enc::CCW); + CHECK(encoder.get_motion(true, true) == Enc::None); +} From d0d17ebd11b6c33a57de78fe414fba7aacd409dc Mon Sep 17 00:00:00 2001 From: audiobird Date: Mon, 28 Jul 2025 09:13:25 -0700 Subject: [PATCH 5/6] add HSV color class that converts to type Color{} --- util/hsv.hh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 util/hsv.hh diff --git a/util/hsv.hh b/util/hsv.hh new file mode 100644 index 0000000..ed49dd8 --- /dev/null +++ b/util/hsv.hh @@ -0,0 +1,39 @@ +#pragma once + +#include "util/colors.hh" +#include + +class HSVColor { +public: + uint8_t h; + uint8_t s; + uint8_t v; + + constexpr operator Color() const { + if (!s) { + return Color{v, v, v}; + } + + const auto region = h / 43u; + const auto remainder = (h - (region * 43u)) * 6u; + + const uint8_t p = (v * (255u - s)) >> 8u; + const uint8_t q = (v * (255u - ((s * remainder) >> 8u))) >> 8u; + const uint8_t t = (v * (255u - ((s * (255u - remainder)) >> 8u))) >> 8u; + + switch (region) { + case 0: + return Color{v, t, p}; + case 1: + return Color{q, v, p}; + case 2: + return Color{p, v, t}; + case 3: + return Color{p, q, v}; + case 4: + return Color{t, p, v}; + default: + return Color{v, p, q}; + } + } +}; From 8148361694c6d7cf38d7899738ee4f876f0e00b9 Mon Sep 17 00:00:00 2001 From: audiobird Date: Mon, 28 Jul 2025 09:24:36 -0700 Subject: [PATCH 6/6] add HSV color conversion tests --- tests/hsv_tests.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/hsv_tests.cc diff --git a/tests/hsv_tests.cc b/tests/hsv_tests.cc new file mode 100644 index 0000000..9d5b73d --- /dev/null +++ b/tests/hsv_tests.cc @@ -0,0 +1,19 @@ +#include "doctest.h" +#include "util/hsv.hh" + +TEST_CASE("HSV to Color") { + // check primary colors + CHECK(Color{HSVColor{0, 255, 255}} == Colors::red); + CHECK(Color{HSVColor{86, 255, 255}} == Colors::green); + CHECK(Color{HSVColor{172, 255, 255}} == Colors::blue); + + // check no saturation + const auto ns = Color{HSVColor{172, 0, 255}}; + CHECK(ns.red() == ns.green()); + CHECK(ns.red() == ns.blue()); + CHECK(ns.red() == 255); + + // check value + const auto v = Color{HSVColor{0, 255, 0}}; + CHECK(v == Colors::black); +}