huge advancements (working themes, workin on noise for demo)

master
Yorick GEOFFRE 2 years ago
parent 74aba4f4fa
commit e7230714f3

@ -0,0 +1,37 @@
#include "colorattributer.h"
Q_INVOKABLE QColor ColorAttributer::encode(const float& value) {
if(myColors.size() < 2)
return QColor();
float normalizedValue = (value - minValue) / (maxValue - minValue);
auto upper = myColors.lower_bound(normalizedValue);
auto lower = upper;
if(upper == myColors.begin()) {
return upper->second;
}
else if(upper == myColors.end()) {
--lower;
return lower->second;
}
else {
--lower;
float fraction = (normalizedValue - lower->first) / (upper->first - lower->first);
return QColor::fromRgbF(
lower->second.redF() + fraction * (upper->second.redF() - lower->second.redF()),
lower->second.greenF() + fraction * (upper->second.greenF() - lower->second.greenF()),
lower->second.blueF() + fraction * (upper->second.blueF() - lower->second.blueF())
);
}
}
void ColorAttributer::addColor(const float& value, const QColor& color) {
if (value >= 0.0 && value <= 1.0) {
myColors[value] = color;
} else {
return; //invalid color mapping (0->1)
}
}

@ -0,0 +1,23 @@
#ifndef COLORATTRIBUTER_H
#define COLORATTRIBUTER_H
#include <QObject>
#include <QVector>
#include <QColor>
#include <QString>
class ColorAttributer : public QObject
{
Q_OBJECT
protected:
std::map<float, QColor> myColors;
float minValue, maxValue;
QString myName;
public:
ColorAttributer(QString name, const float& minValue = -3000000.0f, const float& maxValue = -1000000.0f) : minValue(minValue), maxValue(maxValue), myName(name) {}
Q_INVOKABLE inline QString getName() const {return myName;}
Q_INVOKABLE void addColor(const float& value, const QColor& color);
Q_INVOKABLE QColor encode(const float& value);
};
#endif // COLORATTRIBUTER_H

@ -3,8 +3,9 @@
#include <QObject>
class Command
class Command : public QObject
{
Q_OBJECT
public:
Command();
virtual void exec() = 0;

@ -6,12 +6,12 @@
#include "command.h"
#include "src/MLX90640_API.h"
class DataPollCommand : public QObject, public Command
class DtPollCommand : public Command
{
protected:
MLX90640* _thermal;
public:
DataPollCommand(MLX90640* thermal) : _thermal(thermal){}
DtPollCommand(MLX90640* thermal) : _thermal(thermal){}
void exec() override {_thermal->getData();}
};

@ -0,0 +1,6 @@
#include "perlin.h"
Perlin::Perlin()
{
}

@ -0,0 +1,52 @@
#ifndef PERLIN_H
#define PERLIN_H
#include <QObject>
#include <math.h>
#include <QVector>
class Perlin
{
public:
Perlin();
float interpolate(float a, float b, float x) {
float ft = x * 3.1415927;
float f = (1 - cos(ft)) * 0.5;
return a * (1 - f) + b * f;
}
float noise(int x, int y, float t, int seed) {
int n = x + y * 57 + int(t) * 131 + seed;
n = (n << 13) ^ n;
return (1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
float perlinNoise(float x, float y, float t, int seed) {
int wholePartX = (int)x;
float fractionalPartX = x - wholePartX;
int wholePartY = (int)y;
float fractionalPartY = y - wholePartY;
float v1 = noise(wholePartX, wholePartY, t, seed);
float v2 = noise(wholePartX + 1, wholePartY, t, seed);
float v3 = noise(wholePartX, wholePartY + 1, t, seed);
float v4 = noise(wholePartX + 1, wholePartY + 1, t, seed);
float i1 = interpolate(v1, v2, fractionalPartX);
float i2 = interpolate(v3, v4, fractionalPartX);
return interpolate(i1, i2, fractionalPartY);
}
void fillWithPerlinNoise(QVector<float>& vector, int width, int height, int seed) {
float t = 0.0f;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
float noise = perlinNoise(x / (float)width, y / (float)height, t, seed);
vector[(y * width + x)] = noise;
}
t += 0.01f; // flow speed variable
}
}
};
#endif // PERLIN_H

@ -1,24 +1,36 @@
#include "pollingtimer.h"
#include <chrono>
void PollingTimer::start(){
shouldRun = true;
}
#include "pollingtimer.h"
#include <chrono>
void PollingTimer::stop(){
shouldRun = false;
void PollingTimer::doLoop() {
while (shouldRun) {
auto start = std::chrono::high_resolution_clock::now();
_c->exec();
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
if (elapsed < this->timeout) {
std::this_thread::sleep_for(std::chrono::milliseconds(this->timeout - elapsed));
}
fprintf(stderr, "time elapsed: %lld",elapsed);
}
}
void PollingTimer::doLoop(){
std::chrono::steady_clock::time_point begin;
std::chrono::steady_clock::time_point end;
void PollingTimer::start() {
if (myThread != nullptr) {
stop();
}
shouldRun = true;
myThread = new std::thread([this]() { this->doLoop(); });
}
while(shouldRun){
begin = std::chrono::steady_clock::now();
_c->exec();
end = std::chrono::steady_clock::now();
std::this_thread::sleep_for(
std::chrono::microseconds((this->timeout)
- std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()));
void PollingTimer::stop() {
if (myThread != nullptr) {
shouldRun = false;
myThread->join();
delete myThread;
myThread = nullptr;
}
}

@ -4,23 +4,27 @@
#include <QObject>
#include <atomic>
#include <thread>
#include <QThread>
#include "command.h"
class PollingTimer
class PollingTimer : public QObject
{
Q_OBJECT
protected:
std::atomic<bool> shouldRun;
std::thread* myThread;
std::thread* myThread = nullptr;
Command* _c;
unsigned int timeout;
void doLoop();
public:
QThread workerThread;
PollingTimer(Command* c) : shouldRun(false), _c(c){}
void start();
void stop();
Q_INVOKABLE void start();
Q_INVOKABLE void stop();
Q_INVOKABLE inline void setDelay(const unsigned int& timeout) {this->timeout = timeout;}
};
#endif // POLLINGTIMER_H

@ -19,17 +19,14 @@ Page {
onClicked: {
if(mainPage.isInitialized){
mainPage.isInitialized = false;
pollingTimer.stop();
polling_timer.stop();
}else{
mainPage.isInitialized = true;
mlx90640.fuzzyInit();
pollingTimer.start();
polling_timer.start();
}
}
TextArea{
text: mlx90640.getImageVectAt(100)
}
}
Grid {
@ -42,36 +39,38 @@ Page {
id: rectangle
width: 15
height: 15
property int rowIndex: Math.floor(index / 32)
property int columnIndex: index % 32
property int invertedIndex: (23 - rowIndex) * 32 + columnIndex
property real value: mlx90640.getImageVectAt(invertedIndex)
property real minValue: -3000000.000000
property real maxValue: -1000000.000000
property real hue: 0.67 - ((value - minValue) / (maxValue - minValue) * 0.67)
property color sensorColor: Qt.hsva(hue, 1, 1, 1)
color: sensorColor
color: thermalRenderer.getDataForIndex(index)
}
}
Connections {
target: mlx90640
onDataReady: {
// force the repeater to update its items
repeater.model = [];
repeater.model = 768;
target: thermalRenderer
onDataChanged: {
for (var i = 0; i < repeater.count; i++) {
var item = repeater.itemAt(i);
if (item) {
item.color = thermalRenderer.getDataForIndex(i);
}
}
}
}
}
Timer {
id: pollingTimer
interval: 70
repeat: true
onTriggered: {
mlx90640.getData();
ComboBox{
id: themes
anchors.horizontalCenter: parent.horizontalCenter
label: "Theme: "
currentIndex: 2
menu: ContextMenu
{
MenuItem { text: "hotiron" }
MenuItem { text: "rainbow" }
MenuItem { text: "gray" }
}
}
onCurrentIndexChanged: {
thermalRenderer.setActiveAttributer(themes.currentIndex);
}
}
}
}
}

@ -14,8 +14,8 @@
* limitations under the License.
*
*/
#include "mlx90640_I2C_Driver.h"
#include "mlx90640_API.h"
#include "MLX90640_I2C_Driver.h"
#include <math.h>
int MLX90640::mlx90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData)

@ -22,6 +22,9 @@
#include <QVector>
#include <array>
#include <unistd.h>
#include "perlin.h"
#define mlx90640_DEV_ADDR 0x33
#define mlx90640_NO_ERROR 0
@ -142,6 +145,7 @@ public:
uint16_t outlierPixels[5];
} paramsMLX90640;
unsigned char slaveAddress = 0x33;
paramsMLX90640 mlx90640;
@ -193,19 +197,21 @@ public:
uint16_t mlx90640Frame[834];
float mlx90640Image[768];
int status;
//fprintf(stderr, "trigger measurement...\n");
//status = mlx90640_TriggerMeasurement (0x33);
fprintf(stderr, "sync frame...\n");
status = mlx90640_SynchFrame(0x33);
status = mlx90640_GetFrameData (0x33, mlx90640Frame);
mlx90640_GetImage(mlx90640Frame, &mlx90640, mlx90640Image);
for (int i = 0; i < 767; ++i) {
imageVect[i] = mlx90640Image[i];
fprintf(stderr, "ImageVect[%d]: %f\n", i, imageVect[i]);
if (status == 0) {
mlx90640_GetImage(mlx90640Frame, &mlx90640, mlx90640Image);
for (int i = 0; i < 767; ++i) {
imageVect[i] = mlx90640Image[i];
}
} else {
Perlin perlin;
perlin.fillWithPerlinNoise(imageVect, 32, 24, 0xBEEF);
}
emit dataReady();
emit dataReady(imageVect);
}
Q_INVOKABLE float getImageVectAt(int index) {
@ -241,8 +247,9 @@ protected:
int IsPixelBad(uint16_t pixel,paramsMLX90640 *params);
int ValidateFrameData(uint16_t *frameData);
int ValidateAuxData(uint16_t *auxData);
Perlin perlin;
signals:
void dataReady();
void dataReady(QVector<float> data);
void initializedChanged();
};
#endif

@ -27,11 +27,13 @@ I2cif::I2cif(QObject *parent) :
{
m_probingResult = QStringList();
m_readResult = "Nothing yet";
tohVddSet("on");
emit tohVddStatusChanged();
}
I2cif::~I2cif()
{
tohVddSet("off");
}
@ -112,7 +114,7 @@ Q_INVOKABLE void I2cif::i2cWrite(const uint8_t &slaveAddr, const uint16_t &write
const char* devNameChar = "/dev/i2c-1";
fprintf(stderr, "writing to address %02x: ", slaveAddr);
//fprintf(stderr, "writing to address %02x: ", slaveAddr);
if ((file = open (devNameChar, O_RDWR)) < 0)
{
@ -134,12 +136,12 @@ Q_INVOKABLE void I2cif::i2cWrite(const uint8_t &slaveAddr, const uint16_t &write
buf[2] = (data >> 8) & 0xFF; // High byte
buf[3] = data & 0xFF; // Low byte
fprintf(stderr, "Data to be written: ");
/*fprintf(stderr, "Data to be written: ");
for (int i = 0; i < 4; i++) {
fprintf(stderr, "%02x ", buf[i]);
}
fprintf(stderr, "\n");
*/
/* write the data */
if (write(file, buf, 4) != 4)
{
@ -151,7 +153,7 @@ Q_INVOKABLE void I2cif::i2cWrite(const uint8_t &slaveAddr, const uint16_t &write
close(file);
fprintf(stderr,"write ok\n");
//fprintf(stderr,"write ok\n");
emit i2cWriteOk();
}
@ -190,7 +192,7 @@ void I2cif::i2cRead(const uint8_t &slaveAddr, const uint16_t &startAddress, cons
i2c_data.msgs[1].addr = slaveAddr;
i2c_data.msgs[1].flags = I2C_M_RD;
constexpr uint16_t ChunkSize = 32; // 64 bytes (32 uint16_t)
constexpr uint16_t ChunkSize = 32; // 192 bytes (96 uint16_t)
uint16_t chunks = nMemAddressRead / ChunkSize;
uint16_t remainder = nMemAddressRead % ChunkSize;
@ -216,21 +218,21 @@ void I2cif::i2cRead(const uint8_t &slaveAddr, const uint16_t &startAddress, cons
return;
}
fprintf(stderr, "read ");
//fprintf(stderr, "read ");
for (uint16_t i = 0; i < length; ++i) {
uint16_t index = chunk * ChunkSize + i;
data[index] = ((buf[2 * index] << 8) == 0xFF ? 0x00 : (buf[2 * index] << 8)) | buf[2 * index + 1];
m_readResult = m_readResult + conv.toHex(data[index], 4) + " ";
fprintf(stderr, "%04x ", data[index]);
//fprintf(stderr, "%04x ", data[index]);
}
fprintf(stderr, "\n");
//fprintf(stderr, "\n");
// Wait 0.4ms before next chunk
usleep(400);
addr += ChunkSize;
}
fprintf(stderr, "\n");
//fprintf(stderr, "\n");
delete[] buf;
emit i2cReadResultChanged();
close(file);

@ -1,31 +1,76 @@
 #ifdef QT_QML_DEBUG
#include "conv.h"
#include "i2cif.h"
#include "mlx90640_API.h"
#include "i2cif.h"
#include "mlx90640_API.h"
#include "ColorAttributer.h"
#include "ThermalDataRenderer.h"
#include "DataPollCommand.h"
#include "PollingTimer.h"
#include "conv.h"
#include "datapollcommand.h"
#include <QtQuick>
#endif
#include <QString>
#include <sailfishapp.h>
#include <QtQuick>
#include <sailfishapp.h>
int main(int argc, char *argv[])
{
qmlRegisterType<I2cif>("harbour.i2ctool.I2cif", 1, 0, "I2cif");
qmlRegisterType<Conv>("harbour.i2ctool.Conv", 1, 0, "Conv");
qmlRegisterType<MLX90640>("melexis.driver", 1, 0, "MLX90640");
int main(int argc, char *argv[]) {
// Register types and create instances
qmlRegisterType<I2cif>("harbour.i2ctool.I2cif", 1, 0, "I2cif");
qmlRegisterType<Conv>("harbour.i2ctool.Conv", 1, 0, "Conv");
qmlRegisterType<MLX90640>("melexis.driver", 1, 0, "MLX90640");
qmlRegisterType<ThermalDataRenderer>("my.app", 1, 0, "ThermalDataRenderer");
//qmlRegisterType<ColorAttributer>("my.app", 1, 0, "ColorAttributer");
// Create instance of MLX90640
MLX90640 mlx90640;
MLX90640 mlx90640;
// Initialize application and view
QGuiApplication app(argc, argv);
QQuickView view;
ThermalDataRenderer* thermalRenderer = new ThermalDataRenderer();
MLX90640 thermal;
view.rootContext()->setContextProperty("mlx90640",&thermal);
auto hotIron = std::make_shared<ColorAttributer>("hot iron");
hotIron->addColor(0.0, QColor(0, 0, 0)); // Black
hotIron->addColor(0.35, QColor(128, 0, 128)); // Magenta
hotIron->addColor(0.5, QColor(255, 69, 0)); // OrangeRed
hotIron->addColor(1.0, QColor(255, 255, 0)); // Yellow
view.setSource(SailfishApp::pathToMainQml());
view.show();
auto rainbow = std::make_shared<ColorAttributer>("rainbow");
rainbow->addColor(0.0, QColor(255, 0, 0)); // Red
rainbow->addColor(0.33, QColor(0, 255, 0)); // Green
rainbow->addColor(0.67, QColor(0, 0, 255)); // Blue
rainbow->addColor(1.0, QColor(255, 0, 255)); // Magenta
return app.exec();
}
auto grayscale = std::make_shared<ColorAttributer>("grayscale");
grayscale->addColor(0.0, QColor(0, 0, 0)); // Black
grayscale->addColor(0.25, QColor(75, 75, 75)); // dark gray
grayscale->addColor(0.5, QColor(150, 150, 150)); // gray
grayscale->addColor(0.75, QColor(200, 200, 200)); // light gray
grayscale->addColor(1.0, QColor(255, 255, 255)); // White
thermalRenderer->addAttributer(hotIron);
thermalRenderer->addAttributer(rainbow);
thermalRenderer->addAttributer(grayscale);
thermalRenderer->setActiveAttributer(2);//enable hotiron
QGuiApplication app(argc, argv);
QQuickView view;
MLX90640* thermal = new MLX90640();
DtPollCommand* dtpoll = new DtPollCommand(thermal);
PollingTimer* pt = new PollingTimer(dtpoll);
pt->moveToThread(&(pt->workerThread));
pt->workerThread.start();
qRegisterMetaType<QVector<float>>("QVector<float>");
//when a frame is ready, pass it to the renderer
QObject::connect(thermal, SIGNAL(dataReady(QVector<float>)), thermalRenderer, SLOT(receiveNewData(QVector<float>)), Qt::ConnectionType::DirectConnection);
pt->setDelay(100);
view.rootContext()->setContextProperty("polling_timer",pt);
view.rootContext()->setContextProperty("mlx90640", thermal);
view.rootContext()->setContextProperty("thermalRenderer", thermalRenderer);
view.setSource(SailfishApp::pathToMainQml());
view.show();
return app.exec();
}

@ -1,6 +1,23 @@
#include "thermaldatarenderer.h"
#include <chrono>
void ThermalDataRenderer::receiveNewData(QVector<float> data) {
auto start = std::chrono::high_resolution_clock::now();
//fprintf(stderr, "in renderer");
//fprintf(stderr, "renderer received data of length: %d\n",data.length());
ThermalDataRenderer::ThermalDataRenderer()
{
if(activeAttributer >= attributers.size()){
fprintf(stderr, "attr size mismatch: %d; %d",activeAttributer,attributers.size());
activeAttributer = 0;
}
if(renderBuffer.size() < data.size())
renderBuffer = QVector<QColor>(data.size()+1);
for (int i = 0; i < data.size() && i < renderBuffer.size(); ++i) {
renderBuffer[i] = attributers[activeAttributer]->encode(data[i]);
}
emit dataChanged();
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
//fprintf(stderr,"renderer time: %lld\n",elapsed);
}

@ -4,15 +4,74 @@
#include <QObject>
#include <QVector>
#include <QColor>
#include <QThread>
class ThermalDataRenderer
#include <memory>
#include <exception>
#include "colorattributer.h"
typedef std::shared_ptr<ColorAttributer> ColorAttributerPtr;
class ThermalDataRenderer : public QObject
{
Q_OBJECT
bool direct = false;
QVector<QColor> renderBuffer;
QVector<QColor> renderBuffer; //size = 768
QVector<ColorAttributerPtr> attributers;
unsigned int activeAttributer;
float minValue = 0.0f;
float maxValue = 0.0f;
float maxValue = 1.0f;
public:
ThermalDataRenderer();
Q_PROPERTY(QVector<std::shared_ptr<ColorAttributer>> attributers READ getAttributers NOTIFY attributersChanged)
QThread workerThread;
ThermalDataRenderer() : renderBuffer(768){}
Q_INVOKABLE QColor getDataForIndex(const int& index){
if(index < renderBuffer.length() && index >= 0)
{
return renderBuffer.at(index);
}else
return QColor(0,0,0);
}
inline void addAttributer(ColorAttributerPtr attributer) {
attributers.push_back(attributer);
}
inline QVector<ColorAttributerPtr> getAttributers(){ return attributers; }
Q_INVOKABLE inline void setActiveAttributer(const int& index) {
if(index < attributers.size()) {
activeAttributer = index;
}
}
inline unsigned int getActiveAttributer() const {
return activeAttributer;
}
inline void setMinValue(float value) {
minValue = value;
}
inline float getMinValue() const {
return minValue;
}
inline void setMaxValue(float value) {
maxValue = value;
}
inline float getMaxValue() const {
return maxValue;
}
public: signals:
void attributersChanged();
void dataChanged();
public slots:
void receiveNewData(QVector<float> data);
};
#endif // THERMALDATARENDERER_H

@ -15,7 +15,9 @@ TARGET = thermi2c
CONFIG += sailfishapp
SOURCES += src/thermi2c.cpp \
colorattributer.cpp \
command.cpp \
perlin.cpp \
pollingtimer.cpp \
src/mlx90640_API.cpp \
src/mlx90640_I2C_Driver.cpp \
@ -47,8 +49,10 @@ CONFIG += sailfishapp_i18n
TRANSLATIONS += translations/thermi2c-de.ts
HEADERS += \
colorattributer.h \
command.h \
datapollcommand.h \
perlin.h \
pollingtimer.h \
src/mlx90640_API.h \
src/mlx90640_I2C_Driver.h \

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.15.2, 2023-05-30T15:23:11. -->
<!-- Written by QtCreator 4.15.2, 2023-06-12T17:01:44. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@ -85,6 +85,9 @@
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
<value type="bool" key="UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>

Loading…
Cancel
Save