diff --git a/kstars/ekos/align/offlineastrometryparser.cpp b/kstars/ekos/align/offlineastrometryparser.cpp index 93e1edc9b..61651da42 100644 --- a/kstars/ekos/align/offlineastrometryparser.cpp +++ b/kstars/ekos/align/offlineastrometryparser.cpp @@ -1,402 +1,402 @@ /* Astrometry.net Parser Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "offlineastrometryparser.h" #include "ekos_align_debug.h" #include "align.h" #include "ksutils.h" #include "Options.h" #include namespace Ekos { OfflineAstrometryParser::OfflineAstrometryParser() : AstrometryParser() { astrometryIndex[2.8] = "index-4200"; astrometryIndex[4.0] = "index-4201"; astrometryIndex[5.6] = "index-4202"; astrometryIndex[8] = "index-4203"; astrometryIndex[11] = "index-4204"; astrometryIndex[16] = "index-4205"; astrometryIndex[22] = "index-4206"; astrometryIndex[30] = "index-4207"; astrometryIndex[42] = "index-4208"; astrometryIndex[60] = "index-4209"; astrometryIndex[85] = "index-4210"; astrometryIndex[120] = "index-4211"; astrometryIndex[170] = "index-4212"; astrometryIndex[240] = "index-4213"; astrometryIndex[340] = "index-4214"; astrometryIndex[480] = "index-4215"; astrometryIndex[680] = "index-4216"; astrometryIndex[1000] = "index-4217"; astrometryIndex[1400] = "index-4218"; astrometryIndex[2000] = "index-4219"; // Reset parity on solver failure connect(this, &OfflineAstrometryParser::solverFailed, this, [&]() { parity = QString(); }); } OfflineAstrometryParser::~OfflineAstrometryParser() { } bool OfflineAstrometryParser::init() { #ifdef Q_OS_OSX if (Options::astrometryConfFileIsInternal()) KSUtils::configureDefaultAstrometry(); #endif if (astrometryFilesOK) return true; if (astrometryNetOK() == false) { if (align && align->isEnabled()) KMessageBox::information( nullptr, i18n( "Failed to find astrometry.net binaries. Please ensure astrometry.net is installed and try again."), i18n("Missing astrometry files"), "missing_astrometry_binaries_warning"); return false; } astrometryFilesOK = true; QProcess solveField; #ifdef Q_OS_OSX // Please check if this works! solveField.start("bash", QStringList() << "-c" << "solve-field --help | grep Revision"); #else solveField.start("bash", QStringList() << "-c" << "solve-field --help | grep Revision"); #endif solveField.waitForFinished(); QString output = solveField.readAllStandardOutput(); qCDebug(KSTARS_EKOS_ALIGN) << "solve-field Revision" << output; if (output.isEmpty() == false) { QString version = output.mid(9, 4); align->appendLogText(i18n("Detected Astrometry.net version %1", version)); if (version <= "0.67" && Options::astrometryUseNoFITS2FITS() == false) { Options::setAstrometryUseNoFITS2FITS(true); align->appendLogText(i18n("Setting astrometry option --no-fits2fits")); } else if (version > "0.67" && Options::astrometryUseNoFITS2FITS()) { Options::setAstrometryUseNoFITS2FITS(false); align->appendLogText(i18n("Turning off option --no-fits2fits")); } } return true; } bool OfflineAstrometryParser::astrometryNetOK() { bool solverOK = false, wcsinfoOK = false; if (Options::astrometrySolverIsInternal()) { QFileInfo solverFileInfo(QCoreApplication::applicationDirPath() + "/astrometry/bin/solve-field"); solverOK = solverFileInfo.exists() && solverFileInfo.isFile(); } else { QFileInfo solverFileInfo(Options::astrometrySolverBinary()); solverOK = solverFileInfo.exists() && solverFileInfo.isFile(); } if (Options::astrometryWCSIsInternal()) { QFileInfo wcsFileInfo(QCoreApplication::applicationDirPath() + "/astrometry/bin/wcsinfo"); wcsinfoOK = wcsFileInfo.exists() && wcsFileInfo.isFile(); } else { QFileInfo wcsFileInfo(Options::astrometryWCSInfo()); wcsinfoOK = wcsFileInfo.exists() && wcsFileInfo.isFile(); } return (solverOK && wcsinfoOK); } void OfflineAstrometryParser::verifyIndexFiles(double fov_x, double fov_y) { static double last_fov_x = 0, last_fov_y = 0; if (last_fov_x == fov_x && last_fov_y == fov_y) return; last_fov_x = fov_x; last_fov_y = fov_y; double fov_lower = 0.10 * fov_x; double fov_upper = fov_x; QStringList indexFiles; QString astrometryDataDir; bool indexesOK = true; if (getAstrometryDataDir(astrometryDataDir) == false) return; QStringList nameFilter("*.fits"); QDir directory(astrometryDataDir); QStringList indexList = directory.entryList(nameFilter); QString indexSearch = indexList.join(" "); QString startIndex, lastIndex; unsigned int missingIndexes = 0; foreach (float skymarksize, astrometryIndex.keys()) { if (skymarksize >= fov_lower && skymarksize <= fov_upper) { indexFiles << astrometryIndex.value(skymarksize); if (indexSearch.contains(astrometryIndex.value(skymarksize)) == false) { if (startIndex.isEmpty()) startIndex = astrometryIndex.value(skymarksize); lastIndex = astrometryIndex.value(skymarksize); indexesOK = false; missingIndexes++; } } } if (indexesOK == false) { if (missingIndexes == 1) align->appendLogText( i18n("Index file %1 is missing. Astrometry.net would not be able to adequately solve plates until you " "install the missing index files. Download the index files from http://www.astrometry.net", startIndex)); else align->appendLogText(i18n("Index files %1 to %2 are missing. Astrometry.net would not be able to " "adequately solve plates until you install the missing index files. Download the " "index files from http://www.astrometry.net", startIndex, lastIndex)); } } bool OfflineAstrometryParser::getAstrometryDataDir(QString &dataDir) { QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); if (confFile.open(QIODevice::ReadOnly) == false) { KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the " "configuration file full path in INDI options.", Options::astrometryConfFile())); return false; } QTextStream in(&confFile); QString line; while (!in.atEnd()) { line = in.readLine(); if (line.isEmpty() || line.startsWith('#')) continue; line = line.trimmed(); if (line.startsWith(QLatin1String("add_path"))) { dataDir = line.mid(9).trimmed(); return true; } } KMessageBox::error(0, i18n("Unable to find data dir in astrometry configuration file.")); return false; } bool OfflineAstrometryParser::startSovler(const QString &filename, const QStringList &args, bool generated) { INDI_UNUSED(generated); QStringList solverArgs = args; // Use parity if it is: 1. Already known from previous solve. 2. This is NOT a blind solve if (Options::astrometryDetectParity() && (parity.isEmpty() == false) && (args.contains("parity") == false) && (args.contains("-3") || args.contains("-L"))) solverArgs << "--parity" << parity; QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); solverArgs << "--config" << confPath; QString solutionFile = QDir::tempPath() + "/solution.wcs"; solverArgs << "-W" << solutionFile << filename; fitsFile = filename; solver.clear(); solver = new QProcess(this); #ifdef Q_OS_OSX QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH", ""); if (Options::astrometrySolverIsInternal()) { env.insert("PATH", QCoreApplication::applicationDirPath() + "/netpbm/bin:" + QCoreApplication::applicationDirPath() + "/python/bin:/usr/local/bin:" + path); env.insert("PYTHONPATH", QCoreApplication::applicationDirPath() + "/python/bin/site-packages"); } else { env.insert("PATH", "/usr/local/bin:" + path); } solver->setProcessEnvironment(env); #endif connect(solver, SIGNAL(finished(int)), this, SLOT(solverComplete(int))); connect(solver, SIGNAL(readyReadStandardOutput()), this, SLOT(logSolver())); #if QT_VERSION > QT_VERSION_CHECK(5, 6, 0) - connect(solver, &QProcess::errorOccurred, this, [&]() { + connect(solver.data(), &QProcess::errorOccurred, this, [&]() { align->appendLogText(i18n("Error starting solver: %1", solver->errorString())); emit solverFailed(); }); #else connect(solver, SIGNAL(error(QProcess::ProcessError)), this, SIGNAL(solverFailed())); #endif solverTimer.start(); QString solverPath; if (Options::astrometrySolverIsInternal()) solverPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/solve-field"; else solverPath = Options::astrometrySolverBinary(); solver->start(solverPath, solverArgs); align->appendLogText(i18n("Starting solver...")); if (Options::astrometrySolverVerbose()) { QString command = solverPath + ' ' + solverArgs.join(' '); align->appendLogText(command); } return true; } bool OfflineAstrometryParser::stopSolver() { if (solver.isNull() == false) { solver->terminate(); solver->disconnect(); } return true; } void OfflineAstrometryParser::solverComplete(int exist_status) { solver->disconnect(); // TODO use QTemporaryFile later QString solutionFile = QDir::tempPath() + "/solution.wcs"; QFileInfo solution(solutionFile); if (exist_status != 0 || solution.exists() == false) { align->appendLogText(i18n("Solver failed. Try again.")); emit solverFailed(); return; } connect(&wcsinfo, SIGNAL(finished(int)), this, SLOT(wcsinfoComplete(int))); QString wcsPath; if (Options::astrometryWCSIsInternal()) wcsPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/wcsinfo"; else wcsPath = Options::astrometryWCSInfo(); wcsinfo.start(wcsPath, QStringList(solutionFile)); } void OfflineAstrometryParser::wcsinfoComplete(int exist_status) { wcsinfo.disconnect(); if (exist_status != 0) { align->appendLogText(i18n("WCS header missing or corrupted. Solver failed.")); emit solverFailed(); return; } QString wcsinfo_stdout = wcsinfo.readAllStandardOutput(); QStringList wcskeys = wcsinfo_stdout.split(QRegExp("[\n]")); QStringList key_value; double ra = 0, dec = 0, orientation = 0, pixscale = 0; for (auto &key : wcskeys) { key_value = key.split(' '); if (key_value.size() > 1) { if (key_value[0] == "ra_center") ra = key_value[1].toDouble(); else if (key_value[0] == "dec_center") dec = key_value[1].toDouble(); else if (key_value[0] == "orientation_center") orientation = key_value[1].toDouble(); else if (key_value[0] == "pixscale") pixscale = key_value[1].toDouble(); else if (key_value[0] == "parity") parity = (key_value[1].toInt() == 0) ? "pos" : "neg"; } } int elapsed = (int)round(solverTimer.elapsed() / 1000.0); align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed)); emit solverFinished(orientation, ra, dec, pixscale); } void OfflineAstrometryParser::logSolver() { if (Options::astrometrySolverVerbose()) align->appendLogText(solver->readAll().trimmed()); } } diff --git a/kstars/ekos/profilewizard.cpp b/kstars/ekos/profilewizard.cpp index 35fb3121a..85c1537c2 100644 --- a/kstars/ekos/profilewizard.cpp +++ b/kstars/ekos/profilewizard.cpp @@ -1,299 +1,299 @@ /* Ekos Profile Wizard Copyright (C) 2017 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include "profilewizard.h" #include "kstars.h" #include "auxiliary/kspaths.h" #include "ksnotification.h" #include "qMDNS.h" ProfileWizard::ProfileWizard() : QDialog(KStars::Instance()) { setupUi(this); #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif QPixmap im; if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzekos.png"))) wizardPix->setPixmap(im); if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "windi.png"))) windiPix->setPixmap(im); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, &QDialogButtonBox::helpRequested, this, []() { KStars::Instance()->appHelpActivated(); }); connect(buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton *button) { if (button == buttonBox->button(QDialogButtonBox::Reset)) reset(); }); connect(discoverEkosB, &QPushButton::clicked, this, []() { QDesktopServices::openUrl(QUrl("http://www.indilib.org/about/ekos.html")); }); connect(videoTutorialsB, &QPushButton::clicked, this, []() { QDesktopServices::openUrl(QUrl("https://www.youtube.com/user/QAstronomy")); }); connect(INDIInfoB, &QPushButton::clicked, this, []() { QDesktopServices::openUrl(QUrl("http://indilib.org/about/discover-indi.html")); }); // Intro actions connect(introNextB, &QPushButton::clicked, this, [this]() { wizardContainer->setCurrentIndex(EQUIPMENT_LOCATION); }); // Equipment Location actions connect(localEquipmentB, SIGNAL(clicked()), this, SLOT(processLocalEquipment())); connect(remoteEquipmentB, &QPushButton::clicked, this, [this]() { wizardContainer->setCurrentIndex(REMOTE_EQUIPMENT); }); connect(stellarMateEquipmentB, &QPushButton::clicked, this, [this]() { wizardContainer->setCurrentIndex(STELLARMATE_EQUIPMENT); }); // Remote Equipment Action connect(remoteEquipmentNextB, SIGNAL(clicked()), this, SLOT(processRemoteEquipment())); // StellarMate Equipment Action connect(stellarMateEquipmentNextB, SIGNAL(clicked()), this, SLOT(processStellarMateEquipment())); #ifdef Q_OS_WIN // Auto Detect does not work on Windows yet for some reason. // Packet is sent correctly, but no replies are received from anything on the LAN outside of PC. stellarMateAutoDetectB->setEnabled(false); #else connect(stellarMateAutoDetectB, SIGNAL(clicked()), this, SLOT(detectStellarMate())); #endif // Local Windows connect(windowsReadyB, SIGNAL(clicked()), this, SLOT(processLocalWindows())); // Local Mac connect(useExternalINDIB, SIGNAL(clicked()), this, SLOT(processLocalMac())); connect(useInternalINDIB, SIGNAL(clicked()), this, SLOT(processLocalMac())); // Create Profile connect(createProfileB, SIGNAL(clicked()), this, SLOT(createProfile())); } void ProfileWizard::reset() { useInternalServer = true; useWebManager = false; useJoystick = false; useRemoteAstrometry = false; useSkySafari = false; useWatchDog = false; useGuiderType = INTERNAL_GUIDER; host.clear(); port = "7624"; wizardContainer->setCurrentIndex(INTRO); } void ProfileWizard::processLocalEquipment() { #if defined(Q_OS_OSX) wizardContainer->setCurrentIndex(MAC_LOCAL); #elif defined(Q_OS_WIN) wizardContainer->setCurrentIndex(WINDOWS_LOCAL); #else useInternalServer = true; useJoystickCheck->setEnabled(true); useRemoteAstrometryCheck->setEnabled(false); useWatchDogCheck->setEnabled(false); useSkySafariCheck->setEnabled(true); wizardContainer->setCurrentIndex(CREATE_PROFILE); #endif } void ProfileWizard::processRemoteEquipment() { bool portOK = false; remotePortEdit->text().toInt(&portOK); if (portOK == false) { KSNotification::error(i18n("Invalid port!")); return; } if (remoteHostEdit->text().isEmpty()) { KSNotification::error(i18n("Host name cannot be empty!")); return; } useInternalServer = false; host = remoteHostEdit->text(); port = remotePortEdit->text(); if (webManagerNotSureB->isChecked()) { QTcpSocket socket; // Should probably make 8624 configurable too? socket.connectToHost(host, 8624); useWebManager = socket.waitForConnected(); } else useWebManager = webManagerYesR->isChecked(); useJoystickCheck->setEnabled(true); useRemoteAstrometryCheck->setEnabled(true); useWatchDogCheck->setEnabled(true); useSkySafariCheck->setEnabled(true); wizardContainer->setCurrentIndex(CREATE_PROFILE); } void ProfileWizard::processStellarMateEquipment() { if (stellarMateHost->text().isEmpty()) { KSNotification::error(i18n("Host name cannot be empty!")); return; } host = stellarMateHost->text(); port = stellarMatePort->text(); useInternalServer = false; useWebManager = true; useJoystickCheck->setEnabled(true); useRemoteAstrometryCheck->setEnabled(true); useWatchDogCheck->setEnabled(true); useSkySafariCheck->setEnabled(true); wizardContainer->setCurrentIndex(CREATE_PROFILE); } void ProfileWizard::processLocalWindows() { useInternalServer = false; host = "localhost"; port = "7624"; useJoystickCheck->setEnabled(false); useRemoteAstrometryCheck->setEnabled(false); useWatchDogCheck->setEnabled(false); useSkySafariCheck->setEnabled(false); wizardContainer->setCurrentIndex(CREATE_PROFILE); } void ProfileWizard::processLocalMac() { QPushButton *button = qobject_cast(sender()); if (button == nullptr) return; host = "localhost"; port = "7624"; useInternalServer = (button == useInternalINDIB); useJoystickCheck->setEnabled(false); useRemoteAstrometryCheck->setEnabled(false); useWatchDogCheck->setEnabled(false); useSkySafariCheck->setEnabled(true); wizardContainer->setCurrentIndex(CREATE_PROFILE); } void ProfileWizard::createProfile() { if (profileNameEdit->text().isEmpty()) { KSNotification::error(i18n("Profile name cannot be empty!")); return; } useJoystick = useJoystickCheck->isEnabled() && useJoystickCheck->isChecked(); useWatchDog = useWatchDogCheck->isEnabled() && useWatchDogCheck->isChecked(); useSkySafari = useSkySafariCheck->isEnabled() && useSkySafariCheck->isChecked(); useRemoteAstrometry = useRemoteAstrometryCheck->isEnabled() && useRemoteAstrometryCheck->isChecked(); if (useInternalGuiderR->isChecked()) useGuiderType = INTERNAL_GUIDER; else if (usePHD2R->isChecked()) useGuiderType = PHD2_GUIDER; else useGuiderType = LIN_GUIDER; profileName = profileNameEdit->text(); if (useInternalServer) { host.clear(); port.clear(); } accept(); } QStringList ProfileWizard::selectedAuxDrivers() { QStringList auxList; if (useJoystick) auxList << "Joystick"; if (useWatchDog) auxList << "WatchDog"; if (useSkySafari) auxList << "SkySafari"; if (useRemoteAstrometry) auxList << "Astrometry"; return auxList; } int ProfileWizard::selectedExternalGuider() { return useGuiderType; } void ProfileWizard::detectStellarMate() { stellarMateDetectDialog = new QProgressDialog(this); stellarMateDetectDialog->setMinimum(0); stellarMateDetectDialog->setMaximum(0); stellarMateDetectDialog->setWindowTitle(i18n("Detecting StellarMate...")); stellarMateDetectDialog->setLabelText(i18n("Please wait while searching for StellarMate...")); stellarMateDetectDialog->show(); - connect(stellarMateDetectDialog, &QProgressDialog::canceled, [&]() + connect(stellarMateDetectDialog.data(), &QProgressDialog::canceled, [&]() { qMDNS::getInstance()->disconnect(); }); connect(qMDNS::getInstance(), SIGNAL(hostFound(QHostInfo)), this, SLOT(processHostInfo(QHostInfo))); QTimer::singleShot(120*1000, this, SLOT(detectStellarMateTimeout())); qMDNS::getInstance()->lookup("stellarmate"); } void ProfileWizard::processHostInfo(QHostInfo info) { stellarMateHost->setText(info.hostName()); qMDNS::getInstance()->disconnect(); stellarMateDetectDialog->close(); } void ProfileWizard::detectStellarMateTimeout() { if (stellarMateDetectDialog->isHidden() == false) { KSNotification::error(i18n("Failed to detect any StellarMate gadget. Make sure it is powered and on the same network.")); stellarMateDetectDialog->close(); } }