diff options
| author | Nikita Kostovsky <luntik2012@gmail.com> | 2024-12-09 13:19:58 +0100 |
|---|---|---|
| committer | Nikita Kostovsky <luntik2012@gmail.com> | 2024-12-09 13:19:58 +0100 |
| commit | 81ce2f7ee5f27d08479d40fb6450a49f70f3355c (patch) | |
| tree | 1fa85786c8696494d84fdf7fc1c351e89b1a7a7b | |
| parent | ee43621697a4cdc4c7728d4b42ad73b8321137c4 (diff) | |
add x calibration prototype, add .clang-config
| -rw-r--r-- | .clang-format | 15 | ||||
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | imagealgos.cpp | 22 | ||||
| -rw-r--r-- | main.cpp | 1069 | ||||
| -rw-r--r-- | printerclient.cpp | 18 |
5 files changed, 767 insertions, 360 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56838cf --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: BlockIndent +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +BasedOnStyle: Microsoft +BinPackParameters: false +BinPackArguments: false +BreakConstructorInitializers: BeforeComma +ColumnLimit: 80 +InsertNewlineAtEOF: true +IndentWidth: 4 +SpacesBeforeTrailingComments: 2 +UseTab: Never +LineEnding: LF +PointerAlignment: Left diff --git a/CMakeLists.txt b/CMakeLists.txt index e07bf77..a00e7f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std:c++latest") -find_package(Qt6 6.4 REQUIRED COMPONENTS Quick HttpServer SerialPort) +find_package(Qt6 6.4 REQUIRED COMPONENTS Gui Quick HttpServer SerialPort) find_package(TBB REQUIRED) @@ -91,6 +91,7 @@ target_link_libraries(app${PROJECT_NAME} PRIVATE "${LIBCAMERA_LIBRARIES}" PkgConfig::Pistache + Qt6::Gui Qt6::HttpServer Qt6::SerialPort wiringPi diff --git a/imagealgos.cpp b/imagealgos.cpp index b4fba4b..267e4ae 100644 --- a/imagealgos.cpp +++ b/imagealgos.cpp @@ -303,7 +303,7 @@ Pixels& Pixels::operator/=(const float divider) QList<QLineF> pixelsToLines(const Pixels &rawProfile) { const auto& pixels = rawProfile.pixels; - QList<QPointF> points { pixels.size() }; + QList<QPointF> points(pixels.size()); for (int i = 0; i < pixels.size(); ++i) { @@ -320,7 +320,7 @@ QList<QLineF> pointsToLines(const QList<QPointF> &points) constexpr double maxDistanceFromLine { 3 }; constexpr double minLineLength { 10 }; - QList<int> lineAnchors { 0, points.size() - 1 }; + QList<int> lineAnchors{0, int(points.count() - 1)}; auto vecLength = [](const QPointF& vector) { return std::hypot(vector.x(), vector.y()); @@ -364,7 +364,7 @@ QList<QLineF> pointsToLines(const QList<QPointF> &points) // distance // qDebug() << "dist" << fabs(x*y2 - y*x2) / norm << point << line; - return fabs(x*y2 - y*x2) / norm; + return fabs(x * y2 - y * x2) / norm; }; // for (const auto& p : points) @@ -386,13 +386,21 @@ QList<QLineF> pointsToLines(const QList<QPointF> &points) const QLineF line { points.at(leftPointIdx), points.at(rightPointIdx) }; + // std::cout << "max between " << leftPointIdx + 1 << " and " + // << rightPointIdx - 1 << std::endl; + 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); - }); + } + ); + + // std::cout << "done max" << std::endl; + // auto tmp = *farthest; + // std::cout << "done farthest" << std::endl; // qDebug() << "farthest point" << distanceToLine(*farthest, line); // qDebug() << "farthest dist" << distanceToLine(*farthest, line); @@ -400,7 +408,13 @@ QList<QLineF> pointsToLines(const QList<QPointF> &points) if (distanceToLine(*farthest, line) > maxDistanceFromLine) { + // std::cout << "try insert at " << i + 1 << ". lineAnchors.size is + // " + // << lineAnchors.size() + // << ". inserting this: " << farthest - points.cbegin() + // << std::endl; lineAnchors.insert(i + 1, farthest - points.cbegin()); + // std::cout << "done insert" << std::endl; --i; // qDebug() << "I'm here" << i; continue; @@ -1,48 +1,49 @@ #include <chrono> #include <errno.h> +#include <fstream> #include <iostream> #include <iterator> -#include <fstream> #include <string.h> -#include <thread> #include <sys/mman.h> +#include <thread> #include "LibCamera.h" -#include "printerclient.h" -#include "httpservice.h" +#include "fuck_intel.h" #include "genetic_algos.h" +#include "httpservice.h" +#include "imagealgos.h" #include "pigpio.h" +#include "printerclient.h" #include "rotaryencoder.h" -#include "imagealgos.h" -#include "fuck_intel.h" #include <QCoreApplication> #include <QDebug> #include <QDir> #include <QFile> #include <QHttpServer> +#include <QImage> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QSerialPort> #include <QTextStream> -#include <QtConcurrent/QtConcurrentRun> #include <QTimer> +#include <QtConcurrent/QtConcurrentRun> -#define try_apply_config() \ -if(!applyConfig(config)) \ - { \ - camera->release(); \ - cm->stop(); \ - \ - return EXIT_FAILURE;\ +#define try_apply_config() \ + if (!applyConfig(config)) \ + { \ + camera->release(); \ + cm->stop(); \ + \ + return EXIT_FAILURE; \ } - const QString exposureTimeKey = "exposureTime"; const QString laserLevelKey = "laserLevel"; -enum ScanningModeFlags : uint8_t { +enum ScanningModeFlags : uint8_t +{ None = 0, Calibration }; @@ -50,57 +51,83 @@ enum ScanningModeFlags : uint8_t { // ScanningModeFlags operator|(ScanningModeFlags lhs, ScanningModeFlags rhs) // { // using T = std::underlying_type<ScanningModeFlags>::type; -// return static_cast<ScanningModeFlags>(static_cast<T>(lhs) | static_cast<T>(rhs)); +// return static_cast<ScanningModeFlags>(static_cast<T>(lhs) | +// static_cast<T>(rhs)); // } -ScanningModeFlags scanningModeFlags { ScanningModeFlags::None }; +ScanningModeFlags scanningModeFlags{ScanningModeFlags::None}; QElapsedTimer calibrationTimer; extern volatile int32_t positionSteps; -struct requested_params_t { - int32_t exposureTime = { 1000 }; - int32_t laserLevel = { 3000 }; - uint32_t stepsPerMm { 200 }; +struct requested_params_t +{ + int32_t exposureTime = {1000}; + int32_t laserLevel = {3000}; + uint32_t stepsPerMm{200}; } requested_params; -namespace { +namespace +{ Image img; Pixels pixels; std::vector<Pixels> calibrationPixels; QMutex calibrationPixelsMutex; -} +} // namespace -const QString dumpsRoot { QStringLiteral("/home/user/dumps") }; +const QString dumpsRoot{QStringLiteral("/home/user/dumps")}; using namespace std::chrono_literals; static std::shared_ptr<libcamera::Camera> camera; std::unique_ptr<libcamera::CameraConfiguration> config; -static std::map<int, std::pair<void *, unsigned int>> mappedBuffers_; +static std::map<int, std::pair<void*, unsigned int>> mappedBuffers_; std::vector<std::unique_ptr<libcamera::Request>> requests; libcamera::ControlList lastControls; static QList<Pixels> openDump(const QString& dumpPath = ""); static std::optional<Pixels> openRawProfile(const QString& filePath); -constexpr float hardcodedZRangeMm { 175.f }; -constexpr size_t calibrationTableHeight { 0x4000 }; // 16384 +constexpr float hardcodedZRangeMm{175.f}; +constexpr size_t calibrationTableHeight{0x4000}; // 16384 // img_width * calibrationTableHeight using CalibrationColumn = std::array<float, calibrationTableHeight>; using CalibrationTable = std::array<CalibrationColumn, img_width>; constexpr auto calibrationColumnHeight = std::tuple_size<CalibrationColumn>(); // CalibrationTable* calibrationTable { new CalibrationTable {{ 0 }} }; -static bool applyConfig(const std::unique_ptr<libcamera::CameraConfiguration> & config); -static void onRequestCompleted(libcamera::Request *completed_request); +namespace +{ +QSharedPointer<CalibrationTable> calibrationTableZ; +QSharedPointer<CalibrationTable> calibrationTableX; +constexpr uint16_t discretesInRage{16384}; +} // namespace + +static bool applyConfig( + const std::unique_ptr<libcamera::CameraConfiguration>& config +); +static void onRequestCompleted(libcamera::Request* completed_request); static void printControls(); static void dumpCalibrationPixels(); static QList<Pixels> filter(const QList<Pixels>& rawProfiles); -static QSharedPointer<CalibrationTable> calibrate(const QList<Pixels>& rawProfiles); +static QSharedPointer<CalibrationTable> calibrateX( + const QList<Pixels>& rawProfiles +); +static QSharedPointer<CalibrationTable> calibrateZ( + const QList<Pixels>& rawProfiles +); + +static QImage calibrationTableToImage( + const QSharedPointer<CalibrationTable>& calibrationTable +); + +static void interpolate(QSharedPointer<CalibrationTable>& table); +static void interpolate(CalibrationColumn& column); auto printPixels = [](const auto& pixels) { - for (size_t i = (img_width - 10) / 2; i < img_width - ((img_width - 10) / 2); ++i) + for (size_t i = (img_width - 10) / 2; + i < img_width - ((img_width - 10) / 2); + ++i) { std::cout << pixels[i] << " "; } @@ -109,70 +136,100 @@ auto printPixels = [](const auto& pixels) { bool initLaser(); -class A +int main(int argc, char* argv[]) { -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 (!initLaser()) + { + return EXIT_FAILURE; + } - if (false) + PrinterClient printerClient; + + if (true) { // 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 rawProfiles = + // openDump("/home/user/dumps/2024.11.26_21.53.55_small"); + + { + auto rawProfiles = openDump("/home/user/dumps/2024.12.07_14.09.33"); + // auto rawProfiles = openDump("/home/user/dumps/z"); + qDebug() << "raw z-profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; + + auto filteredRawProfiles = filter(std::move(rawProfiles)); + qDebug() << "filtered z-profiles count is" + << filteredRawProfiles.count(); + + ::calibrationTableZ = calibrateZ(std::move(filteredRawProfiles)); + + bool ok = calibrationTableToImage(::calibrationTableZ) + .save("/home/user/dumps/z/imageZ.png"); - auto filteredRawProfiles = filter(rawProfiles); - qDebug() << "filtered profiles count is" << filteredRawProfiles.count(); + if (!ok) + { + qDebug() << "cannot save imageZ.png"; + exit(EXIT_FAILURE); + } + + interpolate(::calibrationTableZ); + + calibrationTableToImage(::calibrationTableZ) + .save("/home/user/dumps/z/imageZ_interpolated.png"); + } + + { + auto rawProfiles = openDump("/home/user/dumps/2024.12.07_14.51.07"); + qDebug() << "raw x-profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; - auto calibrationTable = calibrate(filteredRawProfiles); - // auto calibrationTable = calibrate(rawProfiles); - // CalibrationTable* aaa { new CalibrationTable {{ 0 }} }; - // (*aaa)[1][0] = 123; - // CalibrationTable bbb {{ 0 }}; - // bbb[1][0] = 123; + auto filteredRawProfiles = filter(std::move(rawProfiles)); + qDebug() << "filtered x-profiles count is" + << filteredRawProfiles.count(); - // float* values = new float[calibrationColumnHeight]; + ::calibrationTableX = calibrateX(std::move(filteredRawProfiles)); + + bool ok = calibrationTableToImage(::calibrationTableX) + .save("/home/user/dumps/z/imageX.png"); + + if (!ok) + { + qDebug() << "cannot save imageX.png"; + exit(EXIT_FAILURE); + } + + interpolate(::calibrationTableX); + + calibrationTableToImage(::calibrationTableX) + .save("/home/user/dumps/z/imageX_interpolated.png"); + } } + // exit(EXIT_SUCCESS); + QElapsedTimer t; t.start(); qDebug() << "msecs before encoder:" << t.elapsed(); RotaryEncoder encoder; - PrinterClient printerClient; qDebug() << "msecs before camera:" << t.elapsed(); // FIXME: don't use one var for everything int ret; - std::unique_ptr<libcamera::CameraManager> cm = std::make_unique<libcamera::CameraManager>(); + std::unique_ptr<libcamera::CameraManager> cm = + std::make_unique<libcamera::CameraManager>(); cm->start(); const auto cameras = cm->cameras(); - openDump(); + // openDump(); if (cameras.empty()) - { + { std::cout << "No cameras were identified on the system." << std::endl; cm->stop(); @@ -200,8 +257,10 @@ int main(int argc, char *argv[]) { } // FIXME: nullptr - // std::unique_ptr<CameraConfiguration> config = camera->generateConfiguration( { StreamRole::Viewfinder } ); - /*std::unique_ptr<CameraConfiguration> */config = camera->generateConfiguration( { libcamera::StreamRole::Raw } ); + // std::unique_ptr<CameraConfiguration> config = + // camera->generateConfiguration( { StreamRole::Viewfinder } ); + /*std::unique_ptr<CameraConfiguration> */ config = + camera->generateConfiguration({libcamera::StreamRole::Raw}); if (config->empty()) { @@ -224,12 +283,15 @@ int main(int argc, char *argv[]) { // } // FIXME: nullptr - 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; + 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; // FIXME: empty variant - std::cout << "Color space is: " << streamConfig.colorSpace.value().toString() << std::endl; + std::cout << "Color space is: " + << streamConfig.colorSpace.value().toString() << std::endl; std::cout << "Orientation is: " << config->orientation << std::endl; // formats::R8, // formats::R10, @@ -263,7 +325,8 @@ int main(int argc, char *argv[]) { // doit(Rotate270Mirror); // doit(Rotate90); - std::cout << "new config " << streamConfig.toString() << std::endl; + std::cout + << "new config " << streamConfig.toString() << std::endl; // FIXME: may crassh even on success (e.g. by setting pixelFormat to "8") if (camera->configure(config.get()) != EXIT_SUCCESS) @@ -292,13 +355,15 @@ int main(int argc, char *argv[]) { } size_t allocated = size_t(ret); - std::cout << "Allocated " << allocated << " buffers for stream" << std::endl; + std::cout << "Allocated " << allocated << " buffers for stream" + << std::endl; - const std::vector<std::unique_ptr<libcamera::FrameBuffer>> &buffers = allocator->buffers(stream); + const std::vector<std::unique_ptr<libcamera::FrameBuffer>>& buffers = + allocator->buffers(stream); // for (size_t i = 0; i < buffers.size(); ++i) static int expOffset = 0; - for (const auto & buffer : buffers) + for (const auto& buffer : buffers) { std::unique_ptr<libcamera::Request> request = camera->createRequest(); @@ -318,10 +383,16 @@ int main(int argc, char *argv[]) { return ret; } - for (const auto & plane : buffer->planes()) + for (const auto& plane : buffer->planes()) { - void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, - plane.fd.get(), 0); + void* memory = mmap( + NULL, + plane.length, + PROT_READ, + MAP_SHARED, + plane.fd.get(), + 0 + ); mappedBuffers_[plane.fd.get()] = std::make_pair(memory, plane.length); } @@ -330,20 +401,24 @@ int main(int argc, char *argv[]) { std::int64_t lowerUS = 1 * 1000 * 1000 / desiredFPS; std::int64_t higherUS = lowerUS; - std::int64_t value_pair[2] = { higherUS / 2, higherUS }; + std::int64_t value_pair[2] = {higherUS / 2, higherUS}; request->controls().set(libcamera::controls::AnalogueGain, 1.0); request->controls().set(libcamera::controls::ExposureTime, 100); request->controls().set( libcamera::controls::FrameDurationLimits, - libcamera::Span<const std::int64_t, 2>(value_pair)); + libcamera::Span<const std::int64_t, 2>(value_pair) + ); requests.push_back(std::move(request)); } camera->requestCompleted.connect(onRequestCompleted); - std::unique_ptr<libcamera::ControlList> camcontrols { new libcamera::ControlList() }; - // camcontrols->set(controls::FrameDurationLimits, libcamera::Span<const std::int64_t, 2>({8702, 10718903})); + std::unique_ptr<libcamera::ControlList> camcontrols{ + new libcamera::ControlList() + }; + // camcontrols->set(controls::FrameDurationLimits, libcamera::Span<const + // std::int64_t, 2>({8702, 10718903})); // camcontrols->set(controls::ExposureTime, 100); // camcontrols->set(controls::AnalogueGain, 0.1); @@ -357,7 +432,7 @@ int main(int argc, char *argv[]) { // camera->start(); - for (auto & request : requests) + for (auto& request : requests) { camera->queueRequest(request.get()); } @@ -388,15 +463,16 @@ int main(int argc, char *argv[]) { QJsonArray pixels; - for (size_t i = 0; i < img_width; ++i) { + for (size_t i = 0; i < img_width; ++i) + { // pixels << img_height - img.pixels[i]; pixels << ::pixels.pixels[i]; } QJsonObject json; json["pixels"] = pixels; - json["encoderPosition"] = qint64 { encoder.position() }; - json["measurementCounter"] = qint64 { img.counters.measurementCounter }; + json["encoderPosition"] = qint64{encoder.position()}; + json["measurementCounter"] = qint64{img.counters.measurementCounter}; json["timestampUs"] = qint64(img.counters.timestampUs); const auto lines = pixelsToLines(::pixels); @@ -407,9 +483,9 @@ int main(int argc, char *argv[]) { for (const auto& l : lines) { - jsonLines << QJsonArray { - QJsonArray { l.p1().x(), l.p1().y() }, - QJsonArray { l.p2().x(), l.p2().y() } + jsonLines << QJsonArray{ + QJsonArray{l.p1().x(), l.p1().y()}, + QJsonArray{l.p2().x(), l.p2().y()} }; } @@ -418,155 +494,178 @@ int main(int argc, char *argv[]) { return QHttpServerResponse(QJsonDocument(json).toJson()); }); + qHttpServer.route( + "/v1/commands/resetEncoder", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - qHttpServer.route("/v1/commands/resetEncoder", [&](const QHttpServerRequest &request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) { - return QHttpServerResponse::StatusCode::NotFound; - } - - qDebug() << "reset encoder"; - - positionSteps = 0; + qDebug() << "reset encoder"; - return QHttpServerResponse::StatusCode::Ok; - }); + positionSteps = 0; - qHttpServer.route("/v1/commands/startCalibration", [&](const QHttpServerRequest &request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) { - return QHttpServerResponse::StatusCode::NotFound; + return QHttpServerResponse::StatusCode::Ok; } + ); - qDebug() << "start calibration"; + qHttpServer.route( + "/v1/commands/startCalibration", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - // TODO: use flags - scanningModeFlags = ScanningModeFlags::Calibration; - calibrationTimer.start(); + qDebug() << "start calibration"; - return QHttpServerResponse::StatusCode::Ok; - }); + // TODO: use flags + scanningModeFlags = ScanningModeFlags::Calibration; + calibrationTimer.start(); - qHttpServer.route("/v1/commands/gCode", [&](const QHttpServerRequest &request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) { - return QHttpServerResponse::StatusCode::NotFound; + return QHttpServerResponse::StatusCode::Ok; } + ); - const auto command = request.body(); + qHttpServer.route( + "/v1/commands/gCode", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - qDebug() << "send gCode:" << command; + const auto command = request.body(); - printerClient.sendCommand(command); + qDebug() << "send gCode:" << command; - return QHttpServerResponse::StatusCode::Ok; - }); + printerClient.sendCommand(command); - qHttpServer.route("/v1/commands/startCalibration", [&](const QHttpServerRequest &request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) { - return QHttpServerResponse::StatusCode::NotFound; + return QHttpServerResponse::StatusCode::Ok; } + ); - const auto command = request.body(); + qHttpServer.route( + "/v1/commands/startCalibration", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - qDebug() << "send gCode:" << command; + const auto command = request.body(); - printerClient.sendCommand(command); + qDebug() << "send gCode:" << command; - return QHttpServerResponse::StatusCode::Ok; - }); + printerClient.sendCommand(command); - qHttpServer.route("/v1/sensor/params", [&](const QHttpServerRequest &request) -> QHttpServerResponse { + return QHttpServerResponse::StatusCode::Ok; + } + ); - switch (request.method()) { - case QHttpServerRequest::Method::Get: - { - std::lock_guard<std::mutex> lg(pgm_image_mtx); - QJsonObject json; + qHttpServer.route( + "/v1/sensor/params", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + switch (request.method()) + { + case QHttpServerRequest::Method::Get: { + std::lock_guard<std::mutex> lg(pgm_image_mtx); + QJsonObject json; - const libcamera::ControlIdMap & ctrlIdMap = camera->controls().idmap(); + const libcamera::ControlIdMap& ctrlIdMap = + camera->controls().idmap(); - qDebug() << "readParams:" << lastControls.size(); - qDebug() << request.method(); + qDebug() << "readParams:" << lastControls.size(); + qDebug() << request.method(); - for (const auto & [id, value]: lastControls) - { - const libcamera::ControlId * controlId = ctrlIdMap.at(id); - auto name = QString::fromStdString(controlId->name()); - const auto valueStr = QString::fromStdString(value.toString()); - qDebug() << "\t param:" - << controlId->id() - << name - << valueStr - ; + for (const auto& [id, value] : lastControls) + { + const libcamera::ControlId* controlId = ctrlIdMap.at(id); + auto name = QString::fromStdString(controlId->name()); + const auto valueStr = + QString::fromStdString(value.toString()); + qDebug() + << "\t param:" << controlId->id() << name << valueStr; - name[0] = name[0].toLower(); - json[name] = valueStr; - } + name[0] = name[0].toLower(); + json[name] = valueStr; + } - json[laserLevelKey] = requested_params.laserLevel; + json[laserLevelKey] = requested_params.laserLevel; - qDebug() << "response body:" << json; + qDebug() << "response body:" << json; - // QHttpServerResponse - return QHttpServerResponse(QJsonDocument(json).toJson()); - } + // QHttpServerResponse + return QHttpServerResponse(QJsonDocument(json).toJson()); + } - case QHttpServerRequest::Method::Post: - { - qDebug() << "request body:" << request.body(); + case QHttpServerRequest::Method::Post: { + qDebug() << "request body:" << request.body(); - auto json = QJsonDocument::fromJson(request.body()).object(); + auto json = QJsonDocument::fromJson(request.body()).object(); + + if (json.contains(exposureTimeKey)) + { + const int32_t value{json[exposureTimeKey].toInt()}; - if (json.contains(exposureTimeKey)) { - const int32_t value { json[exposureTimeKey].toInt() }; + if (value == 0) + { + return QHttpServerResponse::StatusCode::NotFound; + } - if (value == 0) { - return QHttpServerResponse::StatusCode::NotFound; + qDebug() << "set new exposure time:" << value; + requested_params.exposureTime = value; } - qDebug() << "set new exposure time:" << value; - requested_params.exposureTime = value; - } + if (json.contains(laserLevelKey)) + { + const int32_t value{json[laserLevelKey].toInt()}; - if (json.contains(laserLevelKey)) { - const int32_t value { json[laserLevelKey].toInt() }; + if (value == 0) + { + return QHttpServerResponse::StatusCode::NotFound; + } - if (value == 0) { - return QHttpServerResponse::StatusCode::NotFound; - } + qDebug() << "set new laserLevel:" << value; + requested_params.laserLevel = value; - qDebug() << "set new laserLevel:" << value; - requested_params.laserLevel = value; + const QString laserLevelFile{ + "/sys/class/pwm/pwmchip2/pwm1/duty_cycle" + }; + QFile f{laserLevelFile}; - const QString laserLevelFile { "/sys/class/pwm/pwmchip2/pwm1/duty_cycle"}; - QFile f { laserLevelFile }; + if (!f.open(QFile::ReadWrite)) + { + qDebug() << "cannot open laser level file:" + << f.errorString(); + qDebug() << "file path is" << f.fileName(); + return QHttpServerResponse::StatusCode:: + InternalServerError; + } - if (!f.open(QFile::ReadWrite)) { - qDebug() << "cannot open laser level file:" << f.errorString(); - qDebug() << "file path is" << f.fileName(); - return QHttpServerResponse::StatusCode::InternalServerError; - } + QTextStream s{&f}; - QTextStream s { &f }; + s << value; - s << value; + s >> requested_params.laserLevel; - s >> requested_params.laserLevel; + qDebug() << "done with laser level"; + } - qDebug() << "done with laser level"; + return QHttpServerResponse(request.body()); + } + default: { + return QHttpServerResponse(QByteArray("unsupported http method") + ); + } } - - return QHttpServerResponse(request.body()); - } - default: - { - return QHttpServerResponse(QByteArray("unsupported http method")); - } } - }); + ); qDebug() << "listen: " << qHttpServer.listen(QHostAddress::Any, 8081); - QFuture<void> future = QtConcurrent::run([](){ - + QFuture<void> future = QtConcurrent::run([]() { Port port(8080); Address addr(Ipv4::any(), port); @@ -591,7 +690,7 @@ int main(int argc, char *argv[]) { future.cancel(); future.waitForFinished(); - for (auto & [fd, mem] : mappedBuffers_) + for (auto& [fd, mem] : mappedBuffers_) { munmap(mem.first, mem.second); } @@ -632,7 +731,8 @@ int main(int argc, char *argv[]) { // // ControlList controls_; // int64_t frame_time = 1000000 / 10; // // Set frame rate - // // controls_.set( controls::FrameDurationLimits, libcamera::Span<const int64_t, 2>( + // // controls_.set( controls::FrameDurationLimits, libcamera::Span<const + // int64_t, 2>( // // { frame_time, frame_time } )); // // Adjust the brightness of the output images, in the range -1.0 to 1.0 // // controls_.set(controls::Brightness, 0.5); @@ -666,7 +766,6 @@ int main(int argc, char *argv[]) { // // cam.set(controls); // // } - // frame_count++; // if ((time(0) - start_time) >= 1){ // printf("fps: %d\n", frame_count); @@ -688,17 +787,18 @@ int main(int argc, char *argv[]) { * Signals operate in the libcamera CameraManager thread context, so it is * 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(libcamera::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(); + static std::chrono::steady_clock::time_point fpsTimstamp = + std::chrono::steady_clock::now(); QElapsedTimer t; t.start(); - static uint32_t performanceCounter { 0 }; - static uint32_t elapsedSum { 0 }; + static uint32_t performanceCounter{0}; + static uint32_t elapsedSum{0}; bool verbose = false; @@ -709,19 +809,20 @@ void onRequestCompleted(libcamera::Request *completed_request) return; } - const std::map<const Stream *, FrameBuffer *> &buffers = completed_request->buffers(); + const std::map<const Stream*, FrameBuffer*>& buffers = + completed_request->buffers(); // std::cout << "request completed, buffers count is " << buffers.size(); // // TODO: rewrite this shit for (auto [stream, buffer] : buffers) { - const auto & streamConfig = stream->configuration(); - const auto & imageSize = streamConfig.size; - const auto & pixelFormat = streamConfig.pixelFormat; - const auto & stride = streamConfig.stride; + const auto& streamConfig = stream->configuration(); + const auto& imageSize = streamConfig.size; + const auto& pixelFormat = streamConfig.pixelFormat; + const auto& stride = streamConfig.stride; - const FrameMetadata &metadata = buffer->metadata(); + const FrameMetadata& metadata = buffer->metadata(); // if (verbose) // { @@ -732,12 +833,12 @@ void onRequestCompleted(libcamera::Request *completed_request) for (size_t i = 0; i < buffer->planes().size(); ++i) { - const FrameBuffer::Plane & plane = buffer->planes()[i]; - const FrameMetadata::Plane & metaplane = buffer->metadata().planes()[i]; + const FrameBuffer::Plane& plane = buffer->planes()[i]; + const FrameMetadata::Plane& metaplane = + buffer->metadata().planes()[i]; size_t size = std::min(metaplane.bytesused, plane.length); - void * data = mappedBuffers_[plane.fd.get()].first; - + void* data = mappedBuffers_[plane.fd.get()].first; // FIXME: remove hardcode img.width = imageSize.width; @@ -749,7 +850,8 @@ void onRequestCompleted(libcamera::Request *completed_request) img.pixelFormat = pixelFormat; img.counters.measurementCounter = metadata.sequence; img.counters.timestampUs = metadata.timestamp / 1000; - img.counters.encoderPosition = RotaryEncoder::instance()->position(); + img.counters.encoderPosition = + RotaryEncoder::instance()->position(); // qDebug() << "pos:" << img.counters.encoderPosition; // uint16_t unpacked[img.width * img.height] = { 0 }; @@ -762,14 +864,18 @@ void onRequestCompleted(libcamera::Request *completed_request) // qDebug() << "calibration mode" << scanningModeFlags; - if (scanningModeFlags == ScanningModeFlags::Calibration) { - const int32_t maxEncoderPosition = int32_t(hardcodedZRangeMm) * requested_params.stepsPerMm; + if (scanningModeFlags == ScanningModeFlags::Calibration) + { + const int32_t maxEncoderPosition = + int32_t(hardcodedZRangeMm) * requested_params.stepsPerMm; // qDebug() << "calibration max range" << maxEncoderPosition; - // qDebug() << "calibration encoder pos" << pixels.counters.encoderPosition; + // qDebug() << "calibration encoder pos" << + // pixels.counters.encoderPosition; if (pixels.counters.encoderPosition >= 0 && pixels.counters.encoderPosition <= maxEncoderPosition) { - qDebug() << "calibration save at pos:" << pixels.counters.encoderPosition; + qDebug() << "calibration save at pos:" + << pixels.counters.encoderPosition; QMutexLocker l(&calibrationPixelsMutex); ::calibrationPixels.push_back(std::move(pixels)); } @@ -791,7 +897,8 @@ void onRequestCompleted(libcamera::Request *completed_request) } else { - // qDebug() << "calibration skip at pos:" << pixels.counters.encoderPosition; + // qDebug() << "calibration skip at pos:" << + // pixels.counters.encoderPosition; } } @@ -799,15 +906,15 @@ void onRequestCompleted(libcamera::Request *completed_request) } } - const libcamera::ControlList &metadata = completed_request->metadata(); - const ControlInfoMap & control_map = camera->controls(); + const libcamera::ControlList& metadata = completed_request->metadata(); + const ControlInfoMap& control_map = camera->controls(); // const ControlIdMap & ctrlIdMap = control_map.idmap(); auto frameDurationCtrl = control_map.find(&controls::FrameDurationLimits); // auto expTimeCtrl = control_map.find(&controls::ExposureTime); - double fps = frameDurationCtrl == control_map.end() ? - std::numeric_limits<double>::quiet_NaN() : - (1e6 / frameDurationCtrl->second.min().get<int64_t>()); + double fps = frameDurationCtrl == control_map.end() + ? std::numeric_limits<double>::quiet_NaN() + : (1e6 / frameDurationCtrl->second.min().get<int64_t>()); auto exp = metadata.get(controls::ExposureTime); auto ag = metadata.get(controls::AnalogueGain); @@ -817,26 +924,27 @@ void onRequestCompleted(libcamera::Request *completed_request) if (verbose) { - std::cout << "fps: " << fps - << " exp: " << *exp - << " ag: " << *ag + std::cout << "fps: " << fps << " exp: " << *exp << " ag: " + << *ag // << " br: " << *br - << " ae: " << *ae - << " aa: " << *completed_request->controls() - .get(libcamera::controls::ExposureTime) + << " ae: " << *ae << " aa: " + << *completed_request->controls().get( + libcamera::controls::ExposureTime + ) << std::endl; } completed_request->reuse(Request::ReuseBuffers); completed_request->controls().set(libcamera::controls::AeEnable, false); - completed_request->controls().set(libcamera::controls::draft - ::NoiseReductionMode, - libcamera::controls::draft - ::NoiseReductionModeEnum - ::NoiseReductionModeHighQuality); - completed_request->controls().set(libcamera::controls::ExposureTime, - requested_params.exposureTime); - + completed_request->controls().set( + libcamera::controls::draft ::NoiseReductionMode, + libcamera::controls::draft ::NoiseReductionModeEnum :: + NoiseReductionModeHighQuality + ); + completed_request->controls().set( + libcamera::controls::ExposureTime, + requested_params.exposureTime + ); camera->queueRequest(completed_request); @@ -844,20 +952,23 @@ void onRequestCompleted(libcamera::Request *completed_request) elapsedSum += t.elapsed(); // if (performanceCounter == 20) - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point now = + std::chrono::steady_clock::now(); if ((now - fpsTimstamp) > 1000ms) { - auto msPerFrame { float(elapsedSum / performanceCounter) }; + auto msPerFrame{float(elapsedSum / performanceCounter)}; - double configFps = frameDurationCtrl == control_map.end() ? - std::numeric_limits<double>::quiet_NaN() : - (1e6 / frameDurationCtrl->second.min().get<int64_t>()); + double configFps = + frameDurationCtrl == control_map.end() + ? std::numeric_limits<double>::quiet_NaN() + : (1e6 / frameDurationCtrl->second.min().get<int64_t>()); - auto fps { 1000.f / msPerFrame }; + auto fps{1000.f / msPerFrame}; qDebug() << "fps ideal/real is" << configFps << "/" << fps - << "; ms per frame is" << msPerFrame << "counted fps" << performanceCounter; + << "; ms per frame is" << msPerFrame << "counted fps" + << performanceCounter; elapsedSum = 0; performanceCounter = 0; @@ -865,29 +976,29 @@ void onRequestCompleted(libcamera::Request *completed_request) } // qDebug() << "-------------------------------------------"; - } -static bool applyConfig(const std::unique_ptr<libcamera::CameraConfiguration> & config) +static bool applyConfig( + const std::unique_ptr<libcamera::CameraConfiguration>& config +) { using namespace libcamera; auto status = config->validate(); // WARNING: unsafe - libcamera::StreamConfiguration &streamConfig = config->at(0); + libcamera::StreamConfiguration& streamConfig = config->at(0); - switch (status) { + switch (status) + { case CameraConfiguration::Status::Valid: std::cout << "config is valid" << std::endl; break; case CameraConfiguration::Status::Adjusted: - std::cout << "\tpixelFormat: " - << streamConfig.pixelFormat.toString() << std::endl; - std::cout << "\tbufferCount: " - << streamConfig.bufferCount << std::endl; - std::cout << "\torientation: " - << config->orientation << std::endl; + std::cout << "\tpixelFormat: " << streamConfig.pixelFormat.toString() + << std::endl; + std::cout << "\tbufferCount: " << streamConfig.bufferCount << std::endl; + std::cout << "\torientation: " << config->orientation << std::endl; break; case CameraConfiguration::Status::Invalid: std::cout << "config is invalid, quit." << std::endl; @@ -901,18 +1012,19 @@ static bool applyConfig(const std::unique_ptr<libcamera::CameraConfiguration> & static void printControls() { using namespace libcamera; - const libcamera::ControlInfoMap & control_map = camera->controls(); + const libcamera::ControlInfoMap& control_map = camera->controls(); // for (const auto & [id, info]: control_map) - for (const std::pair<const ControlId *, ControlInfo> & pair : control_map) + for (const std::pair<const ControlId*, ControlInfo>& pair : control_map) { - const ControlId * const & id = pair.first; - const ControlInfo & info = pair.second; + const ControlId* const& id = pair.first; + const ControlInfo& info = pair.second; - std::cout << "\tc " << id->name() - << " (" << id->id() + std::cout << "\tc " << id->name() << " (" << id->id() << "): " << info.toString() - << (info.def().isNone() ? "" : " (dflt:" + info.def().toString() + ")"); + << (info.def().isNone() + ? "" + : " (dflt:" + info.def().toString() + ")"); if (!info.values().size()) { @@ -922,7 +1034,7 @@ static void printControls() std::cout << " - ["; - for (const auto & v : info.values()) + for (const auto& v : info.values()) { std::cout << " " << v.toString(); } @@ -940,26 +1052,27 @@ static void dumpCalibrationPixels() std::swap(rawProfiles, ::calibrationPixels); } - const QString dumpSubdir { + const QString dumpSubdir{ QDateTime::currentDateTime().toString("yyyy.MM.dd_hh.mm.ss") }; - const QDir dumpPath { dumpsRoot + "/" + dumpSubdir }; + const QDir dumpPath{dumpsRoot + "/" + dumpSubdir}; if (!dumpPath.mkdir(dumpPath.path())) { - qWarning() << "cannot create dump dir: " << dumpPath.path(); + qWarning() << "cannot create dir: " << dumpPath.path(); return; } for (const auto& rawProfile : rawProfiles) { - const auto filename = QLatin1String("raw_profile_meas_%1_enc_%2") - .arg(QString::number(rawProfile.counters.measurementCounter)) - .arg(rawProfile.counters.encoderPosition); + const auto filename = + QLatin1String("raw_profile_meas_%1_enc_%2") + .arg(QString::number(rawProfile.counters.measurementCounter)) + .arg(rawProfile.counters.encoderPosition); const auto filepath = dumpPath.path() + "/" + filename; - QFile f { filepath }; + QFile f{filepath}; if (!f.open(QFile::WriteOnly)) { @@ -969,11 +1082,12 @@ static void dumpCalibrationPixels() return; } - QJsonObject jsonCounters { - { "timestampUs", qint64(rawProfile.counters.timestampUs) }, - { "measurementCounter", qint64(rawProfile.counters.measurementCounter) }, - { "encoderPosition", qint64(rawProfile.counters.encoderPosition) }, - }; + QJsonObject jsonCounters{ + {"timestampUs", qint64(rawProfile.counters.timestampUs)}, + {"measurementCounter", + qint64(rawProfile.counters.measurementCounter)}, + {"encoderPosition", qint64(rawProfile.counters.encoderPosition)}, + }; QJsonObject json; @@ -1002,16 +1116,16 @@ static void dumpCalibrationPixels() qDebug() << "dump finished"; } -static QList<Pixels> openDump(const QString &dumpPath) +static QList<Pixels> openDump(const QString& dumpPath) { - QString dirToRead { dumpPath }; + QString dirToRead{dumpPath}; qDebug() << "trying to open dump path:" << dirToRead; if (dirToRead.isEmpty()) { qDebug() << "dumpPath not specified. looking into" << dumpsRoot; - QDir dumpsRootDir { dumpsRoot }; + QDir dumpsRootDir{dumpsRoot}; const auto filter = QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable; // there is no battery in my rpi5 for now @@ -1029,12 +1143,12 @@ static QList<Pixels> openDump(const QString &dumpPath) dirToRead = dumpsRoot + "/" + entries.last(); } - QDir dumpDir { dirToRead }; + QDir dumpDir{dirToRead}; const auto filter = QDir::Files; const auto sort = QDir::Name; - const auto filenames = dumpDir.entryList(filter, sort); + auto filenames = dumpDir.entryList(filter, sort); if (filenames.isEmpty()) { @@ -1042,36 +1156,47 @@ static QList<Pixels> openDump(const QString &dumpPath) return {}; } - QList<std::optional<Pixels>> resultOptionals { filenames.size() }; + qDebug() << "create results array" << filenames.size(); + auto resultOptionals = + QScopedPointer(new QList<std::optional<Pixels>>(filenames.size())); QElapsedTimer t; t.start(); + qDebug() << "open real files"; std::transform( - std::execution::par, - filenames.begin(), filenames.end(), - resultOptionals.begin(), + std::execution::par_unseq, + filenames.begin(), + filenames.end(), + resultOptionals->begin(), [dirToRead](const auto& filename) { - std::cout << '.'; + // std::cout << '.'; return openRawProfile(dirToRead + "/" + filename); - }); + } + ); - std::cout << std::endl; + filenames.clear(); + filenames.squeeze(); + + qDebug() << Q_FUNC_INFO << "open raw profiles: elapsed (ms)" << t.elapsed(); + // std::cout << std::endl; std::remove_if( std::execution::par, - resultOptionals.begin(), - resultOptionals.end(), - [](auto& a) { return !a.has_value(); }); + resultOptionals->begin(), + resultOptionals->end(), + [](auto& a) { return !a.has_value(); } + ); - QList<Pixels> result { resultOptionals.size() }; + QList<Pixels> result(resultOptionals->size()); std::transform( std::execution::par, - std::make_move_iterator(resultOptionals.begin()), - std::make_move_iterator(resultOptionals.end()), + std::make_move_iterator(resultOptionals->begin()), + std::make_move_iterator(resultOptionals->end()), result.begin(), - [](auto& p) { return p.value(); }); + [](auto& p) { return p.value(); } + ); qDebug() << Q_FUNC_INFO << "elapsed (ms)" << t.elapsed(); @@ -1080,7 +1205,7 @@ static QList<Pixels> openDump(const QString &dumpPath) static std::optional<Pixels> openRawProfile(const QString& filePath) { - QFile f { filePath }; + QFile f{filePath}; if (!f.open(QFile::ReadOnly)) { @@ -1096,15 +1221,26 @@ static std::optional<Pixels> openRawProfile(const QString& filePath) Pixels result; result.counters.timestampUs = jsonCounters["timestampUs"].toInteger(); - result.counters.measurementCounter = jsonCounters["measurementCounter"].toInteger(); - result.counters.encoderPosition = jsonCounters["encoderPosition"].toInteger(); + result.counters.measurementCounter = + jsonCounters["measurementCounter"].toInteger(); + result.counters.encoderPosition = + jsonCounters["encoderPosition"].toInteger(); const auto jsonPixels = json["pixels"].toArray(); + // TODO: check json pixels count + std::transform( + // std::execution::par_unseq, + jsonPixels.constBegin(), + jsonPixels.constEnd(), + result.pixels.begin(), + [](const auto& jsonPixel) { return jsonPixel.toDouble(); } + ); - for (size_t i = 0; i < jsonPixels.count() && i < result.pixels.size(); ++i) - { - result.pixels[i] = jsonPixels[i].toDouble(); - } + // for (size_t i = 0; i < jsonPixels.count() && i < result.pixels.size(); + // ++i) + // { + // result.pixels[i] = jsonPixels[i].toDouble(); + // } return result; } @@ -1117,36 +1253,38 @@ static QList<Pixels> filter(const QList<Pixels>& rawProfiles) // for (size_t i = 0; i < 10; ++i) // { - // std::cout << "pos - " << rawProfiles.at(i).counters.encoderPosition << std::endl; + // 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; + // std::cout << "current pos is " << sum.counters.encoderPosition + // << std::endl; - printPixels(sum.pixels); + // printPixels(sum.pixels); - size_t count { 1 }; + size_t count{1}; ++it; - std::cout << "here\n"; - while (it != rawProfiles.constEnd() && it->counters.encoderPosition == sum.counters.encoderPosition) + // std::cout << "here\n"; + while (it != rawProfiles.constEnd() && + it->counters.encoderPosition == sum.counters.encoderPosition) { - std::cout << "here2\n"; - std::cout << "\tadd to pos " << it->counters.encoderPosition << std::endl; + // std::cout << "here2\n"; + // std::cout << "\tadd to pos " << it->counters.encoderPosition + // << std::endl; sum += *it; - std::cout << "\tadded" << std::endl; + // std::cout << "\tadded" << std::endl; ++count; ++it; - } - - std::cout << "here3\n"; + // std::cout << "here3\n"; sum /= float(count); - printPixels(sum.pixels); + // printPixels(sum.pixels); result << sum; @@ -1156,31 +1294,34 @@ static QList<Pixels> filter(const QList<Pixels>& rawProfiles) return result; } -static QSharedPointer<CalibrationTable> calibrate(const QList<Pixels>& rawProfiles) +static QSharedPointer<CalibrationTable> calibrateZ( + const QList<Pixels>& rawProfiles +) { - QSharedPointer<CalibrationTable> result { new CalibrationTable {{ 0 }} }; - - constexpr uint16_t discretesInRage { 16384 }; + QSharedPointer<CalibrationTable> result{new CalibrationTable{{0}}}; for (const auto& rawProfile : rawProfiles) { - std::cout << "calibration: pos is " << rawProfile.counters.encoderPosition << std::endl; - const float positionMm { + // 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); + // 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::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) / + // << std::setw(8) << + // float(rawProfile.counters.encoderPosition) / // float(requested_params.stepsPerMm) // << ": "; @@ -1189,31 +1330,34 @@ static QSharedPointer<CalibrationTable> calibrate(const QList<Pixels>& rawProfil const auto& pixelValue = pixels.at(columnIdx); // float[0, img_height] -> uint16_t[0, calibrationColumnHeight] - const uint16_t discretePixelValue { + 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()); + 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 << ";"; - } + // if (columnIdx >= ((img_width - 10) / 2) && + // columnIdx < img_width - ((img_width - 10) / 2)) + // { + // std::cout << discretePixelValue << ";"; + // } } - std::cout << std::endl << std::flush; + // std::cout << std::endl << std::flush; } return result; @@ -1221,16 +1365,123 @@ static QSharedPointer<CalibrationTable> calibrate(const QList<Pixels>& rawProfil // return {}; } +static QSharedPointer<CalibrationTable> calibrateX( + const QList<Pixels>& rawProfiles +) +{ + QSharedPointer<CalibrationTable> result{new CalibrationTable{{0}}}; + + for (const auto& rawProfile : rawProfiles) + { + const auto& pixels = rawProfile.pixels; + auto lines = pixelsToLines(rawProfile); + + Q_ASSERT_X(lines.count() > 2, Q_FUNC_INFO, "no lines"); + + QList<double> xAnchors(lines.size() + 1); + + std::transform( + std::execution::par_unseq, + lines.constBegin(), + lines.constEnd(), + xAnchors.begin(), + [](const auto& l) { return l.x1(); } + ); + + xAnchors.last() = lines.last().x2(); + + constexpr double triangleHeightMm{5.}; + constexpr double triangleBaseMm{8.}; + + auto nearestAnchorToX0 = std::min_element( + std::execution::par_unseq, + xAnchors.constBegin(), + xAnchors.constEnd(), + [](const auto& a, const auto& b) { + return std::abs(a) < std::abs(b); + } + ); + + int nearestAnchorToX0Idx = nearestAnchorToX0 - xAnchors.constBegin(); + + QList<double> xAnchorsMm(xAnchors.count()); + + for (int i = 0; i < xAnchors.size(); ++i) + { + xAnchorsMm[i] = (i - nearestAnchorToX0Idx) * triangleBaseMm / 2.; + } + + auto xAnchorIt = xAnchors.constBegin() + 1; + auto xAnchorMmIt = xAnchorsMm.constBegin() + 1; + + for (size_t columnIdx = 0; columnIdx < pixels.size(); ++columnIdx) + { + // skip points with to the left from left line and to the right from + // right line + const auto columnX = int(columnIdx) - int(img_width / 2); + if (columnX < xAnchors.first() || columnX > xAnchors.last()) + { + continue; + } + + if (columnX > *xAnchorIt) + { + ++xAnchorIt; + ++xAnchorMmIt; + } + const auto xLeft = *(xAnchorIt - 1); + const auto xRight = *(xAnchorIt); + + if (columnX < xLeft || columnX > xRight) + { + if (rawProfile.counters.encoderPosition >= 0) + { + qWarning() + << "x anchor not found" << xLeft << columnX << xRight; + continue; + } + } + + const auto& pixelValue = pixels.at(columnIdx); + 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() + ); + + const auto xLineLen = xRight - xLeft; + const auto xLeftMm = *(xAnchorMmIt - 1); + const auto xRelative = float(columnX - xLeft) / xLineLen; + const auto xMmValue = xLeftMm + xRelative * (triangleBaseMm / 2.); + + auto& calibrationColumn = (*result)[columnIdx]; + calibrationColumn[discretePixelValue] = xMmValue; + } + } + + return result; +} + bool initLaser() { - const QLatin1String pwmChip { "pwmchip2" }; - const uint16_t pwmChannel { 1 }; - const QLatin1String pwmSystemRoot { "/sys/class/pwm" }; - const QString pwmChipRoot { pwmSystemRoot + "/" + pwmChip }; + 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" }; + const QString pwmExportFile{pwmChipRoot + "/export"}; - QFile f { pwmExportFile }; + QFile f{pwmExportFile}; if (!f.open(QFile::WriteOnly)) { @@ -1240,13 +1491,13 @@ bool initLaser() return false; } - QTextStream s { &f }; + QTextStream s{&f}; s << pwmChannel; - const QString pwm { QLatin1String("pwm%1").arg(QString::number(pwmChannel)) }; - const QString pwmRoot { pwmChipRoot + "/" + pwm }; + const QString pwm{QLatin1String("pwm%1").arg(QString::number(pwmChannel))}; + const QString pwmRoot{pwmChipRoot + "/" + pwm}; - const QString periodFilename { pwmRoot + "/period" }; + const QString periodFilename{pwmRoot + "/period"}; f.close(); f.setFileName(periodFilename); @@ -1258,11 +1509,11 @@ bool initLaser() return false; } - const unsigned periodHz { 50'000 }; + const unsigned periodHz{50'000}; s << periodHz; - const QString dutyCycleFilename { pwmRoot + "/duty_cycle" }; + const QString dutyCycleFilename{pwmRoot + "/duty_cycle"}; f.close(); f.setFileName(dutyCycleFilename); @@ -1274,11 +1525,11 @@ bool initLaser() return false; } - const unsigned dutyCycle { 3'000 }; + const unsigned dutyCycle{3'000}; s << dutyCycle; - const QString enableFilename { pwmRoot + "/enable" }; + const QString enableFilename{pwmRoot + "/enable"}; f.close(); f.setFileName(enableFilename); @@ -1290,9 +1541,123 @@ bool initLaser() return false; } - const int enable { 1 }; + const int enable{1}; s << enable; return true; } + +static QImage calibrationTableToImage( + const QSharedPointer<CalibrationTable>& calibrationTable +) +{ + QImage result( + QSize(calibrationTable->size(), calibrationTable->at(0).size()), + QImage::Format::Format_Indexed8 + ); + + // QImage image(QSize(imageWidth, imageHeight), QImage::Format_Indexed8); + + QColor color(Qt::green); + auto r = color.redF(); + auto g = color.greenF(); + auto b = color.blueF(); + + for (int c = 0; c < 256; c++) + { + QRgb col = qRgb(int(c * r), int(c * g), int(c * b)); + result.setColor(c, col); + } + + int notNull = 0; + + for (size_t columnIdx = 0; columnIdx < calibrationTable->size(); + ++columnIdx) + { + const auto& column = calibrationTable->at(columnIdx); + + for (size_t rowIdx = 0; rowIdx < column.size(); ++rowIdx) + { + // if (!qFuzzyIsNull(column.at(rowIdx))) + // { + // qDebug() << "here"; + // } + + bool hasValue = !qFuzzyIsNull(column.at(rowIdx)); + + if (hasValue) + { + // qDebug() << "x/y" << columnIdx << rowIdx; + ++notNull; + } + + result.setPixel(columnIdx, rowIdx, hasValue ? 255 : 0); + } + } + qDebug() << "not null count" << notNull; + + return result; +} + +static void interpolate(QSharedPointer<CalibrationTable>& table) +{ + std::for_each( + std::execution::par, + table->begin(), + table->end(), + [](auto& column) { interpolate(column); } + ); +} + +static void interpolate(CalibrationColumn& column) +{ + auto firstNonZero = + std::find_if(column.begin(), column.end(), [](const auto& pixel) { + return !qFuzzyIsNull(pixel); + }); + + if (firstNonZero == column.end()) + { + return; + } + + for (auto it = firstNonZero; it != column.cend(); ++it) + { + auto firstZero = + std::find_if(it + 1, column.end(), [](const auto& pixel) { + return qFuzzyIsNull(pixel); + }); + + if (firstZero == column.end()) + { + return; + } + + auto nextNonZero = + std::find_if(firstZero + 1, column.end(), [](const auto& pixel) { + return !qFuzzyIsNull(pixel); + }); + + if (nextNonZero == column.end()) + { + return; + } + + auto prevNonZero = firstZero - 1; + + auto diff = *nextNonZero - *prevNonZero; + auto size = nextNonZero - prevNonZero; + auto stepDiff = float(diff) / size; + + // qDebug) << *prevNonZero << *nextNonZero << size << stepDiff; + + for (auto zero = firstZero; zero < nextNonZero; ++zero) + { + *zero = (zero - firstZero + 1) * stepDiff; + // qDebug() << "set zero to" << *zero; + } + + it = nextNonZero - 1; + } +} diff --git a/printerclient.cpp b/printerclient.cpp index c5bf189..a78de50 100644 --- a/printerclient.cpp +++ b/printerclient.cpp @@ -5,10 +5,22 @@ #include <QDebug> #include <QFile> #include <QSerialPort> +#include <QSerialPortInfo> -PrinterClient::PrinterClient(QObject *parent) - : QObject { parent } - , m_serialPort { new QSerialPort { "/dev/ttyUSB0", this } } +QString getFirstTtyUSB() +{ + auto ports = QSerialPortInfo::availablePorts(); + + std::remove_if(ports.begin(), ports.end(), [](const auto& port) { + return !port.portName().contains("ttyUSB"); + }); + + return ports.isEmpty() ? "" : ports.first().portName(); +} + +PrinterClient::PrinterClient(QObject* parent) + : QObject{parent} // , m_serialPort{new QSerialPort{"/dev/ttyUSB0", this}} + , m_serialPort{new QSerialPort{getFirstTtyUSB(), this}} { if (!m_serialPort->setBaudRate(QSerialPort::Baud115200)) { throw std::runtime_error( |
