From 0b08341ef193b41add5edb096a6efcf39e87b1f4 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 27 Oct 2013 18:04:37 +0100 Subject: [PATCH] Initial commit including a linear color smoother Former-commit-id: ffc00087996324f989e56dc5c95ab734c7c86dfa --- include/hyperion/Hyperion.h | 5 +- include/hyperion/LedDevice.h | 3 + libsrc/hyperion/CMakeLists.txt | 3 + libsrc/hyperion/Hyperion.cpp | 23 +++++- libsrc/hyperion/LedDeviceTest.cpp | 5 ++ libsrc/hyperion/LedDeviceTest.h | 3 + libsrc/hyperion/LedDeviceWs2801.cpp | 10 ++- libsrc/hyperion/LedDeviceWs2801.h | 6 ++ libsrc/hyperion/LinearColorSmoothing.cpp | 91 ++++++++++++++++++++++++ libsrc/hyperion/LinearColorSmoothing.h | 72 +++++++++++++++++++ src/hyperiond/hyperiond.cpp | 3 - 11 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 libsrc/hyperion/LinearColorSmoothing.cpp create mode 100644 libsrc/hyperion/LinearColorSmoothing.h diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 51cd8d60..7718f205 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -135,10 +135,11 @@ public: /// const InputInfo& getPriorityInfo(const int priority) const; - static LedDevice * constructDevice(const Json::Value & deviceConfig); + static LedDevice * createDevice(const Json::Value & deviceConfig); static LedString createLedString(const Json::Value & ledsConfig); static HsvTransform * createHsvTransform(const Json::Value & hsvConfig); static ColorTransform * createColorTransform(const Json::Value & colorConfig); + static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); private slots: /// @@ -175,7 +176,7 @@ private: bool _haveBgrOutput; /// The actual LedDevice - LedDevice* _device; + LedDevice * _device; /// The timer for handling priority channel timeouts QTimer _timer; diff --git a/include/hyperion/LedDevice.h b/include/hyperion/LedDevice.h index 083d113b..4539fc28 100644 --- a/include/hyperion/LedDevice.h +++ b/include/hyperion/LedDevice.h @@ -29,4 +29,7 @@ public: /// @return Zero on success else negative /// virtual int write(const std::vector& ledValues) = 0; + + /// Switch the leds off + virtual int switchOff() = 0; }; diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 4905852b..f881e8b8 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -6,6 +6,8 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) # Group the headers that go through the MOC compiler SET(Hyperion_QT_HEADERS ${CURRENT_HEADER_DIR}/Hyperion.h + + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h ) SET(Hyperion_HEADERS @@ -34,6 +36,7 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp ${CURRENT_SOURCE_DIR}/LedDeviceWs2801.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp ) set(Hyperion_RESOURCES diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index ee27f4c6..24825ebe 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -13,10 +13,12 @@ #include "LedDeviceWs2801.h" #include "LedDeviceTest.h" +#include "LinearColorSmoothing.h" + #include #include -LedDevice* Hyperion::constructDevice(const Json::Value& deviceConfig) +LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) { std::cout << "Device configuration: " << deviceConfig << std::endl; LedDevice* device = nullptr; @@ -36,6 +38,7 @@ LedDevice* Hyperion::constructDevice(const Json::Value& deviceConfig) } else { + std::cout << "Unable to create device" << std::endl; // Unknown / Unimplemented device } return device; @@ -59,6 +62,7 @@ ColorTransform* Hyperion::createColorTransform(const Json::Value& colorConfig) ColorTransform* transform = new ColorTransform(threshold, gamma, blacklevel, whitelevel); return transform; } + LedString Hyperion::createLedString(const Json::Value& ledsConfig) { LedString ledString; @@ -89,6 +93,12 @@ LedString Hyperion::createLedString(const Json::Value& ledsConfig) return ledString; } +LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice) +{ + //return new LinearColorSmoothing(ledDevice, 20.0, .3); + return ledDevice; +} + Hyperion::Hyperion(const Json::Value &jsonConfig) : _ledString(createLedString(jsonConfig["leds"])), _muxer(_ledString.leds().size()), @@ -97,11 +107,16 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) : _greenTransform(createColorTransform(jsonConfig["color"]["green"])), _blueTransform(createColorTransform(jsonConfig["color"]["blue"])), _haveBgrOutput(jsonConfig["device"].get("bgr-output", false).asBool()), - _device(constructDevice(jsonConfig["device"])), + _device(createDevice(jsonConfig["device"])), _timer() { + // initialize the image processor factory ImageProcessorFactory::getInstance().init(_ledString, jsonConfig["blackborderdetector"].get("enable", true).asBool()); + // initialize the color smoothing filter + _device = createColorSmoothing(jsonConfig["smoothing"], _device); + + // setup the timer _timer.setSingleShot(true); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); @@ -112,6 +127,10 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) : Hyperion::~Hyperion() { + // switch off all leds + clearall(); + _device->switchOff(); + // Delete the Led-String delete _device; diff --git a/libsrc/hyperion/LedDeviceTest.cpp b/libsrc/hyperion/LedDeviceTest.cpp index fa815a77..49c31c07 100644 --- a/libsrc/hyperion/LedDeviceTest.cpp +++ b/libsrc/hyperion/LedDeviceTest.cpp @@ -24,3 +24,8 @@ int LedDeviceTest::write(const std::vector & ledValues) return 0; } + +int LedDeviceTest::switchOff() +{ + return 0; +} diff --git a/libsrc/hyperion/LedDeviceTest.h b/libsrc/hyperion/LedDeviceTest.h index 473a4855..9e3c026e 100644 --- a/libsrc/hyperion/LedDeviceTest.h +++ b/libsrc/hyperion/LedDeviceTest.h @@ -32,6 +32,9 @@ public: /// virtual int write(const std::vector & ledValues); + /// Switch the leds off + virtual int switchOff(); + private: /// The outputstream std::ofstream _ofs; diff --git a/libsrc/hyperion/LedDeviceWs2801.cpp b/libsrc/hyperion/LedDeviceWs2801.cpp index aa474242..b4d60013 100644 --- a/libsrc/hyperion/LedDeviceWs2801.cpp +++ b/libsrc/hyperion/LedDeviceWs2801.cpp @@ -15,7 +15,8 @@ LedDeviceWs2801::LedDeviceWs2801(const std::string& outputDevice, const unsigned baudrate) : mDeviceName(outputDevice), mBaudRate_Hz(baudrate), - mFid(-1) + mFid(-1), + mLedCount(0) { memset(&spi, 0, sizeof(spi)); @@ -62,6 +63,8 @@ int LedDeviceWs2801::open() int LedDeviceWs2801::write(const std::vector &ledValues) { + mLedCount = ledValues.size(); + if (mFid < 0) { std::cerr << "Can not write to device which is open." << std::endl; @@ -81,3 +84,8 @@ int LedDeviceWs2801::write(const std::vector &ledValues) return retVal; } + +int LedDeviceWs2801::switchOff() +{ + return write(std::vector(mLedCount, RgbColor::BLACK)); +} diff --git a/libsrc/hyperion/LedDeviceWs2801.h b/libsrc/hyperion/LedDeviceWs2801.h index 833666a3..2aed1b4b 100644 --- a/libsrc/hyperion/LedDeviceWs2801.h +++ b/libsrc/hyperion/LedDeviceWs2801.h @@ -44,6 +44,9 @@ public: /// virtual int write(const std::vector &ledValues); + /// Switch the leds off + virtual int switchOff(); + private: /// The name of the output device const std::string mDeviceName; @@ -56,4 +59,7 @@ private: spi_ioc_transfer spi; /// The 'latch' time for latching the shifted-value into the leds timespec latchTime; + + /// the number of leds (needed when switching off) + size_t mLedCount; }; diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp new file mode 100644 index 00000000..e591921b --- /dev/null +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -0,0 +1,91 @@ +// Qt includes +#include + +#include "LinearColorSmoothing.h" + +LinearColorSmoothing::LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency, double settlingTime) : + QObject(), + LedDevice(), + _ledDevice(ledDevice), + _updateInterval(1000.0 / ledUpdateFrequency), + _settlingTime(1000 * settlingTime), + _timer() +{ + _timer.setSingleShot(false); + _timer.setInterval(_updateInterval); + + connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds())); +} + +LinearColorSmoothing::~LinearColorSmoothing() +{ + delete _ledDevice; +} + +int LinearColorSmoothing::write(const std::vector &ledValues) +{ + // received a new target color + if (_previousValues.size() == 0) + { + // not initialized yet + _targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime; + _targetValues = ledValues; + + _previousTime = QDateTime::currentMSecsSinceEpoch(); + _previousValues = ledValues; + _timer.start(); + } + else + { + _targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime; + memcpy(_targetValues.data(), ledValues.data(), ledValues.size() * sizeof(RgbColor)); + } + + return 0; +} + +int LinearColorSmoothing::switchOff() +{ + // stop smoothing filter + _timer.stop(); + + // return to uninitialized state + _previousValues.clear(); + _previousTime = 0; + _targetValues.clear(); + _targetTime = 0; + + // finally switch off all leds + return _ledDevice->switchOff(); +} + +void LinearColorSmoothing::updateLeds() +{ + int64_t now = QDateTime::currentMSecsSinceEpoch(); + int deltaTime = _targetTime - now; + + if (deltaTime < 0) + { + memcpy(_previousValues.data(), _targetValues.data(), _targetValues.size() * sizeof(RgbColor)); + _previousTime = now; + + _ledDevice->write(_previousValues); + } + else + { + float k = 1.0f - 1.0f * deltaTime / (_targetTime - _previousTime); + + for (size_t i = 0; i < _previousValues.size(); ++i) + { + RgbColor & prev = _previousValues[i]; + RgbColor & target = _targetValues[i]; + + prev.red += k * (target.red - prev.red); + prev.green += k * (target.green - prev.green); + prev.blue += k * (target.blue - prev.blue); + } + _previousTime = now; + + _ledDevice->write(_previousValues); + } +} diff --git a/libsrc/hyperion/LinearColorSmoothing.h b/libsrc/hyperion/LinearColorSmoothing.h new file mode 100644 index 00000000..4aaafe86 --- /dev/null +++ b/libsrc/hyperion/LinearColorSmoothing.h @@ -0,0 +1,72 @@ +#pragma once + +// STL includes +#include +#include + +// Qt includes +#include + +// Linux-SPI includes +#include + +// hyperion incluse +#include + +/// Linear Smooting class +/// +/// This class processes the requested led values and forwards them to the device after applying +/// a linear smoothing effect. This class can be handled as a generic LedDevice. +class LinearColorSmoothing : public QObject, public LedDevice +{ + Q_OBJECT + +public: + /// Constructor + /// @param LedDevice the led device + /// @param LedUpdatFrequency The frequency at which the leds will be updated (Hz) + /// @param settingTime The time after which the updated led values have been fully applied (sec) + LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency, double settlingTime); + + /// Destructor + virtual ~LinearColorSmoothing(); + + /// write updated values as input for the smoothing filter + /// + /// @param ledValues The color-value per led + /// @return Zero on succes else negative + /// + virtual int write(const std::vector &ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private slots: + /// Timer callback which writes updated led values to the led device + void updateLeds(); + +private: + /// The led device + LedDevice * _ledDevice; + + /// The interval at which to update the leds (msec) + const int64_t _updateInterval; + + /// The time after which the updated led values have been fully applied (msec) + const int64_t _settlingTime; + + /// The Qt timer object + QTimer _timer; + + /// The timestamp at which the target data should be fully applied + int64_t _targetTime; + + /// The target led data + std::vector _targetValues; + + /// The timestamp of the previously written led data + int64_t _previousTime; + + /// The previously written led data + std::vector _previousValues; +}; diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index c4babb42..8d17d92d 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -157,9 +157,6 @@ int main(int argc, char** argv) int rc = app.exec(); std::cout << "Application closed with code " << rc << std::endl; - // Clear all colors (switchting off all leds) - hyperion.clearall(); - // Delete all component delete bootSequence; delete dispmanx;