From 5e6df9ccf832d166bd231297b521394beec1fec6 Mon Sep 17 00:00:00 2001 From: Nikita Kostovsky Date: Mon, 2 Dec 2024 19:42:15 +0100 Subject: line search --- CMakeLists.txt | 9 +- LibCamera.cpp | 532 ++++++++++++++++++++++++++++---------------------------- LibCamera.h | 131 +++++++------- fuck_intel.h | 9 + genetic_algos.h | 2 +- imagealgos.cpp | 228 +++++++++++++++++++++++- imagealgos.h | 10 ++ main.cpp | 432 +++++++++++++++++++++++++++++++++++++-------- 8 files changed, 949 insertions(+), 404 deletions(-) create mode 100644 fuck_intel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 83ef86c..e07bf77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ include_guard(GLOBAL) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) +set(CMAKE_AUTORCC OFF) set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) @@ -32,6 +32,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") # ...../rpi-sysroot/usr/lib/qt6/libexec/moc: error while loading shared libraries: # libpcre2-16.so.0: cannot open shared object file: No such file or directory # +# export this path for qtcreator: # LD_LIBRARY_PATH=/home/nikita/rpi/rpi-sysroot/usr/lib/aarch64-linux-gnu/:$LD_LIBRARY_PATH set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) @@ -59,6 +60,8 @@ set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std:c++latest") find_package(Qt6 6.4 REQUIRED COMPONENTS Quick HttpServer SerialPort) +find_package(TBB REQUIRED) + qt_standard_project_setup(REQUIRES 6.4) include_directories(. "${CAMERA_INCLUDE_DIRS}") @@ -82,6 +85,7 @@ qt_add_executable(apporpheus rotaryencoder.cpp printerclient.h printerclient.cpp + fuck_intel.h ) target_link_libraries(app${PROJECT_NAME} PRIVATE @@ -90,6 +94,7 @@ target_link_libraries(app${PROJECT_NAME} Qt6::HttpServer Qt6::SerialPort wiringPi + TBB::tbb ) include(GNUInstallDirs) @@ -107,4 +112,4 @@ endif() # add_compile_options(-Wall -Wextra -Wpedantic) -add_compile_options(-Ofast -fno-unroll-loops) +add_compile_options(-Ofast -fno-unroll-loops -Wall) diff --git a/LibCamera.cpp b/LibCamera.cpp index 945a8ae..1e68228 100644 --- a/LibCamera.cpp +++ b/LibCamera.cpp @@ -1,267 +1,267 @@ -#include "LibCamera.h" - -using namespace std::placeholders; - -int LibCamera::initCamera() { - int ret; - cm = std::make_unique(); - ret = cm->start(); - if (ret){ - std::cout << "Failed to start camera manager: " - << ret << std::endl; - return ret; - } - cameraId = cm->cameras()[0]->id(); - camera_ = cm->get(cameraId); - if (!camera_) { - std::cerr << "Camera " << cameraId << " not found" << std::endl; - return 1; - } - - if (camera_->acquire()) { - std::cerr << "Failed to acquire camera " << cameraId - << std::endl; - return 1; - } - camera_acquired_ = true; - return 0; -} - -char * LibCamera::getCameraId(){ - return cameraId.data(); -} - -void LibCamera::configureStill(int width, int height, PixelFormat format, int buffercount, int rotation) { - printf("Configuring still capture...\n"); - config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); - if (width && height) { - libcamera::Size size(width, height); - config_->at(0).size = size; - } - config_->at(0).pixelFormat = format; - if (buffercount) - config_->at(0).bufferCount = buffercount; - Transform transform = Transform::Identity; - bool ok; - Transform rot = transformFromRotation(rotation, &ok); - if (!ok) - throw std::runtime_error("illegal rotation value, Please use 0 or 180"); - transform = rot * transform; - if (!!(transform & Transform::Transpose)) - throw std::runtime_error("transforms requiring transpose not supported"); - // FIXME: update - // config_->transform = transform; - - CameraConfiguration::Status validation = config_->validate(); - if (validation == CameraConfiguration::Invalid) - throw std::runtime_error("failed to valid stream configurations"); - else if (validation == CameraConfiguration::Adjusted) - std::cout << "Stream configuration adjusted" << std::endl; - - printf("Still capture setup complete\n"); -} - -int LibCamera::startCamera() { - int ret; - ret = camera_->configure(config_.get()); - if (ret < 0) { - std::cout << "Failed to configure camera" << std::endl; - return ret; - } - - camera_->requestCompleted.connect(this, &LibCamera::requestComplete); - - allocator_ = std::make_unique(camera_); - - return startCapture(); -} - -int LibCamera::startCapture() { - int ret; - unsigned int nbuffers = UINT_MAX; - for (StreamConfiguration &cfg : *config_) { - ret = allocator_->allocate(cfg.stream()); - if (ret < 0) { - std::cerr << "Can't allocate buffers" << std::endl; - return -ENOMEM; - } - - unsigned int allocated = allocator_->buffers(cfg.stream()).size(); - nbuffers = std::min(nbuffers, allocated); - } - - for (unsigned int i = 0; i < nbuffers; i++) { - std::unique_ptr request = camera_->createRequest(); - if (!request) { - std::cerr << "Can't create request" << std::endl; - return -ENOMEM; - } - - for (StreamConfiguration &cfg : *config_) { - Stream *stream = cfg.stream(); - const std::vector> &buffers = - allocator_->buffers(stream); - const std::unique_ptr &buffer = buffers[i]; - - ret = request->addBuffer(stream, buffer.get()); - if (ret < 0) { - std::cerr << "Can't set buffer for request" - << std::endl; - return ret; - } - for (const FrameBuffer::Plane &plane : buffer->planes()) { - void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, - plane.fd.get(), 0); - mappedBuffers_[plane.fd.get()] = - std::make_pair(memory, plane.length); - } - } - - requests_.push_back(std::move(request)); - } - - ret = camera_->start(&this->controls_); - // ret = camera_->start(); - if (ret) { - std::cout << "Failed to start capture" << std::endl; - return ret; - } - controls_.clear(); - camera_started_ = true; - for (std::unique_ptr &request : requests_) { - ret = queueRequest(request.get()); - if (ret < 0) { - std::cerr << "Can't queue request" << std::endl; - camera_->stop(); - return ret; - } - } - viewfinder_stream_ = config_->at(0).stream(); - return 0; -} - -void LibCamera::StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const -{ - StreamConfiguration const &cfg = stream->configuration(); - if (w) - *w = cfg.size.width; - if (h) - *h = cfg.size.height; - if (stride) - *stride = cfg.stride; -} - -Stream *LibCamera::VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const -{ - StreamDimensions(viewfinder_stream_, w, h, stride); - return viewfinder_stream_; -} - -int LibCamera::queueRequest(Request *request) { - std::lock_guard stop_lock(camera_stop_mutex_); - if (!camera_started_) - return -1; - { - std::lock_guard lock(control_mutex_); - request->controls() = std::move(controls_); - } - return camera_->queueRequest(request); -} - -void LibCamera::requestComplete(Request *request) { - if (request->status() == Request::RequestCancelled) - return; - processRequest(request); -} - -void LibCamera::processRequest(Request *request) { - requestQueue.push(request); -} - -void LibCamera::returnFrameBuffer(LibcameraOutData frameData) { - uint64_t request = frameData.request; - Request * req = (Request *)request; - req->reuse(Request::ReuseBuffers); - queueRequest(req); -} - -bool LibCamera::readFrame(LibcameraOutData *frameData){ - std::lock_guard lock(free_requests_mutex_); - // int w, h, stride; - if (!requestQueue.empty()){ - Request *request = this->requestQueue.front(); - - const Request::BufferMap &buffers = request->buffers(); - for (auto it = buffers.begin(); it != buffers.end(); ++it) { - FrameBuffer *buffer = it->second; - for (unsigned int i = 0; i < buffer->planes().size(); ++i) { - const FrameBuffer::Plane &plane = buffer->planes()[i]; - const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; +// #include "LibCamera.h" + +// using namespace std::placeholders; + +// int LibCamera::initCamera() { +// int ret; +// cm = std::make_unique(); +// ret = cm->start(); +// if (ret){ +// std::cout << "Failed to start camera manager: " +// << ret << std::endl; +// return ret; +// } +// cameraId = cm->cameras()[0]->id(); +// camera_ = cm->get(cameraId); +// if (!camera_) { +// std::cerr << "Camera " << cameraId << " not found" << std::endl; +// return 1; +// } + +// if (camera_->acquire()) { +// std::cerr << "Failed to acquire camera " << cameraId +// << std::endl; +// return 1; +// } +// camera_acquired_ = true; +// return 0; +// } + +// char * LibCamera::getCameraId(){ +// return cameraId.data(); +// } + +// void LibCamera::configureStill(int width, int height, PixelFormat format, int buffercount, int rotation) { +// printf("Configuring still capture...\n"); +// config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); +// if (width && height) { +// libcamera::Size size(width, height); +// config_->at(0).size = size; +// } +// config_->at(0).pixelFormat = format; +// if (buffercount) +// config_->at(0).bufferCount = buffercount; +// Transform transform = Transform::Identity; +// bool ok; +// Transform rot = transformFromRotation(rotation, &ok); +// if (!ok) +// throw std::runtime_error("illegal rotation value, Please use 0 or 180"); +// transform = rot * transform; +// if (!!(transform & Transform::Transpose)) +// throw std::runtime_error("transforms requiring transpose not supported"); +// // FIXME: update +// // config_->transform = transform; + +// CameraConfiguration::Status validation = config_->validate(); +// if (validation == CameraConfiguration::Invalid) +// throw std::runtime_error("failed to valid stream configurations"); +// else if (validation == CameraConfiguration::Adjusted) +// std::cout << "Stream configuration adjusted" << std::endl; + +// printf("Still capture setup complete\n"); +// } + +// int LibCamera::startCamera() { +// int ret; +// ret = camera_->configure(config_.get()); +// if (ret < 0) { +// std::cout << "Failed to configure camera" << std::endl; +// return ret; +// } + +// camera_->requestCompleted.connect(this, &LibCamera::requestComplete); + +// allocator_ = std::make_unique(camera_); + +// return startCapture(); +// } + +// int LibCamera::startCapture() { +// int ret; +// unsigned int nbuffers = UINT_MAX; +// for (StreamConfiguration &cfg : *config_) { +// ret = allocator_->allocate(cfg.stream()); +// if (ret < 0) { +// std::cerr << "Can't allocate buffers" << std::endl; +// return -ENOMEM; +// } + +// unsigned int allocated = allocator_->buffers(cfg.stream()).size(); +// nbuffers = std::min(nbuffers, allocated); +// } + +// for (unsigned int i = 0; i < nbuffers; i++) { +// std::unique_ptr request = camera_->createRequest(); +// if (!request) { +// std::cerr << "Can't create request" << std::endl; +// return -ENOMEM; +// } + +// for (StreamConfiguration &cfg : *config_) { +// Stream *stream = cfg.stream(); +// const std::vector> &buffers = +// allocator_->buffers(stream); +// const std::unique_ptr &buffer = buffers[i]; + +// ret = request->addBuffer(stream, buffer.get()); +// if (ret < 0) { +// std::cerr << "Can't set buffer for request" +// << std::endl; +// return ret; +// } +// for (const FrameBuffer::Plane &plane : buffer->planes()) { +// void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, +// plane.fd.get(), 0); +// mappedBuffers_[plane.fd.get()] = +// std::make_pair(memory, plane.length); +// } +// } + +// requests_.push_back(std::move(request)); +// } + +// ret = camera_->start(&this->controls_); +// // ret = camera_->start(); +// if (ret) { +// std::cout << "Failed to start capture" << std::endl; +// return ret; +// } +// controls_.clear(); +// camera_started_ = true; +// for (std::unique_ptr &request : requests_) { +// ret = queueRequest(request.get()); +// if (ret < 0) { +// std::cerr << "Can't queue request" << std::endl; +// camera_->stop(); +// return ret; +// } +// } +// viewfinder_stream_ = config_->at(0).stream(); +// return 0; +// } + +// void LibCamera::StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const +// { +// StreamConfiguration const &cfg = stream->configuration(); +// if (w) +// *w = cfg.size.width; +// if (h) +// *h = cfg.size.height; +// if (stride) +// *stride = cfg.stride; +// } + +// Stream *LibCamera::VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const +// { +// StreamDimensions(viewfinder_stream_, w, h, stride); +// return viewfinder_stream_; +// } + +// int LibCamera::queueRequest(Request *request) { +// std::lock_guard stop_lock(camera_stop_mutex_); +// if (!camera_started_) +// return -1; +// { +// std::lock_guard lock(control_mutex_); +// request->controls() = std::move(controls_); +// } +// return camera_->queueRequest(request); +// } + +// void LibCamera::requestComplete(Request *request) { +// if (request->status() == Request::RequestCancelled) +// return; +// processRequest(request); +// } + +// void LibCamera::processRequest(Request *request) { +// requestQueue.push(request); +// } + +// void LibCamera::returnFrameBuffer(LibcameraOutData frameData) { +// uint64_t request = frameData.request; +// Request * req = (Request *)request; +// req->reuse(Request::ReuseBuffers); +// queueRequest(req); +// } + +// bool LibCamera::readFrame(LibcameraOutData *frameData){ +// std::lock_guard lock(free_requests_mutex_); +// // int w, h, stride; +// if (!requestQueue.empty()){ +// Request *request = this->requestQueue.front(); + +// const Request::BufferMap &buffers = request->buffers(); +// for (auto it = buffers.begin(); it != buffers.end(); ++it) { +// FrameBuffer *buffer = it->second; +// for (unsigned int i = 0; i < buffer->planes().size(); ++i) { +// const FrameBuffer::Plane &plane = buffer->planes()[i]; +// const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; - void *data = mappedBuffers_[plane.fd.get()].first; - int length = std::min(meta.bytesused, plane.length); - - frameData->size = length; - frameData->imageData = (uint8_t *)data; - } - } - this->requestQueue.pop(); - frameData->request = (uint64_t)request; - return true; - } else { - Request *request = nullptr; - frameData->request = (uint64_t)request; - return false; - } -} - -void LibCamera::set(ControlList controls){ - std::lock_guard lock(control_mutex_); - this->controls_ = std::move(controls); -} - -int LibCamera::resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation) { - stopCamera(); - configureStill(width, height, format, buffercount, rotation); - return startCamera(); -} - -void LibCamera::stopCamera() { - if (camera_){ - { - std::lock_guard lock(camera_stop_mutex_); - if (camera_started_){ - if (camera_->stop()) - throw std::runtime_error("failed to stop camera"); - camera_started_ = false; - } - } - camera_->requestCompleted.disconnect(this, &LibCamera::requestComplete); - } - while (!requestQueue.empty()) - requestQueue.pop(); - - for (auto &iter : mappedBuffers_) - { - std::pair pair_ = iter.second; - munmap(std::get<0>(pair_), std::get<1>(pair_)); - } - - mappedBuffers_.clear(); - - requests_.clear(); - - allocator_.reset(); - - controls_.clear(); -} - -void LibCamera::closeCamera(){ - if (camera_acquired_) - camera_->release(); - camera_acquired_ = false; - - camera_.reset(); - - cm.reset(); -} +// void *data = mappedBuffers_[plane.fd.get()].first; +// int length = std::min(meta.bytesused, plane.length); + +// frameData->size = length; +// frameData->imageData = (uint8_t *)data; +// } +// } +// this->requestQueue.pop(); +// frameData->request = (uint64_t)request; +// return true; +// } else { +// Request *request = nullptr; +// frameData->request = (uint64_t)request; +// return false; +// } +// } + +// void LibCamera::set(ControlList controls){ +// std::lock_guard lock(control_mutex_); +// this->controls_ = std::move(controls); +// } + +// int LibCamera::resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation) { +// stopCamera(); +// configureStill(width, height, format, buffercount, rotation); +// return startCamera(); +// } + +// void LibCamera::stopCamera() { +// if (camera_){ +// { +// std::lock_guard lock(camera_stop_mutex_); +// if (camera_started_){ +// if (camera_->stop()) +// throw std::runtime_error("failed to stop camera"); +// camera_started_ = false; +// } +// } +// camera_->requestCompleted.disconnect(this, &LibCamera::requestComplete); +// } +// while (!requestQueue.empty()) +// requestQueue.pop(); + +// for (auto &iter : mappedBuffers_) +// { +// std::pair pair_ = iter.second; +// munmap(std::get<0>(pair_), std::get<1>(pair_)); +// } + +// mappedBuffers_.clear(); + +// requests_.clear(); + +// allocator_.reset(); + +// controls_.clear(); +// } + +// void LibCamera::closeCamera(){ +// if (camera_acquired_) +// camera_->release(); +// camera_acquired_ = false; + +// camera_.reset(); + +// cm.reset(); +// } diff --git a/LibCamera.h b/LibCamera.h index 2262ab2..89cf835 100644 --- a/LibCamera.h +++ b/LibCamera.h @@ -1,19 +1,25 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#pragma once + +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +#ifdef signals +#error ("don't include this file after any qt files") +#endif #include #include @@ -26,61 +32,60 @@ #include #include #include +// using namespace libcamera; -using namespace libcamera; - -typedef struct { - uint8_t *imageData; - uint32_t size; - uint64_t request; -} LibcameraOutData; +// typedef struct { +// uint8_t *imageData; +// uint32_t size; +// uint64_t request; +// } LibcameraOutData; -class LibCamera { - public: - LibCamera(){}; - ~LibCamera(){}; +// class LibCamera { +// public: +// LibCamera(){}; +// ~LibCamera(){}; - int initCamera(); - void configureStill(int width, int height, PixelFormat format, int buffercount, int rotation); - int startCamera(); - int resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation); - bool readFrame(LibcameraOutData *frameData); - void returnFrameBuffer(LibcameraOutData frameData); +// int initCamera(); +// void configureStill(int width, int height, PixelFormat format, int buffercount, int rotation); +// int startCamera(); +// int resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation); +// bool readFrame(LibcameraOutData *frameData); +// void returnFrameBuffer(LibcameraOutData frameData); - void set(ControlList controls); - void stopCamera(); - void closeCamera(); +// void set(ControlList controls); +// void stopCamera(); +// void closeCamera(); - Stream *VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const; - char * getCameraId(); +// Stream *VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const; +// char * getCameraId(); - private: - int startCapture(); - int queueRequest(Request *request); - void requestComplete(Request *request); - void processRequest(Request *request); +// private: +// int startCapture(); +// int queueRequest(Request *request); +// void requestComplete(Request *request); +// void processRequest(Request *request); - void StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const; +// void StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const; - unsigned int cameraIndex_; - uint64_t last_; - std::unique_ptr cm; - std::shared_ptr camera_; - bool camera_acquired_ = false; - bool camera_started_ = false; - std::unique_ptr config_; - std::unique_ptr allocator_; - std::vector> requests_; - // std::map stream_; - std::map> mappedBuffers_; +// unsigned int cameraIndex_; +// uint64_t last_; +// std::unique_ptr cm; +// std::shared_ptr camera_; +// bool camera_acquired_ = false; +// bool camera_started_ = false; +// std::unique_ptr config_; +// std::unique_ptr allocator_; +// std::vector> requests_; +// // std::map stream_; +// std::map> mappedBuffers_; - std::queue requestQueue; +// std::queue requestQueue; - ControlList controls_; - std::mutex control_mutex_; - std::mutex camera_stop_mutex_; - std::mutex free_requests_mutex_; +// ControlList controls_; +// std::mutex control_mutex_; +// std::mutex camera_stop_mutex_; +// std::mutex free_requests_mutex_; - Stream *viewfinder_stream_ = nullptr; - std::string cameraId; -}; \ No newline at end of file +// Stream *viewfinder_stream_ = nullptr; +// std::string cameraId; +// }; diff --git a/fuck_intel.h b/fuck_intel.h new file mode 100644 index 0000000..a9fa7d5 --- /dev/null +++ b/fuck_intel.h @@ -0,0 +1,9 @@ +#pragma once + +#ifdef emit +#undef emit +#include +#define emit +#else +#include +#endif diff --git a/genetic_algos.h b/genetic_algos.h index 862cd34..8dea732 100644 --- a/genetic_algos.h +++ b/genetic_algos.h @@ -3,9 +3,9 @@ // #define _USE_MATH_DEFINES #include #include -// #include #include #include +// TODO: /* diff --git a/imagealgos.cpp b/imagealgos.cpp index 3e84155..b4fba4b 100644 --- a/imagealgos.cpp +++ b/imagealgos.cpp @@ -1,5 +1,7 @@ #include "imagealgos.h" +#include + #include #include #include @@ -14,6 +16,7 @@ // #include +#include "fuck_intel.h" #include "genetic_algos.h" #include "macro.h" @@ -210,7 +213,7 @@ float process_column(uint16_t (&column)[]) } result = (y2 != y1) ? - (img_height - (float(x1) - (float(y1) / (y2 - y1)))) : + (float(x1) - (float(y1) / (y2 - y1))) : std::numeric_limits::quiet_NaN(); @@ -267,11 +270,230 @@ Pixels process_columns(Image &image) // image.pixels[i] = genetic.run().a; // if (i == 0) { - // std::cout << "pixel: " << image.pixels[i] << std::endl; - // } + // std::cout << "pixel: " << image.pixels[i] << std::endl; + // } } stop_timer(process_columns); return result; } + +Pixels& Pixels::operator+=(const Pixels& other) +{ + std::transform(std::execution::par, + pixels.begin(), pixels.end(), + other.pixels.begin(), + pixels.begin(), + // [](auto& toAdd) { return dst += src; }); + std::plus<>()); + + return *this; +} + +Pixels& Pixels::operator/=(const float divider) +{ + std::for_each(std::execution::par_unseq, + pixels.begin(), pixels.end(), + [divider](auto& pixel) { pixel /= divider; }); + + return *this; +} + +QList pixelsToLines(const Pixels &rawProfile) +{ + const auto& pixels = rawProfile.pixels; + QList points { pixels.size() }; + + for (int i = 0; i < pixels.size(); ++i) + { + points[i] = { qreal(i) - img_width / 2, pixels.at(i) }; + } + + // qDebug() << "mid points" << points.mid(points.count() - 3, 6); + + return pointsToLines(std::move(points)); +} + +QList pointsToLines(const QList &points) +{ + constexpr double maxDistanceFromLine { 3 }; + constexpr double minLineLength { 10 }; + + QList lineAnchors { 0, points.size() - 1 }; + + auto vecLength = [](const QPointF& vector) { + return std::hypot(vector.x(), vector.y()); + }; + + auto distanceToLine = [vecLength](const QPointF& point, const QLineF& line) { + // const auto d1 = point - line.p1(); + // const auto d2 = line.p2() - line.p1(); + + // const auto norm = vecLength(d2); + // qDebug() << "norm" << norm; + + // if (norm <= std::numeric_limits::epsilon()) + // { + // qDebug() << "AAAAAAAAAAAAAAAAAA"; + // return vecLength(d1); + // } + + // const auto result = std::fabs(d1.x() * d2.y() - d1.y() * d2.x()) / norm; + + // if (!qFuzzyIsNull(result)) + // { + // qDebug() << "NOT NULL" << result << point << line; + // } + + // return result; + // transform to loocal coordinates system (0,0) - (lx, ly) + QPointF p1 = line.p1(); + QPointF p2 = line.p2(); + const auto& p = point; + qreal x = p.x() - p1.x(); + qreal y = p.y() - p1.y(); + qreal x2 = p2.x() - p1.x(); + qreal y2 = p2.y() - p1.y(); + + // if line is a point (nodes are the same) => + // just return distance between point and one line node + qreal norm = sqrt(x2*x2 + y2*y2); + if (norm <= std::numeric_limits::epsilon()) + return sqrt(x*x + y*y); + + // distance + // qDebug() << "dist" << fabs(x*y2 - y*x2) / norm << point << line; + return fabs(x*y2 - y*x2) / norm; + }; + + // for (const auto& p : points) + // { + // qDebug() << "\t" << p; + // } + + for (int i = 0; i < lineAnchors.count() - 1; ++i) + { + const auto &lineFirst = i; + const auto &lineLast = i + 1; + const auto& leftPointIdx = lineAnchors.at(lineFirst); + const auto& rightPointIdx = lineAnchors.at(lineLast); + + if (rightPointIdx - leftPointIdx < 2) + { + continue; + } + + const QLineF line { points.at(leftPointIdx), points.at(rightPointIdx) }; + + const auto farthest = std::max_element( + // std::execution::par_unseq, + points.cbegin() + leftPointIdx + 1, + points.cbegin() + rightPointIdx - 1, + [line, distanceToLine](const QPointF& a, const QPointF& b) { + return distanceToLine(a, line) < distanceToLine(b, line); + }); + + // qDebug() << "farthest point" << distanceToLine(*farthest, line); + // qDebug() << "farthest dist" << distanceToLine(*farthest, line); + // qDebug() << "that's from line" << line; + + if (distanceToLine(*farthest, line) > maxDistanceFromLine) + { + lineAnchors.insert(i + 1, farthest - points.cbegin()); + --i; + // qDebug() << "I'm here" << i; + continue; + } + } + + struct LineAB + { + double a { std::numeric_limits::quiet_NaN() }; + double b { std::numeric_limits::quiet_NaN() }; + + LineAB(const QList& points) { + if (points.isEmpty()) + { + return; + } + + double sumX { 0 }; + double sumY { 0 }; + double sumXY { 0 }; + double sumXX { 0 }; + + // FIXME: validate + // for (const auto& point : points) + const int delta = points.count() * 0.15; + for (int i = delta; i < points.count() - delta; ++i) + { + const auto& point = points.at(i); + sumX += point.x(); + sumY += point.y(); + sumXY += point.x() * point.y(); + sumXX += point.x() * point.x(); + } + + // sumX /= points.count(); + // sumY /= points.count(); + // sumXY /= points.count(); + // sumXX /= points.count(); + + const int n = points.count() - delta * 2; + Q_ASSERT_X(n > 0, Q_FUNC_INFO, "n <= 0"); + + a = (n * sumXY - sumX * sumY) / + (n * sumXX - sumX * sumX); + b = (sumY - a * sumX) / n; + } + }; + + // auto pointsToLineAB = + + // qDebug() << "line anchors count is" << lineAnchors.count(); + + constexpr bool useLsd = true; + + QList result { lineAnchors.length() - 1 }; + + for (int i = 0; i < lineAnchors.count() - 1; ++i) + { + const auto& leftPointIdx = lineAnchors.at(i); + const auto& rightPointIdx = lineAnchors.at(i + 1); + + QPointF leftPoint = points.at(leftPointIdx); + QPointF rightPoint = points.at(rightPointIdx); + + LineAB lineAB(points.mid(leftPointIdx, rightPointIdx - leftPointIdx)); + + leftPoint.setY(lineAB.a * leftPoint.x() + lineAB.b); + rightPoint.setY(lineAB.a * rightPoint.x() + lineAB.b); + + if (useLsd) + result[i] = QLineF{ std::move(leftPoint), std::move(rightPoint) }; + else + result[i] = QLineF{ points.at(lineAnchors.at(i)), points.at(lineAnchors.at(i + 1)) }; + } + + if (useLsd) + { + for (int i = 0; i < result.count() - 1; ++i) + { + auto& lineA = result[i]; + auto& lineB = result[i + 1]; + + QPointF intersectionPoint {}; + + if (lineA.intersects(lineB, &intersectionPoint) != QLineF::NoIntersection) + { + lineA.setP2(intersectionPoint); + lineB.setP1(intersectionPoint); + } + } + } + + // qDebug() << result; + + return result; +} diff --git a/imagealgos.h b/imagealgos.h index 77ac625..12dc8cf 100644 --- a/imagealgos.h +++ b/imagealgos.h @@ -6,6 +6,9 @@ #include +#include +#include + constexpr size_t img_width = 1280; constexpr size_t img_height = 800; constexpr uint32_t patternSize = 16; @@ -37,8 +40,12 @@ struct Pixels { Counters counters {}; std::array pixels { 0.f }; + + Pixels& operator+=(const Pixels& other); + Pixels& operator/=(const float divider); }; + size_t pgm_save(Image *img, FILE *outfile, bool really_save = false); void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest); @@ -52,3 +59,6 @@ T median3(const T& a, const T& b, const T& c) { void rotate(Image & image); Pixels process_columns(Image & image); + +QList pixelsToLines(const Pixels& rawProfile); +QList pointsToLines(const QList& points); diff --git a/main.cpp b/main.cpp index 33f94ee..06b7700 100644 --- a/main.cpp +++ b/main.cpp @@ -1,13 +1,21 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "LibCamera.h" +#include "printerclient.h" #include "httpservice.h" #include "genetic_algos.h" -#include "imagealgos.h" -#include "LibCamera.h" #include "pigpio.h" -#include "printerclient.h" #include "rotaryencoder.h" +#include "imagealgos.h" +#include "fuck_intel.h" -#define QT_NO_KEYWORDS #include #include #include @@ -20,14 +28,6 @@ #include #include #include -#undef QT_NO_KEYWORDS - -#include -#include -#include -#include -#include -#include #define try_apply_config() \ if(!applyConfig(config)) \ @@ -76,21 +76,83 @@ const QString dumpsRoot { QStringLiteral("/home/user/dumps") }; using namespace std::chrono_literals; -static std::shared_ptr camera; -std::unique_ptr config; +static std::shared_ptr camera; +std::unique_ptr config; static std::map> mappedBuffers_; -std::vector> requests; -ControlList lastControls; - -static bool applyConfig(const std::unique_ptr & config); -static void onRequestCompleted(Request *completed_request); +std::vector> requests; +libcamera::ControlList lastControls; + +static QList openDump(const QString& dumpPath = ""); +static std::optional openRawProfile(const QString& filePath); +constexpr float hardcodedZRangeMm { 175.f }; +constexpr size_t calibrationTableHeight { 0x4000 }; // 16384 +// img_width * calibrationTableHeight +using CalibrationColumn = std::array; +using CalibrationTable = std::array; +constexpr auto calibrationColumnHeight = std::tuple_size(); +// CalibrationTable* calibrationTable { new CalibrationTable {{ 0 }} }; + +static bool applyConfig(const std::unique_ptr & config); +static void onRequestCompleted(libcamera::Request *completed_request); static void printControls(); static void dumpCalibrationPixels(); -static std::vector openDump(const QString dumpPath = ""); +static QList filter(const QList& rawProfiles); +static QSharedPointer calibrate(const QList& rawProfiles); + +auto printPixels = [](const auto& pixels) { + for (size_t i = (img_width - 10) / 2; i < img_width - ((img_width - 10) / 2); ++i) + { + std::cout << pixels[i] << " "; + } + std::cout << std::endl; +}; + +bool initLaser(); + +class A +{ +public: + A(const int var) : m_var{ var } { std::cout << m_var << std::endl; } + +private: + int m_var { 0 }; +}; + +class B : public A +{ +public: + B(const int var) : A{var} {} +}; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); + // if (!initLaser()) + // { + // return EXIT_FAILURE; + // } + + if (false) + { + // auto rawProfiles = openDump("/home/user/dumps/2024.11.24_19.17.32"); + // auto rawProfiles = openDump("/home/user/dumps/2024.11.26_21.53.55"); + auto rawProfiles = openDump("/home/user/dumps/2024.11.26_21.53.55_small"); + qDebug() << "raw profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; + + auto filteredRawProfiles = filter(rawProfiles); + qDebug() << "filtered profiles count is" << filteredRawProfiles.count(); + + auto calibrationTable = calibrate(filteredRawProfiles); + // auto calibrationTable = calibrate(rawProfiles); + // CalibrationTable* aaa { new CalibrationTable {{ 0 }} }; + // (*aaa)[1][0] = 123; + // CalibrationTable bbb {{ 0 }}; + // bbb[1][0] = 123; + + // float* values = new float[calibrationColumnHeight]; + } + QElapsedTimer t; t.start(); @@ -102,7 +164,7 @@ int main(int argc, char *argv[]) { qDebug() << "msecs before camera:" << t.elapsed(); // FIXME: don't use one var for everything int ret; - std::unique_ptr cm = std::make_unique(); + std::unique_ptr cm = std::make_unique(); cm->start(); const auto cameras = cm->cameras(); @@ -139,7 +201,7 @@ int main(int argc, char *argv[]) { // FIXME: nullptr // std::unique_ptr config = camera->generateConfiguration( { StreamRole::Viewfinder } ); - /*std::unique_ptr */config = camera->generateConfiguration( { StreamRole::Raw } ); + /*std::unique_ptr */config = camera->generateConfiguration( { libcamera::StreamRole::Raw } ); if (config->empty()) { @@ -162,7 +224,7 @@ int main(int argc, char *argv[]) { // } // FIXME: nullptr - StreamConfiguration &streamConfig = config->at(0); + libcamera::StreamConfiguration &streamConfig = config->at(0); std::cout << "Default viewfinder configuration is: " << streamConfig.toString() << std::endl; std::cout << "Pixel format is: " << streamConfig.pixelFormat.toString() << std::endl; std::cout << "Buffer count is: " << streamConfig.bufferCount << std::endl; @@ -179,7 +241,7 @@ int main(int argc, char *argv[]) { // streamConfig.pixelFormat = PixelFormat::fromString("Y8_1X8"); // streamConfig.pixelFormat = formats::R8; - streamConfig.pixelFormat = formats::R16; + streamConfig.pixelFormat = libcamera::formats::R16; streamConfig.bufferCount = 2; // what is default R10_CSI2P? MONO_PISP_COMP1? // MONO_PISP_COMP1 - check rpicam-apps sources for decoding algos @@ -215,7 +277,7 @@ int main(int argc, char *argv[]) { // TODO: try custom FrameBufferAllocator and compare performance - auto allocator = std::make_shared(camera); + auto allocator = std::make_shared(camera); auto stream = streamConfig.stream(); @@ -232,13 +294,13 @@ int main(int argc, char *argv[]) { size_t allocated = size_t(ret); std::cout << "Allocated " << allocated << " buffers for stream" << std::endl; - const std::vector> &buffers = allocator->buffers(stream); + const std::vector> &buffers = allocator->buffers(stream); // for (size_t i = 0; i < buffers.size(); ++i) static int expOffset = 0; for (const auto & buffer : buffers) { - std::unique_ptr request = camera->createRequest(); + std::unique_ptr request = camera->createRequest(); if (!request) { @@ -328,7 +390,7 @@ int main(int argc, char *argv[]) { for (size_t i = 0; i < img_width; ++i) { // pixels << img_height - img.pixels[i]; - pixels << img_height - ::pixels.pixels[i]; + pixels << ::pixels.pixels[i]; } QJsonObject json; @@ -337,6 +399,22 @@ int main(int argc, char *argv[]) { json["measurementCounter"] = qint64 { img.counters.measurementCounter }; json["timestampUs"] = qint64(img.counters.timestampUs); + const auto lines = pixelsToLines(::pixels); + + qDebug() << "lines count is " << lines.count(); + + QJsonArray jsonLines; + + for (const auto& l : lines) + { + jsonLines << QJsonArray { + QJsonArray { l.p1().x(), l.p1().y() }, + QJsonArray { l.p2().x(), l.p2().y() } + }; + } + + json["lines"] = jsonLines; + return QHttpServerResponse(QJsonDocument(json).toJson()); }); @@ -403,14 +481,14 @@ int main(int argc, char *argv[]) { std::lock_guard lg(pgm_image_mtx); QJsonObject json; - const ControlIdMap & ctrlIdMap = camera->controls().idmap(); + const libcamera::ControlIdMap & ctrlIdMap = camera->controls().idmap(); qDebug() << "readParams:" << lastControls.size(); qDebug() << request.method(); for (const auto & [id, value]: lastControls) { - const ControlId * controlId = ctrlIdMap.at(id); + const libcamera::ControlId * controlId = ctrlIdMap.at(id); auto name = QString::fromStdString(controlId->name()); const auto valueStr = QString::fromStdString(value.toString()); qDebug() << "\t param:" @@ -611,8 +689,10 @@ int main(int argc, char *argv[]) { * important not to block the thread for a long time, as this blocks internal * processing of the camera pipelines, and can affect realtime performance. */ -void onRequestCompleted(Request *completed_request) +void onRequestCompleted(libcamera::Request *completed_request) { + using namespace libcamera; + static std::chrono::steady_clock::time_point fpsTimstamp = std::chrono::steady_clock::now(); QElapsedTimer t; @@ -683,8 +763,7 @@ void onRequestCompleted(Request *completed_request) // qDebug() << "calibration mode" << scanningModeFlags; if (scanningModeFlags == ScanningModeFlags::Calibration) { - constexpr int32_t hardcodedZRangeMm { 175 }; - const int32_t maxEncoderPosition = hardcodedZRangeMm * requested_params.stepsPerMm; + const int32_t maxEncoderPosition = int32_t(hardcodedZRangeMm) * requested_params.stepsPerMm; // qDebug() << "calibration max range" << maxEncoderPosition; // qDebug() << "calibration encoder pos" << pixels.counters.encoderPosition; if (pixels.counters.encoderPosition >= 0 && @@ -789,12 +868,14 @@ void onRequestCompleted(Request *completed_request) } -static bool applyConfig(const std::unique_ptr & config) +static bool applyConfig(const std::unique_ptr & config) { + using namespace libcamera; + auto status = config->validate(); // WARNING: unsafe - StreamConfiguration &streamConfig = config->at(0); + libcamera::StreamConfiguration &streamConfig = config->at(0); switch (status) { case CameraConfiguration::Status::Valid: @@ -819,7 +900,8 @@ static bool applyConfig(const std::unique_ptr & config) static void printControls() { - const ControlInfoMap & control_map = camera->controls(); + using namespace libcamera; + const libcamera::ControlInfoMap & control_map = camera->controls(); // for (const auto & [id, info]: control_map) for (const std::pair & pair : control_map) @@ -888,10 +970,10 @@ static void dumpCalibrationPixels() } QJsonObject jsonCounters { - { "timestampUs", qint64(rawProfile.counters.timestampUs) }, - { "measurementCounter", qint64(rawProfile.counters.measurementCounter) }, - { "encoderPosition", qint64(rawProfile.counters.encoderPosition) }, - }; + { "timestampUs", qint64(rawProfile.counters.timestampUs) }, + { "measurementCounter", qint64(rawProfile.counters.measurementCounter) }, + { "encoderPosition", qint64(rawProfile.counters.encoderPosition) }, + }; QJsonObject json; @@ -920,11 +1002,10 @@ static void dumpCalibrationPixels() qDebug() << "dump finished"; } -static std::vector openDump(const QString dumpPath) +static QList openDump(const QString &dumpPath) { - std::vector result; - QString dirToRead { dumpPath }; + qDebug() << "trying to open dump path:" << dirToRead; if (dirToRead.isEmpty()) { @@ -945,10 +1026,10 @@ static std::vector openDump(const QString dumpPath) return {}; } - dirToRead = entries.last(); + dirToRead = dumpsRoot + "/" + entries.last(); } - QDir dumpDir { dumpsRoot + "/" + dirToRead }; + QDir dumpDir { dirToRead }; const auto filter = QDir::Files; const auto sort = QDir::Name; @@ -958,47 +1039,260 @@ static std::vector openDump(const QString dumpPath) if (filenames.isEmpty()) { qDebug() << "no filenames found in" << dumpDir.path(); + return {}; } - for (const auto& filename : filenames) + QList> resultOptionals { filenames.size() }; + + QElapsedTimer t; + t.start(); + + std::transform( + std::execution::par, + filenames.begin(), filenames.end(), + resultOptionals.begin(), + [dirToRead](const auto& filename) { + std::cout << '.'; + return openRawProfile(dirToRead + "/" + filename); + }); + + std::cout << std::endl; + + std::remove_if( + std::execution::par, + resultOptionals.begin(), + resultOptionals.end(), + [](auto& a) { return !a.has_value(); }); + + QList result { resultOptionals.size() }; + + std::transform( + std::execution::par, + std::make_move_iterator(resultOptionals.begin()), + std::make_move_iterator(resultOptionals.end()), + result.begin(), + [](auto& p) { return p.value(); }); + + qDebug() << Q_FUNC_INFO << "elapsed (ms)" << t.elapsed(); + + return result; +} + +static std::optional openRawProfile(const QString& filePath) +{ + QFile f { filePath }; + + if (!f.open(QFile::ReadOnly)) { - qDebug() << "raw profile:" << filename; + qWarning() << "cannot open file for reading:" << f.fileName(); + qWarning() << "error string:" << f.errorString(); + + return {}; + } + + // TODO: rewrite to remove manual serialization/deserialization + const auto json = QJsonDocument::fromJson(f.readAll()).object(); + const auto jsonCounters = json["counters"].toObject(); - QFile f { dumpDir.path() + "/" + filename }; + Pixels result; + result.counters.timestampUs = jsonCounters["timestampUs"].toInteger(); + result.counters.measurementCounter = jsonCounters["measurementCounter"].toInteger(); + result.counters.encoderPosition = jsonCounters["encoderPosition"].toInteger(); - if (!f.open(QFile::ReadOnly)) + const auto jsonPixels = json["pixels"].toArray(); + + for (size_t i = 0; i < jsonPixels.count() && i < result.pixels.size(); ++i) + { + result.pixels[i] = jsonPixels[i].toDouble(); + } + + return result; +} + +static QList filter(const QList& rawProfiles) +{ + QList result; + + QList::const_iterator it = rawProfiles.constBegin(); + + // for (size_t i = 0; i < 10; ++i) + // { + // std::cout << "pos - " << rawProfiles.at(i).counters.encoderPosition << std::endl; + // } + + // ++it; + while (it != rawProfiles.constEnd()) + { + Pixels sum = *it; + std::cout << "current pos is " << sum.counters.encoderPosition << std::endl; + + printPixels(sum.pixels); + + size_t count { 1 }; + ++it; + + std::cout << "here\n"; + while (it != rawProfiles.constEnd() && it->counters.encoderPosition == sum.counters.encoderPosition) { - qWarning() << "cannot open file for reading:" << f.fileName(); - qWarning() << "error string:" << f.errorString(); + std::cout << "here2\n"; + std::cout << "\tadd to pos " << it->counters.encoderPosition << std::endl; + sum += *it; + std::cout << "\tadded" << std::endl; + ++count; + ++it; - return {}; } - // TODO: rewrite to remove manual serialization/deserialization - const auto json = QJsonDocument::fromJson(f.readAll()).object(); - const auto jsonCounters = json["counters"].toObject(); - qDebug() << jsonCounters; + std::cout << "here3\n"; + sum /= float(count); + printPixels(sum.pixels); - Pixels rawProfile; + result << sum; - rawProfile.counters.timestampUs = jsonCounters["timestampUs"].toInteger(); - rawProfile.counters.measurementCounter = jsonCounters["measurementCounter"].toInteger(); - rawProfile.counters.encoderPosition = jsonCounters["encoderPosition"].toInteger(); + // return result; + } - const auto jsonPixels = json["pixels"].toArray(); - qDebug() << jsonPixels.count() << rawProfile.pixels.size(); + return result; +} + +static QSharedPointer calibrate(const QList& rawProfiles) +{ + QSharedPointer result { new CalibrationTable {{ 0 }} }; - for (size_t i = 0; i < jsonPixels.count() && i < rawProfile.pixels.size(); ++i) + constexpr uint16_t discretesInRage { 16384 }; + + for (const auto& rawProfile : rawProfiles) + { + std::cout << "calibration: pos is " << rawProfile.counters.encoderPosition << std::endl; + const float positionMm { + float(rawProfile.counters.encoderPosition) / + float(requested_params.stepsPerMm) + }; + + const auto& pixels = rawProfile.pixels; + printPixels(pixels); + // static size_t counter { 0 }; + // qDebug() << "calibrated" << counter++; + const float pos = rawProfile.counters.encoderPosition; + const float divider = requested_params.stepsPerMm; + std::cout << pos << " " << divider << " " << pos / divider << std::endl; + // std::cout << std::setw(5) << rawProfile.counters.encoderPosition + // << std::setw(5) << requested_params.stepsPerMm + // << std::setw(8) << positionMm + // << std::setw(8) << float(rawProfile.counters.encoderPosition) / + // float(requested_params.stepsPerMm) + // << ": "; + + for (size_t columnIdx = 0; columnIdx < pixels.size(); ++columnIdx) { - rawProfile.pixels[i] = jsonPixels[i].toDouble(); + const auto& pixelValue = pixels.at(columnIdx); + + // float[0, img_height] -> uint16_t[0, calibrationColumnHeight] + const uint16_t discretePixelValue { + uint16_t(pixelValue * discretesInRage / img_height) + }; + + Q_ASSERT_X(discretePixelValue > 0 && discretePixelValue < calibrationColumnHeight, + Q_FUNC_INFO, + ("got ivalid discrete value " + + QString::number(discretePixelValue) + + ". pixelValues is " + + QString::number(pixelValue) + + ". calc result is " + + QString::number(pixelValue * discretesInRage / img_height) + ).toStdString().c_str()); + + // auto& calibrationColumn = result[columnIdx]; + auto& calibrationColumn = (*result)[columnIdx]; + calibrationColumn[discretePixelValue] = positionMm; + + if (columnIdx >= ((img_width - 10) / 2) && columnIdx < img_width - ((img_width - 10) / 2)) + { + std::cout << discretePixelValue << ";"; + } } - } - // { - // QMutexLocker l(&calibrationPixelsMutex); - // std::swap(result, ::calibrationPixels); - // } + std::cout << std::endl << std::flush; + } return result; + // TODO: try something interesting + // return {}; +} + +bool initLaser() +{ + const QLatin1String pwmChip { "pwmchip2" }; + const uint16_t pwmChannel { 1 }; + const QLatin1String pwmSystemRoot { "/sys/class/pwm" }; + const QString pwmChipRoot { pwmSystemRoot + "/" + pwmChip }; + + const QString pwmExportFile { pwmChipRoot + "/export" }; + + QFile f { pwmExportFile }; + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + QTextStream s { &f }; + s << pwmChannel; + + const QString pwm { QLatin1String("pwm%1").arg(QString::number(pwmChannel)) }; + const QString pwmRoot { pwmChipRoot + "/" + pwm }; + + const QString periodFilename { pwmRoot + "/period" }; + f.close(); + f.setFileName(periodFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const unsigned periodHz { 50'000 }; + + s << periodHz; + + const QString dutyCycleFilename { pwmRoot + "/duty_cycle" }; + f.close(); + f.setFileName(dutyCycleFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const unsigned dutyCycle { 3'000 }; + + s << dutyCycle; + + const QString enableFilename { pwmRoot + "/enable" }; + f.close(); + f.setFileName(enableFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const int enable { 1 }; + + s << enable; + + return true; } -- cgit v1.2.3-70-g09d2