From 32973c54ce1236e28c1b3da1c2514c1bb1528e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Mon, 30 Sep 2024 18:39:35 +0200 Subject: [PATCH] Adding platesolving --- CMakeLists.txt | 1 + httpdownloader.cpp | 78 +++++-- httpdownloader.h | 1 + imageinfo.cpp | 142 +++++++++++++ imageinfo.h | 13 ++ mainwindow.cpp | 6 + platesolving.cpp | 127 +++++++++--- platesolving.h | 5 +- platesolving.ui | 436 +++++++++++++++++++++++++++------------ platesolvingsettings.cpp | 130 ++++++++++++ platesolvingsettings.h | 27 +++ platesolvingsettings.ui | 226 ++++++++++++++++++++ rawimage.cpp | 35 +++- rawimage.h | 2 +- solver.cpp | 50 +++-- solver.h | 6 +- 16 files changed, 1075 insertions(+), 210 deletions(-) create mode 100644 platesolvingsettings.cpp create mode 100644 platesolvingsettings.h create mode 100644 platesolvingsettings.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 4226fdc..388aa70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) target_sources(tenmon PRIVATE solver.cpp solver.h platesolving.cpp platesolving.h platesolving.ui + platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui ) endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) diff --git a/httpdownloader.cpp b/httpdownloader.cpp index 21545ce..1f1954a 100644 --- a/httpdownloader.cpp +++ b/httpdownloader.cpp @@ -2,7 +2,11 @@ #include #include #include +#include + +#ifdef PLATESOLVER #include "solver.h" +#endif // filename arcseconds range // index-4119.fits 1400–2000 @@ -135,7 +139,7 @@ static const QMap md5 = { {"index-5202-45.fits.zst", "83fe2cff3cf65317f5c1bf7b953519e9"}, {"index-5202-46.fits.zst", "f12f308a3b53d95ffd7bc420700e4f44"}, {"index-5202-47.fits.zst", "608a14303810c9762b25fc68896d2a26"}, - {"index-5203-00.fits.zst", "155217faa535550c1d7e499145750bb0"}, + {"index-5203-00.fits.zst", "2862efb33765b7bbefb635dcad970298"}, {"index-5203-01.fits.zst", "2cd34cef4b44ad1e770396baccb2a46c"}, {"index-5203-02.fits.zst", "40c9f67282210cc374281cde023c4e71"}, {"index-5203-03.fits.zst", "c8f40e164ec3ce1df92e3a121a127716"}, @@ -368,20 +372,32 @@ void Download::readData() void Download::finished() { - QByteArray data = _reply->readAll(); - qDebug() << "finished" << data.size(); - decompress(data); - - if(md5.contains(_reply->url().fileName())) + if(_reply->error() == QNetworkReply::NoError) { - if(_hash.result().toHex() == md5[_reply->url().fileName()]) - qDebug() << "DOWNLOAD OK"; - else - qDebug() << "DOWNLOAD BAD"; - } + QByteArray data = _reply->readAll(); + qDebug() << "finished" << data.size(); + decompress(data); - _fw.flush(); - _fw.close(); + if(md5.contains(_reply->url().fileName())) + { + if(_hash.result().toHex() == md5[_reply->url().fileName()]) + qDebug() << "DOWNLOAD OK"; + else + { + qDebug() << "DOWNLOAD BAD"; + _fw.remove(); + return; + } + } + + _fw.flush(); + _fw.close(); + } + else + { + qDebug() << "Failed to perform http request" << _reply->url(); + _fw.remove(); + } } void Download::decompress(QByteArray &data) @@ -413,7 +429,9 @@ void Download::decompress(QByteArray &data) HttpDownloader::HttpDownloader(QObject *parent) : QObject(parent) ,_manager(new QNetworkAccessManager(this)) { + _manager->setAutoDeleteReplies(true); connect(_manager, &QNetworkAccessManager::finished, this, &HttpDownloader::finished); +#ifdef PLATESOLVER QDir dir(Solver::getTenmonIndexPath()); if(!dir.exists()) { @@ -422,6 +440,7 @@ HttpDownloader::HttpDownloader(QObject *parent) : QObject(parent) } _indexPath = dir.absolutePath(); +#endif } void HttpDownloader::download(const QUrl &url) @@ -437,17 +456,17 @@ bool HttpDownloader::downloadIndex(int scale) return false; QUrl url("https://nouspiro.space/"); + QStringList files = indexFileNames(scale); - if(scale >= 7) + for(auto &file : files) { - url.setPath(QString("/astrometry/index-%1.fits.zst").arg(4100 + scale)); - download(url); - } - else - { - for(int i=0; i<48; i++) + if(QFile::exists(_indexPath + "/" + file)) { - url.setPath(QString("/astrometry/index-%1-%2.fits.zst").arg(5200 + scale).arg(i, 2, 10, QChar('0'))); + qDebug() << "File already exists, skipping" << file; + } + else + { + url.setPath("/astrometry/" + file + ".zst"); download(url); } } @@ -460,6 +479,22 @@ void HttpDownloader::abort() _download->abort(); } +QStringList HttpDownloader::indexFileNames(int scale) +{ + QStringList ret; + + if(scale >= 7) + { + ret.append(QString("index-%1.fits").arg(4100 + scale)); + } + else + { + for(int i=0; i<48; i++) + ret.append(QString("index-%1-%2.fits").arg(5200 + scale).arg(i, 2, 10, QChar('0'))); + } + return ret; +} + void HttpDownloader::finished() { if(_queue.isEmpty()) @@ -478,7 +513,6 @@ void HttpDownloader::finished() return; } QNetworkRequest request(url); - _download->deleteLater(); _download = new Download(_manager->get(request), _indexPath, this); connect(_download, &Download::progress, this, &HttpDownloader::updateProgress); } diff --git a/httpdownloader.h b/httpdownloader.h index b255943..6c58eda 100644 --- a/httpdownloader.h +++ b/httpdownloader.h @@ -41,6 +41,7 @@ public: // scale in range 19-1 bool downloadIndex(int scale); void abort(); + static QStringList indexFileNames(int scale); signals: void progress(int percent, int files); protected slots: diff --git a/imageinfo.cpp b/imageinfo.cpp index acacc9b..3b85921 100644 --- a/imageinfo.cpp +++ b/imageinfo.cpp @@ -257,6 +257,33 @@ void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, dou } } +double hav(double x) +{ + return (1.0 - std::cos(x)) * 0.5; +} + +double haverSine(const SkyPoint &a, SkyPoint &b) +{ + const double ToRAD = M_PI / 180.0; + double d = hav((a.DEC() - b.DEC()) * ToRAD) + std::cos(a.DEC() * ToRAD) * std::cos(b.DEC() * ToRAD) * hav((a.RA() - b.RA()) * ToRAD); + return std::acos(1.0 - 2.0 * d) * (180.0 / M_PI); +} + +SkyPointScale WCSDataT::getRaDecScale() const +{ + SkyPointScale ret; + pixelToWorld(QPointF(width/2.0, height/2.0), ret.point); + SkyPoint pointX; + SkyPoint pointY; + pixelToWorld(QPointF(width/2.0+1, height/2.0), pointX); + pixelToWorld(QPointF(width/2.0, height/2.0+1), pointY); + double scaleX = haverSine(ret.point, pointX) * 3600.0; + double scaleY = haverSine(ret.point, pointY) * 3600.0; + ret.scaleLow = std::min(scaleX, scaleY); + ret.scaleHigh = std::max(scaleX, scaleY); + return ret; +} + SkyPoint::SkyPoint() : ra(NAN), dec(NAN) { } @@ -284,3 +311,118 @@ QString SkyPoint::toString() const sec = std::modf(min, &min) * 60; return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0'); } + +double SkyPoint::fromHMS(const QString &hms) +{ + double deg = fromDMS(hms); + if(std::isnan(deg))return deg; + return deg * 15.0; +} + +double SkyPoint::fromDMS(const QString &dms) +{ + double deg = 0.0; + QString str = dms.trimmed(); + str.remove(QRegularExpression("[hdms°'\"]")); + str.replace(':', ' '); + str.replace(QRegularExpression("\\s+"), " "); + QStringList fields = str.split(' '); + double sign = 1.0; + + bool ok = false; + if(fields.size() >= 1) + deg = fields.at(0).toDouble(&ok); + if(!ok)return NAN; + if(deg < 0.0) + sign = -1.0; + if(fields.size() >= 2) + deg += sign * fields.at(1).toDouble() / 60.0; + if(fields.size() >= 3) + deg += sign * fields.at(2).toDouble() / 3600.0; + + return deg; +} + +SkyPointScale ImageInfoData::getCenterRaDec() const +{ + SkyPointScale ret; + if(wcs && wcs->valid()) + { + ret = wcs->getRaDecScale(); + } + else + { + double ra,dec,focalLen,scale,pixSizeX,pixSizeY; + int binX = 1; + int binY = 1; + ra = dec = focalLen = scale = pixSizeX = pixSizeY = NAN; + bool ok; + for(const FITSRecord &header : fitsHeader) + { + if(header.key == "OBJCTRA") + { + double tmp = SkyPoint::fromHMS(header.value.toString()); + if(!std::isnan(tmp))ra = tmp; + } + else if(header.key == "RA" && std::isnan(ra)) + { + double tmp = header.value.toDouble(&ok); + if(ok)ra = tmp; + } + else if(header.key == "OBJCTDEC") + { + double tmp = SkyPoint::fromDMS(header.value.toString()); + if(!std::isnan(tmp))dec = tmp; + } + else if(header.key == "DEC" && std::isnan(dec)) + { + double tmp = SkyPoint::fromDMS(header.value.toString()); + if(!std::isnan(tmp))dec = tmp; + } + else if(header.key == "SCALE") + { + double tmp = header.value.toDouble(&ok); + if(ok)scale = tmp; + } + else if(header.key == "FOCALLEN") + { + double tmp = header.value.toDouble(&ok); + if(ok)focalLen = tmp; + } + else if(header.key == "PIXSIZE1" || header.key == "XPIXSZ") + { + pixSizeX = header.value.toDouble(); + } + else if(header.key == "PIXSIZE2" || header.key == "YPIXSZ") + { + pixSizeY = header.value.toDouble(); + } + else if(header.key == "XBINNING") + { + int tmp = header.value.toInt(&ok); + if(ok)binX = tmp; + } + else if(header.key == "YBINNING") + { + int tmp = header.value.toInt(&ok); + if(ok)binY = tmp; + } + } + + ret.point.set(ra, dec); + if(!std::isnan(scale)) + { + ret.scaleLow = ret.scaleHigh = scale; + } + else if(!(std::isnan(focalLen) || std::isnan(pixSizeX) || std::isnan(pixSizeY))) + { + const double r = 206.2648097656; // (180 * 3600) / (1000 * pi) magic number to convert pixel size to focal length ratio to arcsec. + ret.scaleLow = std::min(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); + ret.scaleHigh = std::max(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); + } + } + + ret.scaleLow *= 0.8; + ret.scaleHigh *= 1.2; + return ret; +} diff --git a/imageinfo.h b/imageinfo.h index a422055..3439f28 100644 --- a/imageinfo.h +++ b/imageinfo.h @@ -31,8 +31,19 @@ public: SkyPoint(double ra, double dec); void set(double ra, double dec); double RA() const { return ra; } + double RAHour() const { return ra / 15.0; } double DEC() const { return dec; } QString toString() const; + static double fromHMS(const QString &hms); + static double fromDMS(const QString &dms); +}; + +struct SkyPointScale +{ + SkyPoint point; + //arcsec per pixel + double scaleLow = 0.0; + double scaleHigh = 10000.0; }; class WCSDataT @@ -51,6 +62,7 @@ public: bool worldToPixel(const SkyPoint &point, QPointF &pixel) const; void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const; bool valid() const { return wcs; }; + SkyPointScale getRaDecScale() const; }; struct ImageInfoData @@ -58,6 +70,7 @@ struct ImageInfoData QVector fitsHeader; QVector> info; std::shared_ptr wcs; + SkyPointScale getCenterRaDec() const; }; Q_DECLARE_METATYPE(ImageInfoData); diff --git a/mainwindow.cpp b/mainwindow.cpp index c1eb171..e5cd4e4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -108,8 +108,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) m_databaseView = new DataBaseView(m_database, this); connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString))); +#ifdef PLATESOLVER _plateSolving = new PlateSolving(this); addDockWidget(Qt::RightDockWidgetArea, _plateSolving); +#endif addToolBar(Qt::TopToolBarArea, m_stretchPanel); @@ -146,7 +148,9 @@ 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); +#ifdef PLATESOLVER connect(m_ringList, &ImageRingList::pixmapLoaded, _plateSolving, &PlateSolving::imageLoaded); +#endif connect(m_image, &ImageScrollArea::fileDropped, this, static_cast(&MainWindow::loadFile)); QMenu *fileMenu = new QMenu(tr("File"), this); @@ -255,7 +259,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) dockMenu->addAction(databaseViewDock->toggleViewAction()); dockMenu->addAction(filetreeDock->toggleViewAction()); dockMenu->addAction(histogramDock->toggleViewAction()); +#ifdef PLATESOLVER dockMenu->addAction(_plateSolving->toggleViewAction()); +#endif menuBar()->addMenu(dockMenu); QMenu *helpMenu = menuBar()->addMenu(tr("Help")); diff --git a/platesolving.cpp b/platesolving.cpp index 99e5a75..e4407c0 100644 --- a/platesolving.cpp +++ b/platesolving.cpp @@ -1,45 +1,61 @@ #include "platesolving.h" +#include #include "ui_platesolving.h" #include "solver.h" #include "imageringlist.h" +#include "platesolvingsettings.h" PlateSolving::PlateSolving(QWidget *parent) : QDockWidget(parent) - , ui(new Ui::PlateSolving) + , _ui(new Ui::PlateSolving) { - ui->setupUi(this); + _ui->setupUi(this); _solver = new Solver(this); - + QSettings settings; + _solver->setIndexFolder(settings.value("platesolving/indexPath", Solver::getTenmonIndexPath()).toString()); auto profiles = StellarSolver::getBuiltInProfiles(); + int profileIdx = settings.value("platesolving/profile", 0).toInt(); + _solver->setParameters(profiles[profileIdx]); + for(auto &profile : profiles) { - ui->profileComboBox->addItem(profile.listName); + _ui->profileComboBox->addItem(profile.listName); } + _ui->profileComboBox->setCurrentIndex(profileIdx); + _ui->scaleUnit->setCurrentIndex(settings.value("platesolving/scaleUnit", 1).toInt()); - connect(ui->profileComboBox, &QComboBox::currentIndexChanged, [this](int index){ + 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(_ui->extractButton, &QPushButton::clicked, this, &PlateSolving::extract); + connect(_ui->solveButton, &QPushButton::clicked, this, &PlateSolving::solve); + connect(_ui->settingsButton, &QPushButton::clicked, this, &PlateSolving::settings); connect(_solver, &Solver::solvingDone, this, &PlateSolving::solvingDone); connect(_solver, &Solver::extractionDone, this, &PlateSolving::extractionDone); + connect(_solver, &Solver::logOutput, [this](const QString &log){ _ui->log->appendPlainText(log); }); } PlateSolving::~PlateSolving() { - delete ui; + QSettings settings; + settings.setValue("platesolving/profile", _ui->profileComboBox->currentIndex()); + settings.setValue("platesolving/scaleUnit", _ui->scaleUnit->currentIndex()); + delete _ui; } void PlateSolving::extract() { - ui->solveButton->setDisabled(true); - ui->extractButton->setDisabled(true); + if(!_rawImage)return; + _ui->solveButton->setDisabled(true); + _ui->extractButton->setDisabled(true); + _ui->log->clear(); _solver->loadImage(_rawImage, _path); - _solver->extractSources(ui->withHFR->isChecked()); + _solvingTime.start(); + _solver->extractSources(_ui->withHFR->isChecked()); } void PlateSolving::extractionDone() @@ -61,49 +77,92 @@ void PlateSolving::extractionDone() hfr /= size; } - ui->stars->setText(QString::number(stars.size())); - ui->hfr->setText(QString("%1 %2x%3").arg(hfr).arg(a).arg(b)); + _ui->stars->setText(QString::number(stars.size())); + _ui->hfr->setText(QString("%1 %2x%3").arg(hfr).arg(a).arg(b)); + _ui->log->appendPlainText(QString("Extraction finished in %1 ms").arg(_solvingTime.elapsed())); - ui->solveButton->setDisabled(false); - ui->extractButton->setDisabled(false); + _ui->solveButton->setDisabled(false); + _ui->extractButton->setDisabled(false); } void PlateSolving::solve() { - ui->solveButton->setDisabled(true); - ui->extractButton->setDisabled(true); + if(!_rawImage)return; + _ui->solveButton->setDisabled(true); + _ui->extractButton->setDisabled(true); + _ui->log->clear(); _solver->loadImage(_rawImage, _path); + if(_ui->usePosition->isChecked()) + _solver->setSearchPosition(_ui->raStart->value(), _ui->decStart->value()); + + if(_ui->useScale->isChecked()) + { + SSolver::ScaleUnits scaleUnit; + switch(_ui->scaleUnit->currentIndex()) + { + default: + case 0: + scaleUnit = SSolver::ScaleUnits::DEG_WIDTH; break; + case 1: + scaleUnit = SSolver::ScaleUnits::ARCMIN_WIDTH; break; + case 2: + scaleUnit = SSolver::ScaleUnits::ARCSEC_PER_PIX; break; + case 3: + scaleUnit = SSolver::ScaleUnits::FOCAL_MM; break; + } + _solver->setSearchScale(_ui->fovLow->value(), _ui->fovHigh->value(), scaleUnit); + } + _solvingTime.start(); _solver->solveImage(); } void PlateSolving::solvingDone() { - ui->solveButton->setDisabled(false); - ui->extractButton->setDisabled(false); + _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)); + _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)); + _ui->log->appendPlainText(QString("Solving finished in %1 ms").arg(_solvingTime.elapsed())); } void PlateSolving::imageLoaded(Image *image) { - if(image) + if(image && image->rawImage()) { _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(); + _ui->ra->clear(); + _ui->dec->clear(); + _ui->orientation->clear(); + _ui->fieldWidth->clear(); + _ui->fieldHeight->clear(); + _ui->pixelScale->clear(); + _ui->hfr->clear(); + _ui->stars->clear(); + + const ImageInfoData &info = image->info(); + SkyPointScale pointScale = info.getCenterRaDec(); + if(!std::isnan(pointScale.point.RA()) && !std::isnan(pointScale.point.DEC())) + { + _ui->raStart->setValue(pointScale.point.RAHour()); + _ui->decStart->setValue(pointScale.point.DEC()); + } + + _ui->scaleUnit->setCurrentIndex(2); + _ui->fovLow->setValue(pointScale.scaleLow); + _ui->fovHigh->setValue(pointScale.scaleHigh); } } + +void PlateSolving::settings() +{ + PlateSolvingSettings settings(this); + settings.exec(); +} diff --git a/platesolving.h b/platesolving.h index d3dc36c..7cd0306 100644 --- a/platesolving.h +++ b/platesolving.h @@ -1,6 +1,7 @@ #ifndef PLATESOLVING_H #define PLATESOLVING_H +#include "qelapsedtimer.h" #include class Solver; @@ -17,6 +18,7 @@ class PlateSolving : public QDockWidget Solver *_solver; std::shared_ptr _rawImage; QString _path; + QElapsedTimer _solvingTime; public: explicit PlateSolving(QWidget *parent = nullptr); ~PlateSolving(); @@ -27,8 +29,9 @@ public slots: void solve(); void solvingDone(); void imageLoaded(Image *image); + void settings(); private: - Ui::PlateSolving *ui; + Ui::PlateSolving *_ui; }; #endif // PLATESOLVING_H diff --git a/platesolving.ui b/platesolving.ui index 6dba071..9209a5e 100644 --- a/platesolving.ui +++ b/platesolving.ui @@ -6,8 +6,8 @@ 0 0 - 571 - 472 + 860 + 700 @@ -30,153 +30,315 @@ - - - - - Field height - - - - - - - Pixel scale - - - - - - - RA - - - - - - - true - - - - - - - Orientation - - - - - - - DEC - - - - - - - true - - - - - - - true - - - - - - - Field width - - - - - - - true - - - - - - - true - - - - - - - true - - - - - - - Stars - - - - - - - true - - - - - - - HFR - - - - - - - true - - - - + + + Start point + + + + + + + Degree width + + + + + Arcmin width + + + + + Arcsec per pixel + + + + + 35 mm equivalent focal length + + + + + + + + Use position + + + + + + + 3 + + + 100000.000000000000000 + + + QAbstractSpinBox::AdaptiveDecimalStepType + + + 10000.000000000000000 + + + + + + + DEC + + + + + + + h + + + 3 + + + 24.000000000000000 + + + + + + + High + + + + + + + 3 + + + 10000.000000000000000 + + + QAbstractSpinBox::AdaptiveDecimalStepType + + + + + + + + 0 + 0 + + + + Use scale + + + + + + + Qt::Horizontal + + + + + + + deg + + + -90.000000000000000 + + + 90.000000000000000 + + + + + + + Low + + + + + + + RA + + + + + + + Unit + + + + + + + + + + Solution + + + + + + RA + + + + + + + true + + + + + + + DEC + + + + + + + true + + + + + + + Field width + + + + + + + true + + + + + + + Field height + + + + + + + true + + + + + + + Orientation + + + + + + + true + + + + + + + Pixel scale + + + + + + + true + + + + + + + Stars + + + + + + + true + + + + + + + HFR + + + + + + + true + + + + + + + + + + true + + - - - Extract - - - - - - - Solve - - - - Extract with HFR + + + + Extract + + + + + + + Solve + + + + + + + Settings + + + - - - - Settings - - - diff --git a/platesolvingsettings.cpp b/platesolvingsettings.cpp new file mode 100644 index 0000000..eb55101 --- /dev/null +++ b/platesolvingsettings.cpp @@ -0,0 +1,130 @@ +#include "platesolvingsettings.h" +#include "ui_platesolvingsettings.h" +#include +#include +#include "solver.h" + +PlateSolvingSettings::PlateSolvingSettings(QWidget *parent) : QDialog(parent) + , _ui(new Ui::PlateSolvingSettings) +{ + _ui->setupUi(this); + + _download = new HttpDownloader(this); + connect(_download, &HttpDownloader::progress, this, &PlateSolvingSettings::progress); + connect(_ui->stopDownloadButton, &QPushButton::clicked, _download, &HttpDownloader::abort); + + connect(_ui->scale01, &QCheckBox::clicked, [this](){ if(_ui->scale01->isChecked())_download->downloadIndex(1); }); + connect(_ui->scale02, &QCheckBox::clicked, [this](){ if(_ui->scale02->isChecked())_download->downloadIndex(2); }); + connect(_ui->scale03, &QCheckBox::clicked, [this](){ if(_ui->scale03->isChecked())_download->downloadIndex(3); }); + connect(_ui->scale04, &QCheckBox::clicked, [this](){ if(_ui->scale04->isChecked())_download->downloadIndex(4); }); + connect(_ui->scale05, &QCheckBox::clicked, [this](){ if(_ui->scale05->isChecked())_download->downloadIndex(5); }); + connect(_ui->scale06, &QCheckBox::clicked, [this](){ if(_ui->scale06->isChecked())_download->downloadIndex(6); }); + connect(_ui->scale07, &QCheckBox::clicked, [this](){ if(_ui->scale07->isChecked())_download->downloadIndex(7); }); + connect(_ui->scale08, &QCheckBox::clicked, [this](){ if(_ui->scale08->isChecked())_download->downloadIndex(8); }); + connect(_ui->scale09, &QCheckBox::clicked, [this](){ if(_ui->scale09->isChecked())_download->downloadIndex(9); }); + connect(_ui->scale10, &QCheckBox::clicked, [this](){ if(_ui->scale10->isChecked())_download->downloadIndex(10); }); + connect(_ui->scale11, &QCheckBox::clicked, [this](){ if(_ui->scale11->isChecked())_download->downloadIndex(11); }); + connect(_ui->scale12, &QCheckBox::clicked, [this](){ if(_ui->scale12->isChecked())_download->downloadIndex(12); }); + connect(_ui->scale13, &QCheckBox::clicked, [this](){ if(_ui->scale13->isChecked())_download->downloadIndex(13); }); + connect(_ui->scale14, &QCheckBox::clicked, [this](){ if(_ui->scale14->isChecked())_download->downloadIndex(14); }); + connect(_ui->scale15, &QCheckBox::clicked, [this](){ if(_ui->scale15->isChecked())_download->downloadIndex(15); }); + connect(_ui->scale16, &QCheckBox::clicked, [this](){ if(_ui->scale16->isChecked())_download->downloadIndex(16); }); + connect(_ui->scale17, &QCheckBox::clicked, [this](){ if(_ui->scale17->isChecked())_download->downloadIndex(17); }); + connect(_ui->scale18, &QCheckBox::clicked, [this](){ if(_ui->scale18->isChecked())_download->downloadIndex(18); }); + connect(_ui->scale19, &QCheckBox::clicked, [this](){ if(_ui->scale19->isChecked())_download->downloadIndex(19); }); + + QSettings settings; + _ui->indexPaths->addItems(settings.value("platesolving/indexPaths", Solver::getIndexPaths()).toStringList()); + _ui->indexPaths->setCurrentText(settings.value("platesolving/indexPath", Solver::getTenmonIndexPath()).toString()); + connect(_ui->addButton, &QPushButton::clicked, [this](){ + QString path = QFileDialog::getExistingDirectory(this, tr("Index files directory"), Solver::getTenmonIndexPath()); + if(!path.isEmpty()) + { + bool contain = false; + for(int i=0; i<_ui->indexPaths->count(); i++) + { + if(path == _ui->indexPaths->itemText(i)) + { + contain = true; + break; + } + } + + if(!contain)_ui->indexPaths->addItem(path); + } + }); + connect(_ui->removeButton, &QPushButton::clicked, [this](){ + int current = _ui->indexPaths->currentIndex(); + if(current > 0)_ui->indexPaths->removeItem(current); + }); + + _watcher = new QFileSystemWatcher(this); + _watcher->addPath(Solver::getTenmonIndexPath()); + connect(_watcher, &QFileSystemWatcher::directoryChanged, this, &PlateSolvingSettings::checkIndexFiles); + connect(_ui->indexPaths, &QComboBox::currentTextChanged, [this](const QString &text){ + _watcher->removePaths(_watcher->directories()); + _watcher->addPath(text); + }); + connect(_ui->indexPaths, &QComboBox::currentIndexChanged, [this](int index){ + _ui->indexFilesGroup->setEnabled(index == 0); + }); + checkIndexFiles(); +} + +PlateSolvingSettings::~PlateSolvingSettings() +{ + QSettings settings; + settings.setValue("platesolving/indexPath", _ui->indexPaths->currentText()); + QStringList paths; + for(int i=0; i<_ui->indexPaths->count(); i++) + paths.append(_ui->indexPaths->itemText(i)); + + settings.setValue("platesolving/indexPaths", paths); + delete _ui; +} + +void PlateSolvingSettings::checkIndexFiles() +{ + QString indexDir = Solver::getTenmonIndexPath() + "/"; + auto checkScale = [indexDir](QCheckBox *box, int scale) + { + bool all = true; + QStringList files = HttpDownloader::indexFileNames(scale); + for(auto &file : files) + if(!QFile::exists(indexDir + file)) + { + all = false; + break; + } + + box->setChecked(all); + if(all)box->setStyleSheet("color: green; font: bold;"); + else box->setStyleSheet(""); + }; + + checkScale(_ui->scale01, 1); + checkScale(_ui->scale02, 2); + checkScale(_ui->scale03, 3); + checkScale(_ui->scale04, 4); + checkScale(_ui->scale05, 5); + checkScale(_ui->scale06, 6); + checkScale(_ui->scale07, 7); + checkScale(_ui->scale08, 8); + checkScale(_ui->scale09, 9); + checkScale(_ui->scale10, 10); + checkScale(_ui->scale11, 11); + checkScale(_ui->scale12, 12); + checkScale(_ui->scale13, 13); + checkScale(_ui->scale14, 14); + checkScale(_ui->scale15, 15); + checkScale(_ui->scale16, 16); + checkScale(_ui->scale17, 17); + checkScale(_ui->scale18, 18); + checkScale(_ui->scale19, 19); +} + +void PlateSolvingSettings::progress(int percent, int files) +{ + _ui->filesRemaining->setText(tr("%1 files").arg(files)); + _ui->downloadProgressbar->setValue(percent); +} diff --git a/platesolvingsettings.h b/platesolvingsettings.h new file mode 100644 index 0000000..7102411 --- /dev/null +++ b/platesolvingsettings.h @@ -0,0 +1,27 @@ +#ifndef PLATESOLVINGSETTINGS_H +#define PLATESOLVINGSETTINGS_H + +#include +#include +#include "httpdownloader.h" + +namespace Ui { +class PlateSolvingSettings; +} + +class PlateSolvingSettings : public QDialog +{ + Q_OBJECT + HttpDownloader *_download; + QFileSystemWatcher *_watcher; +public: + explicit PlateSolvingSettings(QWidget *parent = nullptr); + ~PlateSolvingSettings(); + void checkIndexFiles(); +protected slots: + void progress(int percent, int files); +private: + Ui::PlateSolvingSettings *_ui; +}; + +#endif // PLATESOLVINGSETTINGS_H diff --git a/platesolvingsettings.ui b/platesolvingsettings.ui new file mode 100644 index 0000000..ef4481a --- /dev/null +++ b/platesolvingsettings.ui @@ -0,0 +1,226 @@ + + + PlateSolvingSettings + + + + 0 + 0 + 787 + 479 + + + + Dialog + + + + + + + + + 1 + 0 + + + + + + + + Add + + + + + + + Remove + + + + + + + + + <html><head/><body><p>Plate solving need index files in order to solve an image. You can download them here by checking. This is possible only to default location.</p><p>It is required to download index files that cover 100%-50% field of view and recomended 100%-10%. So for images with 70' field of view it is required to download index files in 30'-85' and recomended 4'-85'.</p></body></html> + + + true + + + + + + + Index files + + + + + + 240' - 340'index-4114.fits (1.4 MiB) + + + + + + + 680' - 1000' index-4117.fits (242 kiB) + + + + + + + 480' - 680' index-4116.fits (400 kiB) + + + + + + + 1000' - 1400' index-4118.fits (183 kiB) + + + + + + + 340' - 480' index-4115.fits (723 kiB) + + + + + + + 1400' - 2000' index-4119.fits (141 kiB) + + + + + + + 120' - 170' index-4112.fits (5.1MiB) + + + + + + + 170' - 240' index-4113.fits (2.7MiB) + + + + + + + 85' - 120' index-4111.fits (9.8 MiB) + + + + + + + 60' - 85' index-4110.fits (24 MiB) + + + + + + + 42' - 60' index-4109.fits (48 MiB) + + + + + + + 30' - 42' index-4108.fits (91 MiB) + + + + + + + 22' - 30' index-4107.fits (158 MiB) + + + + + + + 16' - 22' index-5206-*.fits (294 MiB) + + + + + + + 11' - 16' index-5205-*.fits (587 MiB) + + + + + + + 8' - 11' index-5204-*.fits (1.2 GiB) + + + + + + + 4.0' - 5.6' index-5203-*.fits (2.3 GiB) + + + + + + + 5.6' - 8.0' index-5202-*.fits (4.6 GiB) + + + + + + + 2.0' - 2.8' index-5201-*.fits (8.9 GiB) + + + + + + + + + + + + 0 files + + + + + + + 0 + + + + + + + Stop download + + + + + + + + + + diff --git a/rawimage.cpp b/rawimage.cpp index eed6fd9..c23ccd3 100644 --- a/rawimage.cpp +++ b/rawimage.cpp @@ -3,8 +3,8 @@ #include #include #include +#include #include -//#include using F16 = qfloat16; @@ -804,6 +804,39 @@ std::shared_ptr RawImage::fromPlanar(const void *pixels, uint32_t w, u return image; } +std::shared_ptr RawImage::toPlanar() +{ + std::shared_ptr ret = std::make_shared(m_width, m_height, 1, m_type); + size_t size = m_width * m_height; + size_t ch = m_ch; + + auto convert = [&](auto *in, auto *out) + { + for(size_t i=0; i(data()), static_cast(ret->data())); + break; + case UINT16: + case FLOAT16: + convert(static_cast(data()), static_cast(ret->data())); + break; + case UINT32: + case FLOAT32: + convert(static_cast(data()), static_cast(ret->data())); + break; + case FLOAT64: + convert(static_cast(data()), static_cast(ret->data())); + break; + } + + return ret; +} + std::vector RawImage::split() const { std::vector planes; diff --git a/rawimage.h b/rawimage.h index 6ee6ec3..2b91975 100644 --- a/rawimage.h +++ b/rawimage.h @@ -8,7 +8,6 @@ #include #include #include -#include extern int THUMB_SIZE; extern int THUMB_SIZE_BORDER; @@ -114,6 +113,7 @@ public: static std::shared_ptr fromPlanar(const RawImage &img); static std::shared_ptr fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type); + std::shared_ptr toPlanar(); static size_t typeSize(DataType type); std::vector split() const; bool valid() const; diff --git a/solver.cpp b/solver.cpp index 5ea4844..a92a13c 100644 --- a/solver.cpp +++ b/solver.cpp @@ -12,11 +12,10 @@ Solver::Solver(QObject *parent) : QObject(parent) { _solver = new StellarSolver(this); - - QStringList indexFolder = getIndexPaths(); + connect(_solver, &StellarSolver::logOutput, this, &Solver::logOutput); _solver->setProperty("ProcessType", SSolver::SOLVE); - _solver->setIndexFolderPaths(indexFolder); + _solver->setIndexFolderPaths(QStringList(getTenmonIndexPath())); _solver->setParameterProfile(SSolver::Parameters::ALL_STARS); connect(_solver, &StellarSolver::finished, this, &Solver::finished); @@ -26,9 +25,9 @@ Solver::~Solver() { } -void Solver::setIndexFolders(const QStringList &indexPaths) +void Solver::setIndexFolder(const QString &indexPath) { - _solver->setIndexFolderPaths(indexPaths); + _solver->setIndexFolderPaths(QStringList(indexPath)); } bool Solver::loadImage(const QString &path) @@ -46,6 +45,11 @@ bool Solver::loadImage(const QString &path) bool Solver::loadImage(std::shared_ptr &image, const QString &path) { _rawImage = image; + if(_rawImage->channels() > 1) + _rawImagePlanar = _rawImage->toPlanar(); + else + _rawImagePlanar = _rawImage; + switch(_rawImage->type()) { case RawImage::UINT8: @@ -68,13 +72,15 @@ bool Solver::loadImage(std::shared_ptr &image, const QString &path) return false; break; } - _stats.bytesPerPixel = _rawImage->typeSize(_rawImage->type()); - _stats.channels = _rawImage->channels(); - _stats.width = _rawImage->width(); - _stats.height = _rawImage->height(); + _stats.bytesPerPixel = _rawImage->typeSize(_rawImagePlanar->type()); + _stats.channels = _rawImagePlanar->channels(); + _stats.width = _rawImagePlanar->width(); + _stats.height = _rawImagePlanar->height(); _stats.samples_per_channel = _stats.width * _stats.height; - _loaded = _solver->loadNewImageBuffer(_stats, (const uint8_t*)_rawImage->data()); + _solver->clearSearchPosition(); + _solver->clearSearchScale(); + _loaded = _solver->loadNewImageBuffer(_stats, (const uint8_t*)_rawImagePlanar->data()); _path = path; return _loaded; } @@ -169,17 +175,35 @@ void Solver::updateHeader() void Solver::setParameters(Parameters::ParametersProfile profile) { - _solver->setParameterProfile(profile); + auto profileParam = _solver->getBuiltInProfiles().at(profile); + // TODO seems like any paralel is crashing with Qt6 + profileParam.partition = false; + //profileParam.inParallel = false; + _solver->setParameters(profileParam); } void Solver::setParameters(const Parameters ¶meters) { - _solver->setParameters(parameters); + auto profile = parameters; + // TODO seems like any paralel is crashing with Qt6 + profile.partition = false; + //profile.inParallel = false; + _solver->setParameters(profile); +} + +void Solver::setSearchScale(double fovLow, double fowHigh, SSolver::ScaleUnits units) +{ + _solver->setSearchScale(fovLow, fowHigh, units); +} + +void Solver::setSearchPosition(double ra, double dec) +{ + _solver->setSearchPositionRaDec(ra, dec); } QStringList Solver::getIndexPaths() { - QStringList paths;// = StellarSolver::getDefaultIndexFolderPaths(); + QStringList paths = StellarSolver::getDefaultIndexFolderPaths(); paths.prepend(getTenmonIndexPath()); return paths; } diff --git a/solver.h b/solver.h index 87bbd0a..eadc709 100644 --- a/solver.h +++ b/solver.h @@ -15,10 +15,11 @@ class Solver : public QObject QString _path; QString _error; std::shared_ptr _rawImage; + std::shared_ptr _rawImagePlanar; public: explicit Solver(QObject *parent = nullptr); ~Solver(); - void setIndexFolders(const QStringList &list); + void setIndexFolder(const QString &indexPath); bool loadImage(const QString &path); bool loadImage(std::shared_ptr &image, const QString &path); @@ -32,6 +33,8 @@ public: void updateHeader(); void setParameters(SSolver::Parameters::ParametersProfile profile); void setParameters(const SSolver::Parameters ¶meters); + void setSearchScale(double fovLow, double fowHigh, ScaleUnits units); + void setSearchPosition(double ra, double dec); static QStringList getIndexPaths(); static QString getTenmonIndexPath(); @@ -40,6 +43,7 @@ public slots: signals: void solvingDone(); void extractionDone(); + void logOutput(const QString &log); }; #endif // SOLVER_H