File indexing completed on 2024-04-28 15:09:13

0001 /*
0002     SPDX-FileCopyrightText: 2022 Hy Murveit <hy@murveit.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "solverutils.h"
0008 
0009 #include "fitsviewer/fitsdata.h"
0010 #include "Options.h"
0011 #include <QRegularExpression>
0012 #include <QUuid>
0013 
0014 SolverUtils::SolverUtils(const SSolver::Parameters &parameters, double timeoutSeconds,
0015                          SSolver::ProcessType type) :
0016     m_Parameters(parameters), m_TimeoutMilliseconds(timeoutSeconds * 1000.0), m_Type(type)
0017 {
0018     connect(&m_Watcher, &QFutureWatcher<bool>::finished, this, &SolverUtils::executeSolver, Qt::UniqueConnection);
0019     connect(&m_SolverTimer, &QTimer::timeout, this, &SolverUtils::solverTimeout, Qt::UniqueConnection);
0020 
0021     m_StellarSolver.reset(new StellarSolver());
0022     // connect(m_StellarSolver.get(), &StellarSolver::logOutput, this, &SolverUtils::newLog);
0023 }
0024 
0025 SolverUtils::~SolverUtils()
0026 {
0027     disconnect(&m_Watcher, &QFutureWatcher<bool>::finished, this, &SolverUtils::executeSolver);
0028     disconnect(&m_SolverTimer, &QTimer::timeout, this, &SolverUtils::solverTimeout);
0029     if (m_StellarSolver.get())
0030     {
0031         // disconnect(m_StellarSolver.get(), &StellarSolver::logOutput, this, &SolverUtils::newLog);
0032         disconnect(m_StellarSolver.get(), &StellarSolver::finished, this, &SolverUtils::solverDone);
0033     }
0034 }
0035 
0036 void SolverUtils::executeSolver()
0037 {
0038     runSolver(m_ImageData);
0039 }
0040 
0041 void SolverUtils::runSolver(const QString &filename)
0042 {
0043     m_ImageData.reset(new FITSData(), &QObject::deleteLater);
0044     QFuture<bool> response = m_ImageData->loadFromFile(filename);
0045     m_Watcher.setFuture(response);
0046 }
0047 
0048 void SolverUtils::setHealpix(int indexToUse, int healpixToUse)
0049 {
0050     m_IndexToUse = indexToUse;
0051     m_HealpixToUse = healpixToUse;
0052 }
0053 
0054 void SolverUtils::abort()
0055 {
0056     if (m_StellarSolver.get()) m_StellarSolver->abort();
0057 }
0058 
0059 bool SolverUtils::isRunning() const
0060 {
0061     if (!m_StellarSolver.get()) return false;
0062     return m_StellarSolver->isRunning();
0063 }
0064 
0065 void SolverUtils::getSolutionHealpix(int *indexUsed, int *healpixUsed) const
0066 {
0067     *indexUsed = m_StellarSolver->getSolutionIndexNumber();
0068     *healpixUsed = m_StellarSolver->getSolutionHealpix();
0069 }
0070 
0071 
0072 void SolverUtils::prepareSolver()
0073 {
0074     if (m_StellarSolver->isRunning())
0075         m_StellarSolver->abort();
0076     m_StellarSolver->setProperty("ProcessType", m_Type);
0077     m_StellarSolver->loadNewImageBuffer(m_ImageData->getStatistics(), m_ImageData->getImageBuffer());
0078     m_StellarSolver->setProperty("ExtractorType", Options::solveSextractorType());
0079     m_StellarSolver->setProperty("SolverType", Options::solverType());
0080     connect(m_StellarSolver.get(), &StellarSolver::finished, this, &SolverUtils::solverDone, Qt::UniqueConnection);
0081 
0082     if (m_IndexToUse >= 0)
0083     {
0084         // The would only have an effect if Options::solverType() == SOLVER_STELLARSOLVER
0085         QStringList indexFiles = StellarSolver::getIndexFiles(
0086                                      Options::astrometryIndexFolderList(), m_IndexToUse, m_HealpixToUse);
0087         m_StellarSolver->setIndexFilePaths(indexFiles);
0088     }
0089     else
0090         m_StellarSolver->setIndexFolderPaths(Options::astrometryIndexFolderList());
0091 
0092     // External program paths
0093     ExternalProgramPaths externalPaths;
0094     externalPaths.sextractorBinaryPath = Options::sextractorBinary();
0095     externalPaths.solverPath = Options::astrometrySolverBinary();
0096     externalPaths.astapBinaryPath = Options::aSTAPExecutable();
0097     externalPaths.watneyBinaryPath = Options::watneyBinary();
0098     externalPaths.wcsPath = Options::astrometryWCSInfo();
0099     m_StellarSolver->setExternalFilePaths(externalPaths);
0100 
0101     //No need for a conf file this way.
0102     m_StellarSolver->setProperty("AutoGenerateAstroConfig", true);
0103 
0104     auto params = m_Parameters;
0105     params.partition = Options::stellarSolverPartition();
0106     m_StellarSolver->setParameters(params);
0107 
0108     m_TemporaryFilename.clear();
0109 
0110     const SSolver::SolverType type = static_cast<SSolver::SolverType>(m_StellarSolver->property("SolverType").toInt());
0111     if(type == SSolver::SOLVER_LOCALASTROMETRY || type == SSolver::SOLVER_ASTAP || type == SSolver::SOLVER_WATNEYASTROMETRY)
0112     {
0113         m_TemporaryFilename = QDir::tempPath() + QString("/solver%1.fits").arg(QUuid::createUuid().toString().remove(
0114                                   QRegularExpression("[-{}]")));
0115         m_ImageData->saveImage(m_TemporaryFilename);
0116         m_StellarSolver->setProperty("FileToProcess", m_TemporaryFilename);
0117     }
0118     else if (type == SSolver::SOLVER_ONLINEASTROMETRY )
0119     {
0120         m_TemporaryFilename = QDir::tempPath() + QString("/solver%1.fits").arg(QUuid::createUuid().toString().remove(
0121                                   QRegularExpression("[-{}]")));
0122         m_ImageData->saveImage(m_TemporaryFilename);
0123         m_StellarSolver->setProperty("FileToProcess", m_TemporaryFilename);
0124         m_StellarSolver->setProperty("AstrometryAPIKey", Options::astrometryAPIKey());
0125         m_StellarSolver->setProperty("AstrometryAPIURL", Options::astrometryAPIURL());
0126     }
0127 
0128     if (m_UseScale)
0129     {
0130         // Extend search scale from 80% to 120%
0131         m_StellarSolver->setSearchScale(m_ScaleLowArcsecPerPixel * 0.8,
0132                                         m_ScaleHighArcsecPerPixel * 1.2,
0133                                         ARCSEC_PER_PIX);
0134     }
0135     else
0136         m_StellarSolver->setProperty("UseScale", false);
0137 
0138     if (m_UsePosition)
0139         m_StellarSolver->setSearchPositionInDegrees(m_raDegrees, m_decDegrees);
0140     else
0141         m_StellarSolver->setProperty("UsePosition", false);
0142 
0143     // LOG_ALL is crashy now
0144     m_StellarSolver->setLogLevel(SSolver::LOG_NONE);
0145     m_StellarSolver->setSSLogLevel(SSolver::LOG_OFF);
0146 
0147     patchMultiAlgorithm(m_StellarSolver.get());
0148 }
0149 
0150 void SolverUtils::runSolver(const QSharedPointer<FITSData> &data)
0151 {
0152     // Limit the time the solver can run.
0153     m_SolverTimer.setSingleShot(true);
0154     m_SolverTimer.setInterval(m_TimeoutMilliseconds);
0155     m_SolverTimer.start();
0156     // Somehow m_SolverTimer's elapsed time can be greater than the interval,
0157     // so using this to get more exact times.
0158     m_StartTime = QDateTime::currentMSecsSinceEpoch();
0159 
0160     m_ImageData = data;
0161     prepareSolver();
0162     m_StellarSolver->start();
0163 }
0164 
0165 SolverUtils &SolverUtils::useScale(bool useIt, double scaleLowArcsecPerPixel, double scaleHighArcsecPerPixel)
0166 {
0167     m_UseScale = useIt;
0168     m_ScaleLowArcsecPerPixel = scaleLowArcsecPerPixel;
0169     m_ScaleHighArcsecPerPixel = scaleHighArcsecPerPixel;
0170     return *this;
0171 }
0172 
0173 SolverUtils &SolverUtils::usePosition(bool useIt, double raDegrees, double decDegrees)
0174 {
0175     m_UsePosition = useIt;
0176     m_raDegrees = raDegrees;
0177     m_decDegrees = decDegrees;
0178     return *this;
0179 }
0180 
0181 void SolverUtils::solverDone()
0182 {
0183     const double elapsed = (QDateTime::currentMSecsSinceEpoch() - m_StartTime) / 1000.0;
0184     m_SolverTimer.stop();
0185 
0186     if (m_Type == SSolver::SOLVE)
0187     {
0188         FITSImage::Solution solution;
0189         const bool success = m_StellarSolver->solvingDone() && !m_StellarSolver->failed();
0190         if (success)
0191             solution = m_StellarSolver->getSolution();
0192         emit done(false, success, solution, elapsed);
0193     }
0194     else
0195     {
0196         const bool success = m_StellarSolver->extractionDone() && !m_StellarSolver->failed();
0197         emit done(false, success, FITSImage::Solution(), elapsed);
0198     }
0199     if (!m_TemporaryFilename.isEmpty())
0200         QFile::remove(m_TemporaryFilename);
0201     m_TemporaryFilename.clear();
0202 }
0203 
0204 void SolverUtils::solverTimeout()
0205 {
0206     m_SolverTimer.stop();
0207 
0208     disconnect(m_StellarSolver.get(), &StellarSolver::finished, this, &SolverUtils::solverDone);
0209     abort();
0210 
0211     FITSImage::Solution empty;
0212     emit done(true, false, empty, m_TimeoutMilliseconds / 1000.0);
0213     if (!m_TemporaryFilename.isEmpty())
0214         QFile::remove(m_TemporaryFilename);
0215     m_TemporaryFilename.clear();
0216 }
0217 
0218 // We don't trust StellarSolver's mutli-processing algorithm MULTI_DEPTHS which is used
0219 // with multiAlgorithm==MULTI_AUTO && use_scale && !use_position.
0220 void SolverUtils::patchMultiAlgorithm(StellarSolver *solver)
0221 {
0222     if (solver && solver->property("UseScale").toBool() && !solver->property("UsePosition").toBool())
0223     {
0224         auto currentParameters = solver->getCurrentParameters();
0225         currentParameters.multiAlgorithm = NOT_MULTI;
0226         solver->setParameters(currentParameters);
0227     }
0228 }