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

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "remoteastrometryparser.h"
0008 
0009 #include "align.h"
0010 #include "ekos_align_debug.h"
0011 #include "Options.h"
0012 #include "indi/clientmanager.h"
0013 #include "indi/driverinfo.h"
0014 #include "indi/guimanager.h"
0015 #include "indi/indidevice.h"
0016 
0017 #include <indicom.h>
0018 
0019 #include <KMessageBox>
0020 
0021 namespace Ekos
0022 {
0023 RemoteAstrometryParser::RemoteAstrometryParser() : AstrometryParser()
0024 {
0025 }
0026 
0027 bool RemoteAstrometryParser::init()
0028 {
0029     if (m_RemoteAstrometry == nullptr)
0030     {
0031         align->appendLogText(
0032             i18n("Cannot set solver to remote. The Ekos equipment profile must include the astrometry Auxiliary driver."));
0033         return false;
0034     }
0035 
0036     return true;
0037 }
0038 
0039 void RemoteAstrometryParser::verifyIndexFiles(double, double)
0040 {
0041 }
0042 
0043 bool RemoteAstrometryParser::startSolver(const QString &filename, const QStringList &args, bool generated)
0044 {
0045     INDI_UNUSED(generated);
0046 
0047     QFile fp(filename);
0048     if (fp.open(QIODevice::ReadOnly) == false)
0049     {
0050         align->appendLogText(i18n("Cannot open file %1 for reading.", filename));
0051         emit solverFailed();
0052         return false;
0053     }
0054 
0055     auto solverSwitch = m_RemoteAstrometry->getProperty("ASTROMETRY_SOLVER").getSwitch();
0056     auto solverBLOB   = m_RemoteAstrometry->getProperty("ASTROMETRY_DATA").getBLOB();
0057 
0058     if (solverSwitch->isEmpty() || solverBLOB->isEmpty())
0059     {
0060         align->appendLogText(i18n("Failed to find solver properties."));
0061         fp.close();
0062         emit solverFailed();
0063         return false;
0064     }
0065 
0066     sendArgs(args);
0067 
0068     auto enableSW = solverSwitch->findWidgetByName("ASTROMETRY_SOLVER_ENABLE");
0069     if (enableSW->getState() == ISS_OFF)
0070     {
0071         solverSwitch->reset();
0072         enableSW->setState(ISS_ON);
0073         m_RemoteAstrometry->sendNewProperty(solverSwitch);
0074     }
0075 
0076     // #PS: TODO
0077     auto bp = solverBLOB->at(0);
0078 
0079     bp->setBlobLen(fp.size());
0080     bp->setSize(fp.size());
0081 
0082     bp->setBlob(static_cast<uint8_t *>(realloc(bp->getBlob(), bp->getSize())));
0083     if (bp->getBlob() == nullptr)
0084     {
0085         align->appendLogText(i18n("Not enough memory for file %1.", filename));
0086         fp.close();
0087         emit solverFailed();
0088         return false;
0089     }
0090 
0091     memcpy(bp->blob, fp.readAll().constData(), bp->size);
0092 
0093     solverRunning = true;
0094 
0095     m_RemoteAstrometry->getDriverInfo()->getClientManager()->startBlob(solverBLOB->getDeviceName(), solverBLOB->getName(),
0096             QDateTime::currentDateTimeUtc().toString(Qt::ISODate).remove("Z").toLatin1().constData());
0097 
0098     m_RemoteAstrometry->getDriverInfo()->getClientManager()->sendOneBlob(bp);
0099 
0100     m_RemoteAstrometry->getDriverInfo()->getClientManager()->finishBlob();
0101 
0102     align->appendLogText(i18n("Starting remote solver..."));
0103     return true;
0104 }
0105 
0106 bool RemoteAstrometryParser::sendArgs(const QStringList &args)
0107 {
0108     auto solverSettings = m_RemoteAstrometry->getBaseDevice().getText("ASTROMETRY_SETTINGS");
0109 
0110     if (!solverSettings)
0111     {
0112         align->appendLogText(i18n("Failed to find solver settings."));
0113         emit solverFailed();
0114         return false;
0115     }
0116 
0117     QStringList solverArgs = args;
0118     // Add parity option if none is give and we already know parity before
0119     // and is NOT a blind solve
0120     if (Options::astrometryDetectParity() && args.contains("parity") == false &&
0121             (args.contains("-3") || args.contains("-L")))
0122         solverArgs << "--parity" << QString::number(parity);
0123 
0124     //for (int i = 0; i < solverSettings->ntp; i++)
0125     for (auto it : solverSettings)
0126     {
0127         // 2016-10-20: Disable setting this automatically since remote device might have different
0128         // settings
0129         /*if (!strcmp(solverSettings->tp[i].name, "ASTROMETRY_SETTINGS_BINARY"))
0130             IUSaveText(&solverSettings->tp[i], Options::astrometrySolver().toLatin1().constData());*/
0131         if (it.isNameMatch("ASTROMETRY_SETTINGS_OPTIONS"))
0132             it.setText(solverArgs.join(" ").toLatin1().constData());
0133     }
0134 
0135     m_RemoteAstrometry->sendNewProperty(solverSettings);
0136     INDI_D *guiDevice = GUIManager::Instance()->findGUIDevice(m_RemoteAstrometry->getDeviceName());
0137     if (guiDevice)
0138         guiDevice->updateTextGUI(solverSettings);
0139 
0140     return true;
0141 }
0142 
0143 void RemoteAstrometryParser::setEnabled(bool enable)
0144 {
0145     auto solverSwitch = m_RemoteAstrometry->getBaseDevice().getSwitch("ASTROMETRY_SOLVER");
0146 
0147     if (!solverSwitch)
0148         return;
0149 
0150     auto enableSW  = solverSwitch.findWidgetByName("ASTROMETRY_SOLVER_ENABLE");
0151     auto disableSW = solverSwitch.findWidgetByName("ASTROMETRY_SOLVER_DISABLE");
0152 
0153     if (!enableSW || !disableSW)
0154         return;
0155 
0156     if (enable && enableSW->getState() == ISS_OFF)
0157     {
0158         solverSwitch.reset();
0159         enableSW->setState(ISS_ON);
0160         m_RemoteAstrometry->sendNewProperty(solverSwitch);
0161         solverRunning = true;
0162         qCDebug(KSTARS_EKOS_ALIGN) << "Enabling remote solver...";
0163     }
0164     else if (enable == false && disableSW->s == ISS_OFF)
0165     {
0166         solverSwitch.reset();
0167         disableSW->setState(ISS_ON);
0168         m_RemoteAstrometry->sendNewProperty(solverSwitch);
0169         solverRunning = false;
0170         qCDebug(KSTARS_EKOS_ALIGN) << "Disabling remote solver...";
0171     }
0172 }
0173 
0174 bool RemoteAstrometryParser::setCCD(const QString &ccd)
0175 {
0176     targetCCD = ccd;
0177 
0178     if (!m_RemoteAstrometry)
0179         return false;
0180 
0181     auto activeDevices = m_RemoteAstrometry->getBaseDevice().getText("ACTIVE_DEVICES");
0182 
0183     if (!activeDevices)
0184         return false;
0185 
0186     auto activeCCD  = activeDevices.findWidgetByName("ACTIVE_CCD");
0187 
0188     if (!activeCCD)
0189         return false;
0190 
0191     // If same device, no need to update
0192     if (QString(activeCCD->getText()) == ccd)
0193         return true;
0194 
0195     activeCCD->setText(ccd.toLatin1().data());
0196     m_RemoteAstrometry->sendNewProperty(activeDevices);
0197 
0198     return true;
0199 }
0200 
0201 bool RemoteAstrometryParser::stopSolver()
0202 {
0203     if (solverRunning == false)
0204         return true;
0205 
0206     // Disable solver
0207     auto svp = m_RemoteAstrometry->getProperty("ASTROMETRY_SOLVER").getSwitch();
0208     if (svp->isEmpty())
0209         return false;
0210 
0211     auto disableSW = svp->findWidgetByName("ASTROMETRY_SOLVER_DISABLE");
0212     if (disableSW->getState() == ISS_OFF)
0213     {
0214         svp->reset();
0215         disableSW->setState(ISS_ON);
0216         m_RemoteAstrometry->sendNewProperty(svp);
0217     }
0218 
0219     solverRunning = false;
0220 
0221     return true;
0222 }
0223 
0224 void RemoteAstrometryParser::setAstrometryDevice(const QSharedPointer<ISD::GenericDevice> &device)
0225 {
0226     if (device == m_RemoteAstrometry)
0227         return;
0228 
0229     m_RemoteAstrometry = device;
0230 
0231     m_RemoteAstrometry->disconnect(this);
0232 
0233     connect(m_RemoteAstrometry.get(), &ISD::GenericDevice::propertyUpdated, this, &RemoteAstrometryParser::checkStatus);
0234     connect(m_RemoteAstrometry.get(), &ISD::GenericDevice::propertyUpdated, this, &RemoteAstrometryParser::checkResults);
0235 
0236     if (targetCCD.isEmpty() == false)
0237         setCCD(targetCCD);
0238 }
0239 
0240 void RemoteAstrometryParser::checkStatus(INDI::Property prop)
0241 {
0242     if (solverRunning == false || !prop.isNameMatch("ASTROMETRY_SOLVER"))
0243         return;
0244 
0245     if (prop.getState() == IPS_ALERT)
0246     {
0247         stopSolver();
0248         align->appendLogText(i18n("Solver failed. Try again."));
0249         emit solverFailed();
0250         return;
0251     }
0252 }
0253 
0254 void RemoteAstrometryParser::checkResults(INDI::Property prop)
0255 {
0256     if (solverRunning == false || !prop.isNameMatch("ASTROMETRY_RESULTS") || prop.getState() != IPS_OK)
0257         return;
0258 
0259     double pixscale, ra, de, orientation;
0260     pixscale = ra = de = orientation = -1000;
0261 
0262     auto nvp = prop.getNumber();
0263 
0264     for (int i = 0; i < nvp->count(); i++)
0265     {
0266         auto value = nvp->at(i)->getValue();
0267         if (nvp->at(i)->isNameMatch("ASTROMETRY_RESULTS_PIXSCALE"))
0268             pixscale = value;
0269         else if (nvp->at(i)->isNameMatch("ASTROMETRY_RESULTS_ORIENTATION"))
0270             orientation = value;
0271         else if (nvp->at(i)->isNameMatch("ASTROMETRY_RESULTS_RA"))
0272             ra = value;
0273         else if (nvp->at(i)->isNameMatch("ASTROMETRY_RESULTS_DE"))
0274             de = value;
0275         else if (nvp->at(i)->isNameMatch("ASTROMETRY_RESULTS_PARITY"))
0276         {
0277             if (value == 1)
0278                 parity = FITSImage::POSITIVE;
0279             else if (value == -1)
0280                 parity = FITSImage::NEGATIVE;
0281         }
0282     }
0283 
0284     if (pixscale != -1000 && ra != -1000 && de != -1000 && orientation != -1000)
0285     {
0286         stopSolver();
0287         emit solverFinished(orientation, ra, de, pixscale, parity != FITSImage::POSITIVE);
0288     }
0289 }
0290 }