From dccb2e88da878b7d02f303bc775e33f142f0b750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Fri, 20 Sep 2024 14:34:21 +0200 Subject: [PATCH] Working solver --- CMakeLists.txt | 5 +- httpdownloader.cpp | 11 +-- mainwindow.cpp | 7 +- mainwindow.h | 2 + platesolving.cpp | 109 ++++++++++++++++++++++++++ platesolving.h | 34 +++++++++ platesolving.ui | 185 +++++++++++++++++++++++++++++++++++++++++++++ solver.cpp | 174 ++++++++++++++++++++++++++++++++---------- solver.h | 29 ++++++- 9 files changed, 506 insertions(+), 50 deletions(-) create mode 100644 platesolving.cpp create mode 100644 platesolving.h create mode 100644 platesolving.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index aa9c2fc..4226fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,10 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) target_include_directories(tenmon PRIVATE ${STELLARSOLVER_INCLUDE}) target_link_libraries(tenmon PRIVATE ${STELLARSOLVER_LIB}) target_compile_definitions(tenmon PRIVATE "PLATESOLVER") - target_sources(tenmon PRIVATE solver.cpp solver.h) + target_sources(tenmon PRIVATE + solver.cpp solver.h + platesolving.cpp platesolving.h platesolving.ui + ) endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) option(FLATPAK "Flatpak build" OFF) diff --git a/httpdownloader.cpp b/httpdownloader.cpp index 1aa0822..21545ce 100644 --- a/httpdownloader.cpp +++ b/httpdownloader.cpp @@ -2,8 +2,7 @@ #include #include #include -#include -#include +#include "solver.h" // filename arcseconds range // index-4119.fits 1400–2000 @@ -415,15 +414,13 @@ HttpDownloader::HttpDownloader(QObject *parent) : QObject(parent) ,_manager(new QNetworkAccessManager(this)) { connect(_manager, &QNetworkAccessManager::finished, this, &HttpDownloader::finished); - QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); - if(!dir.exists("astrometry")) + QDir dir(Solver::getTenmonIndexPath()); + if(!dir.exists()) { - if(dir.mkpath("astrometry")) + if(dir.mkpath(".")) qDebug() << "Failed to create astrometry directory"; - } - dir.cd("astrometry"); _indexPath = dir.absolutePath(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index a0f7cde..c1eb171 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -108,6 +108,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) m_databaseView = new DataBaseView(m_database, this); connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString))); + _plateSolving = new PlateSolving(this); + addDockWidget(Qt::RightDockWidgetArea, _plateSolving); + addToolBar(Qt::TopToolBarArea, m_stretchPanel); QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this); @@ -143,6 +146,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(m_ringList, &ImageRingList::thumbnailLoaded, m_image, &ImageScrollArea::thumbnailLoaded); connect(m_ringList, &ImageRingList::pixmapLoaded, m_stretchPanel, &StretchToolbar::imageLoaded); connect(m_ringList, &ImageRingList::pixmapLoaded, histogram, &Histogram::imageLoaded); + connect(m_ringList, &ImageRingList::pixmapLoaded, _plateSolving, &PlateSolving::imageLoaded); connect(m_image, &ImageScrollArea::fileDropped, this, static_cast(&MainWindow::loadFile)); QMenu *fileMenu = new QMenu(tr("File"), this); @@ -251,6 +255,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) dockMenu->addAction(databaseViewDock->toggleViewAction()); dockMenu->addAction(filetreeDock->toggleViewAction()); dockMenu->addAction(histogramDock->toggleViewAction()); + dockMenu->addAction(_plateSolving->toggleViewAction()); menuBar()->addMenu(dockMenu); QMenu *helpMenu = menuBar()->addMenu(tr("Help")); @@ -709,7 +714,7 @@ void MainWindow::checkNewVersion() { QString tag = json.object().value("tag_name").toString(); QString version = getVersion(); - if(version < tag) + if(version >= tag) QMessageBox::information(this, tr("Update check"), tr("You have newest version")); else { diff --git a/mainwindow.h b/mainwindow.h index 81586d5..63cf63f 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -10,6 +10,7 @@ #include "filesystemwidget.h" #include "stretchtoolbar.h" #include "databaseview.h" +#include "platesolving.h" class MainWindow : public QMainWindow { @@ -22,6 +23,7 @@ class MainWindow : public QMainWindow FilesystemWidget *m_filesystem; Filetree *m_filetree; DataBaseView *m_databaseView; + PlateSolving *_plateSolving; static int socketPair[2]; QSocketNotifier *socketNotifier; QString _lastDir; diff --git a/platesolving.cpp b/platesolving.cpp new file mode 100644 index 0000000..99e5a75 --- /dev/null +++ b/platesolving.cpp @@ -0,0 +1,109 @@ +#include "platesolving.h" +#include "ui_platesolving.h" +#include "solver.h" +#include "imageringlist.h" + +PlateSolving::PlateSolving(QWidget *parent) + : QDockWidget(parent) + , ui(new Ui::PlateSolving) +{ + ui->setupUi(this); + + _solver = new Solver(this); + + auto profiles = StellarSolver::getBuiltInProfiles(); + for(auto &profile : profiles) + { + ui->profileComboBox->addItem(profile.listName); + } + + connect(ui->profileComboBox, &QComboBox::currentIndexChanged, [this](int index){ + auto profiles = StellarSolver::getBuiltInProfiles(); + _solver->setParameters(profiles[index]); + }); + + connect(ui->extractButton, &QPushButton::clicked, this, &PlateSolving::extract); + connect(ui->solveButton, &QPushButton::clicked, this, &PlateSolving::solve); + connect(_solver, &Solver::solvingDone, this, &PlateSolving::solvingDone); + connect(_solver, &Solver::extractionDone, this, &PlateSolving::extractionDone); +} + +PlateSolving::~PlateSolving() +{ + delete ui; +} + +void PlateSolving::extract() +{ + ui->solveButton->setDisabled(true); + ui->extractButton->setDisabled(true); + + _solver->loadImage(_rawImage, _path); + _solver->extractSources(ui->withHFR->isChecked()); +} + +void PlateSolving::extractionDone() +{ + auto stars = _solver->getStars(); + float a = 0; + float b = 0; + float hfr = 0; + for(auto &star : stars) + { + a += star.a; + b += star.b; + hfr += star.HFR; + } + if(size_t size = stars.size()) + { + a /= size; + b /= size; + hfr /= size; + } + + ui->stars->setText(QString::number(stars.size())); + ui->hfr->setText(QString("%1 %2x%3").arg(hfr).arg(a).arg(b)); + + ui->solveButton->setDisabled(false); + ui->extractButton->setDisabled(false); +} + +void PlateSolving::solve() +{ + ui->solveButton->setDisabled(true); + ui->extractButton->setDisabled(true); + + _solver->loadImage(_rawImage, _path); + _solver->solveImage(); +} + +void PlateSolving::solvingDone() +{ + ui->solveButton->setDisabled(false); + ui->extractButton->setDisabled(false); + + auto solution = _solver->getSolution(); + ui->ra->setText(QString::number(solution.ra)); + ui->dec->setText(QString::number(solution.dec)); + ui->orientation->setText(QString::number(solution.orientation)); + ui->fieldWidth->setText(QString::number(solution.fieldWidth)); + ui->fieldHeight->setText(QString::number(solution.fieldHeight)); + ui->pixelScale->setText(QString::number(solution.pixscale)); +} + +void PlateSolving::imageLoaded(Image *image) +{ + if(image) + { + _rawImage = image->rawImage(); + _path = image->name(); + ui->ra->clear(); + ui->dec->clear(); + ui->orientation->clear(); + ui->fieldWidth->clear(); + ui->fieldHeight->clear(); + ui->pixelScale->clear(); + ui->hfr->clear(); + ui->stars->clear(); + } +} diff --git a/platesolving.h b/platesolving.h new file mode 100644 index 0000000..d3dc36c --- /dev/null +++ b/platesolving.h @@ -0,0 +1,34 @@ +#ifndef PLATESOLVING_H +#define PLATESOLVING_H + +#include + +class Solver; +class RawImage; +class Image; + +namespace Ui { +class PlateSolving; +} + +class PlateSolving : public QDockWidget +{ + Q_OBJECT + Solver *_solver; + std::shared_ptr _rawImage; + QString _path; +public: + explicit PlateSolving(QWidget *parent = nullptr); + ~PlateSolving(); + +public slots: + void extract(); + void extractionDone(); + void solve(); + void solvingDone(); + void imageLoaded(Image *image); +private: + Ui::PlateSolving *ui; +}; + +#endif // PLATESOLVING_H diff --git a/platesolving.ui b/platesolving.ui new file mode 100644 index 0000000..6dba071 --- /dev/null +++ b/platesolving.ui @@ -0,0 +1,185 @@ + + + PlateSolving + + + + 0 + 0 + 571 + 472 + + + + Plate Solving + + + + + + + + + Profile + + + + + + + + + + + + + + Field height + + + + + + + Pixel scale + + + + + + + RA + + + + + + + true + + + + + + + Orientation + + + + + + + DEC + + + + + + + true + + + + + + + true + + + + + + + Field width + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + Stars + + + + + + + true + + + + + + + HFR + + + + + + + true + + + + + + + + + + + Extract + + + + + + + Solve + + + + + + + Extract with HFR + + + + + + + + + Settings + + + + + + + + + diff --git a/solver.cpp b/solver.cpp index f82de3a..5ea4844 100644 --- a/solver.cpp +++ b/solver.cpp @@ -1,5 +1,8 @@ #include "solver.h" +#include +#include #include +#include #include #include #include "rawimage.h" @@ -8,65 +11,121 @@ Solver::Solver(QObject *parent) : QObject(parent) { - _solver = std::make_unique(); + _solver = new StellarSolver(this); - QStringList indexFolder = StellarSolver::getDefaultIndexFolderPaths(); + QStringList indexFolder = getIndexPaths(); _solver->setProperty("ProcessType", SSolver::SOLVE); _solver->setIndexFolderPaths(indexFolder); _solver->setParameterProfile(SSolver::Parameters::ALL_STARS); + + connect(_solver, &StellarSolver::finished, this, &Solver::finished); } Solver::~Solver() { } -bool Solver::solveImage(const QString &path) +void Solver::setIndexFolders(const QStringList &indexPaths) { - ImageInfoData info; - std::shared_ptr rawImage; - if(loadImage(path, info, rawImage, true)) - { - _path = path; - switch(rawImage->type()) - { - case RawImage::UINT8: - _stats.dataType = TBYTE; - break; - case RawImage::UINT16: - _stats.dataType = TUSHORT; - break; - case RawImage::UINT32: - _stats.dataType = TUINT; - break; - case RawImage::FLOAT32: - _stats.dataType = TFLOAT; - break; - case RawImage::FLOAT64: - _stats.dataType = TDOUBLE; - break; - default: - _error = tr("Unsupported image data type"); - return false; - break; - } - _stats.bytesPerPixel = rawImage->typeSize(rawImage->type()); - _stats.channels = rawImage->channels(); - _stats.width = rawImage->width(); - _stats.height = rawImage->height(); - _stats.samples_per_channel = _stats.width * _stats.height; + _solver->setIndexFolderPaths(indexPaths); +} - _solver->loadNewImageBuffer(_stats, (const uint8_t*)rawImage->data()); - return _solver->solve(); +bool Solver::loadImage(const QString &path) +{ + _loaded = false; + std::shared_ptr image; + ImageInfoData info; + if(::loadImage(path, info, image, true)) + { + loadImage(image, path); } return false; } -FITSImage::Solution Solver::getSolution() const +bool Solver::loadImage(std::shared_ptr &image, const QString &path) +{ + _rawImage = image; + switch(_rawImage->type()) + { + case RawImage::UINT8: + _stats.dataType = TBYTE; + break; + case RawImage::UINT16: + _stats.dataType = TUSHORT; + break; + case RawImage::UINT32: + _stats.dataType = TUINT; + break; + case RawImage::FLOAT32: + _stats.dataType = TFLOAT; + break; + case RawImage::FLOAT64: + _stats.dataType = TDOUBLE; + break; + default: + _error = tr("Unsupported image data type"); + return false; + break; + } + _stats.bytesPerPixel = _rawImage->typeSize(_rawImage->type()); + _stats.channels = _rawImage->channels(); + _stats.width = _rawImage->width(); + _stats.height = _rawImage->height(); + _stats.samples_per_channel = _stats.width * _stats.height; + + _loaded = _solver->loadNewImageBuffer(_stats, (const uint8_t*)_rawImage->data()); + _path = path; + return _loaded; +} + +bool Solver::solveImage() +{ + if(_loaded && !_solver->isRunning()) + { + _process = SSolver::ProcessType::SOLVE; + _solver->setProperty("ProcessType", _process); + _solver->start(); + return true; + } + return false; +} + +bool Solver::extractSources(bool hfr) +{ + if(_loaded && !_solver->isRunning()) + { + _process = hfr ? SSolver::ProcessType::EXTRACT_WITH_HFR : SSolver::ProcessType::EXTRACT; + _solver->setProperty("ProcessType", _process); + _solver->start(); + return true; + } + return false; +} + +const FITSImage::Solution& Solver::getSolution() const { return _solver->getSolution(); } +const QList& Solver::getStars() const +{ + return _solver->getStarList(); +} + +double Solver::getHFR() const +{ + double hfr = 0.0; + auto stars = getStars(); + if(stars.empty())return -1.0; + + for(auto &star : stars) + { + hfr += star.HFR; + } + return hfr / stars.size(); +} + QString Solver::errorMessage() const { return _error; @@ -74,6 +133,8 @@ QString Solver::errorMessage() const void Solver::updateHeader() { + if(!_solver->solvingDone())return; + FITSImage::Solution solution = getSolution(); qDebug() << "RA" << solution.ra << "DEC" << solution.dec << "Orient" << solution.orientation << "field wxh" << solution.fieldWidth << solution.fieldHeight << solution.pixscale; qDebug() << "error" << solution.raError << solution.decError; @@ -105,3 +166,40 @@ void Solver::updateHeader() modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates")); file.modifyFITSRecords(&modify); } + +void Solver::setParameters(Parameters::ParametersProfile profile) +{ + _solver->setParameterProfile(profile); +} + +void Solver::setParameters(const Parameters ¶meters) +{ + _solver->setParameters(parameters); +} + +QStringList Solver::getIndexPaths() +{ + QStringList paths;// = StellarSolver::getDefaultIndexFolderPaths(); + paths.prepend(getTenmonIndexPath()); + return paths; +} + +QString Solver::getTenmonIndexPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/astrometry"; +} + +void Solver::finished() +{ + switch(_process) + { + case SSolver::ProcessType::SOLVE: + emit solvingDone(); + break; + case SSolver::ProcessType::EXTRACT_WITH_HFR: + case SSolver::ProcessType::EXTRACT: + emit extractionDone(); + break; + } + +} diff --git a/solver.h b/solver.h index 422e878..87bbd0a 100644 --- a/solver.h +++ b/solver.h @@ -3,20 +3,43 @@ #include +class RawImage; + class Solver : public QObject { Q_OBJECT - std::unique_ptr _solver; + StellarSolver *_solver; FITSImage::Statistic _stats; + SSolver::ProcessType _process = SSolver::SOLVE; + bool _loaded = false; QString _path; QString _error; + std::shared_ptr _rawImage; public: explicit Solver(QObject *parent = nullptr); ~Solver(); - bool solveImage(const QString &path); - FITSImage::Solution getSolution() const; + void setIndexFolders(const QStringList &list); + + bool loadImage(const QString &path); + bool loadImage(std::shared_ptr &image, const QString &path); + bool solveImage(); + bool extractSources(bool hfr); + const FITSImage::Solution& getSolution() const; + const QList& getStars() const; + double getHFR() const; + QString errorMessage() const; void updateHeader(); + void setParameters(SSolver::Parameters::ParametersProfile profile); + void setParameters(const SSolver::Parameters ¶meters); + + static QStringList getIndexPaths(); + static QString getTenmonIndexPath(); +public slots: + void finished(); +signals: + void solvingDone(); + void extractionDone(); }; #endif // SOLVER_H