File indexing completed on 2024-04-28 03:43:09
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 ¶meters, 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 }