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

0001 /*
0002     SPDX-FileCopyrightText: 2013 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003     SPDX-FileCopyrightText: 2013-2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
0004     SPDX-FileCopyrightText: 2018-2020 Robert Lancaster <rlancaste@gmail.com>
0005     SPDX-FileCopyrightText: 2019-2021 Hy Murveit <hy@murveit.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #pragma once
0011 
0012 #include "ui_align.h"
0013 #include "ekos/ekos.h"
0014 #include "indi/indicamera.h"
0015 #include "indi/indistd.h"
0016 #include "indi/indimount.h"
0017 #include "skypoint.h"
0018 
0019 #include <QTime>
0020 #include <QTimer>
0021 #include <QElapsedTimer>
0022 #include <KConfigDialog>
0023 
0024 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
0025 #include <qtdbusglobal.h>
0026 #else
0027 #include <qdbusmacros.h>
0028 #endif
0029 
0030 #include <stellarsolver.h>
0031 #include <memory>
0032 
0033 class QProgressIndicator;
0034 
0035 class AlignView;
0036 class FOV;
0037 class StarObject;
0038 class ProfileInfo;
0039 class RotatorSettings;
0040 
0041 namespace Ekos
0042 {
0043 class AstrometryParser;
0044 class DarkProcessor;
0045 class FilterManager;
0046 class RemoteAstrometryParser;
0047 class OpsAstrometry;
0048 class OpsAlign;
0049 class StellarSolverProfileEditor;
0050 class OpsPrograms;
0051 class OpsASTAP;
0052 class OpsAstrometryIndexFiles;
0053 class MountModel;
0054 class PolarAlignmentAssistant;
0055 class ManualRotator;
0056 
0057 /**
0058  *@class Align
0059  *@short Align class handles plate-solving and polar alignment measurement and correction using astrometry.net
0060  * The align class employs StellarSolver library for local solvers and supports remote INDI-based solver.
0061  * StellarSolver supports internal and external solvers (Astrometry.net, ASTAP, Online Astrometry).
0062  * If an image is solved successfully, the image central J2000 RA & DE coordinates along with pixel scale, rotation, and partiy are
0063  * reported back.
0064  * Index files management is supported with ability to download astrometry.net files. The user may select and edit different solver
0065  * profiles that provide settings to control both extraction and solving profiles in detail. Manual and automatic field rotation
0066  * is supported in order to align the solved images to a particular orientation in the sky. The manual rotation assistant is an interactive
0067  * tool that helps the user to arrive at the desired framing.
0068  * Align module provide Polar Align Helper tool which enables easy-to-follow polar alignment procedure given wide FOVs (> 1.5 degrees)
0069  * Legacy polar aligment is deprecated.
0070  *@author Jasem Mutlaq
0071  *@version 2.0
0072  */
0073 class Align : public QWidget, public Ui::Align
0074 {
0075         Q_OBJECT
0076         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Align")
0077         Q_PROPERTY(Ekos::AlignState status READ status NOTIFY newStatus)
0078         Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
0079         Q_PROPERTY(QString opticalTrain READ opticalTrain WRITE setOpticalTrain)
0080         Q_PROPERTY(QString camera READ camera)
0081         Q_PROPERTY(QString filterWheel READ filterWheel)
0082         Q_PROPERTY(QString filter READ filter WRITE setFilter)
0083         Q_PROPERTY(double exposure READ exposure WRITE setExposure)
0084         Q_PROPERTY(QList<double> fov READ fov)
0085         Q_PROPERTY(QList<double> cameraInfo READ cameraInfo)
0086         Q_PROPERTY(QList<double> telescopeInfo READ telescopeInfo)
0087         //Q_PROPERTY(QString solverArguments READ solverArguments WRITE setSolverArguments)
0088 
0089     public:
0090         explicit Align(const QSharedPointer<ProfileInfo> &activeProfile);
0091         virtual ~Align() override;
0092 
0093         typedef enum { GOTO_SYNC, GOTO_SLEW, GOTO_NOTHING } GotoMode;
0094         typedef enum { SOLVER_LOCAL, SOLVER_REMOTE } SolverMode;
0095         typedef enum
0096         {
0097             ALIGN_RESULT_SUCCESS,
0098             ALIGN_RESULT_WARNING,
0099             ALIGN_RESULT_FAILED
0100         } AlignResult;
0101 
0102         typedef enum
0103         {
0104             BLIND_IDLE,
0105             BLIND_ENGAGNED,
0106             BLIND_USED
0107         } BlindState;
0108 
0109         /** @defgroup AlignDBusInterface Ekos DBus Interface - Align Module
0110              * Ekos::Align interface provides advanced scripting capabilities to solve images using online or offline astrometry.net
0111             */
0112 
0113         /*@{*/
0114 
0115         /** DBUS interface function.
0116              * Select CCD
0117              * @param device CCD device name
0118              * @return Returns true if device if found and selected, false otherwise.
0119              */
0120         Q_SCRIPTABLE QString camera();
0121 
0122         /** DBUS interface function.
0123              * select the filter device from the available filter drivers. The filter device can be the same as the CCD driver if the filter functionality was embedded within the driver.
0124              * @param device The filter device name
0125              * @return Returns true if filter device is found and set, false otherwise.
0126              */
0127         Q_SCRIPTABLE QString filterWheel();
0128 
0129         /** DBUS interface function.
0130              * select the filter from the available filters.
0131              * @param filter The filter name
0132              * @return Returns true if filter is found and set, false otherwise.
0133              */
0134         Q_SCRIPTABLE bool setFilter(const QString &filter);
0135         Q_SCRIPTABLE QString filter();
0136 
0137         /** DBUS interface function.
0138              * Start the plate-solving process given the passed image file.
0139              * @param filename Name of image file to solve. FITS and JPG/JPG/TIFF formats are accepted.
0140              * @param isGenerated Set to true if filename is generated from a CCD capture operation. If the file is loaded from any storage or network media, pass false.
0141              * @return Returns true if device if found and selected, false otherwise.
0142              */
0143         Q_SCRIPTABLE Q_NOREPLY void startSolving();
0144 
0145         /** DBUS interface function.
0146              * Select Solver Action after successfully solving an image.
0147              * @param mode 0 for Sync, 1 for Slew To Target, 2 for Nothing (just display solution results)
0148              */
0149         Q_SCRIPTABLE Q_NOREPLY void setSolverAction(int mode);
0150 
0151         /** DBUS interface function.
0152              * Returns the solver's solution results
0153              * @return Returns array of doubles. First item is RA in degrees. Second item is DEC in degrees.
0154              */
0155         Q_SCRIPTABLE QList<double> getSolutionResult();
0156 
0157         /** DBUS interface function.
0158              * Returns the solver's current status
0159              * @return Returns solver status (Ekos::AlignState)
0160              */
0161         Q_SCRIPTABLE Ekos::AlignState status()
0162         {
0163             return state;
0164         }
0165 
0166         /** DBUS interface function.
0167              * @return Returns State of load slew procedure. Idle if not started. Busy if in progress. Ok if complete. Alert if procedure failed.
0168              */
0169         Q_SCRIPTABLE int getLoadAndSlewStatus()
0170         {
0171             return m_SolveFromFile;
0172         }
0173 
0174         /** DBUS interface function.
0175              * Sets the exposure of the selected CCD device.
0176              * @param value Exposure value in seconds
0177              */
0178         Q_SCRIPTABLE Q_NOREPLY void setExposure(double value);
0179         Q_SCRIPTABLE double exposure()
0180         {
0181             return alignExposure->value();
0182         }
0183 
0184         /** DBUS interface function.
0185          * Get currently active camera info in this order:
0186          * width, height, pixel_size_x, pixel_size_y
0187          */
0188         Q_SCRIPTABLE QList<double> cameraInfo();
0189 
0190         /** DBUS interface function.
0191          * Get current active telescope info in this order:
0192          * focal length, aperture
0193          */
0194         Q_SCRIPTABLE QList<double> telescopeInfo();
0195 
0196         /** @}*/
0197 
0198         /**
0199              * @brief Add Camera to the list of available Cameras.
0200              * @param device pointer to camera device.
0201              * @return True if added successfully, false if duplicate or failed to add.
0202              */
0203         bool setCamera(ISD::Camera *device);
0204 
0205         /**
0206              * @brief addFilterWheel Add new filter wheel filter device.
0207              * @param device pointer to filter device.
0208              * @return True if added successfully, false if duplicate or failed to add.
0209              */
0210         bool setFilterWheel(ISD::FilterWheel *device);
0211 
0212         /**
0213              * @brief Add new mount
0214              * @param device pointer to mount device.
0215              * @return True if added successfully, false if duplicate or failed to add.
0216              */
0217         bool setMount(ISD::Mount *device);
0218 
0219         /**
0220              * @brief Add new Dome
0221              * @param device pointer to dome device.
0222              * @return True if added successfully, false if duplicate or failed to add.
0223              */
0224         bool setDome(ISD::Dome *device);
0225 
0226         /**
0227              * @brief Add new Rotator
0228              * @param device pointer to rotator device.
0229              */
0230         void setRotator(ISD::Rotator *device);
0231 
0232         void removeDevice(const QSharedPointer<ISD::GenericDevice> &device);
0233 
0234         /**
0235              * @brief setAstrometryDevice
0236              * @param newAstrometry
0237              */
0238         void setAstrometryDevice(const QSharedPointer<ISD::GenericDevice> &device);
0239 
0240         /**
0241              * @brief CCD information is updated, sync them.
0242              */
0243         void syncCameraInfo();
0244 
0245         /**
0246          * @brief syncCCDControls Update camera controls like gain, offset, ISO..etc.
0247          */
0248         void syncCameraControls();
0249 
0250         /**
0251              * @brief Generate arguments we pass to the remote solver.
0252              */
0253         QStringList generateRemoteArgs(const QSharedPointer<FITSData> &imageData);
0254 
0255         /**
0256              * @brief Does our parser exist in the system?
0257              */
0258         bool isParserOK();
0259 
0260         // Log
0261         QStringList logText()
0262         {
0263             return m_LogText;
0264         }
0265         QString getLogText()
0266         {
0267             return m_LogText.join("\n");
0268         }
0269         void clearLog();
0270 
0271         /**
0272              * @brief getFOVScale Returns calculated FOV values
0273              * @param fov_w FOV width in arcmins
0274              * @param fov_h FOV height in arcmins
0275              * @param fov_scale FOV scale in arcsec per pixel
0276              */
0277         void getFOVScale(double &fov_w, double &fov_h, double &fov_scale);
0278         QList<double> fov();
0279 
0280         /**
0281          * @brief getCalculatedFOVScale Get calculated FOV scales from the current CCD+Telescope combination.
0282          * @param fov_w return calculated fov width in arcminutes
0283          * @param fov_h return calculated fov height in arcminutes
0284          * @param fov_scale return calculated fov pixcale in arcsecs per pixel.
0285          * @note This is NOT the same as effective FOV which is the measured FOV from astrometry. It is the
0286          * theoretical FOV from calculated values.
0287          */
0288         void getCalculatedFOVScale(double &fov_w, double &fov_h, double &fov_scale);
0289 
0290         void setupFilterManager();
0291         void setupPlot();
0292         void setupSolutionTable();
0293         void setupOptions();
0294 
0295         /**
0296              * @brief Sync the telescope to the solved alignment coordinate.
0297              */
0298         void Sync();
0299 
0300         /**
0301              * @brief Slew the telescope to the solved alignment coordinate.
0302              */
0303         void Slew();
0304 
0305         /**
0306              * @brief Sync the telescope to the solved alignment coordinate, and then slew to the target coordinate.
0307              */
0308         void SlewToTarget();
0309 
0310         /**
0311          * @brief getStellarSolverProfiles
0312          * @return list of StellarSolver profile names
0313          */
0314         QStringList getStellarSolverProfiles();
0315 
0316         GotoMode currentGOTOMode() const
0317         {
0318             return m_CurrentGotoMode;
0319         }
0320 
0321         /**
0322              * @brief generateOptions Generate astrometry.net option given the supplied map
0323              * @param optionsMap List of key=value pairs for all astrometry.net options
0324              * @return String List of valid astrometry.net options
0325              */
0326         static QStringList generateRemoteOptions(const QVariantMap &optionsMap);
0327         static void generateFOVBounds(double fov_h, QString &fov_low, QString &fov_high, double tolerance = 0.05);
0328 
0329         // access to the mount model UI, required for testing
0330         MountModel * mountModel() const
0331         {
0332             return m_MountModel;
0333         }
0334 
0335         PolarAlignmentAssistant *polarAlignmentAssistant() const
0336         {
0337             return m_PolarAlignmentAssistant;
0338         }
0339 
0340         bool wcsSynced() const
0341         {
0342             return m_wcsSynced;
0343         }
0344 
0345         /**
0346              * @brief Process updated device properties
0347              * @param prop INDI Property
0348              */
0349         void updateProperty(INDI::Property prop);
0350 
0351 
0352         /**
0353              * @brief Check CCD and make sure information is updated and FOV is re-calculated.
0354              * @param CCDNum By default, we check the already selected CCD in the dropdown menu. If CCDNum is specified, the check is made against this specific CCD in the dropdown menu. CCDNum is the index of the CCD in the dropdown menu.
0355              */
0356         void checkCamera();
0357 
0358         /**
0359              * @brief Check Filter and make sure information is updated accordingly.
0360              * @param filterNum By default, we check the already selected filter in the dropdown menu. If filterNum is specified, the check is made against this specific filter in the dropdown menu.
0361              *  filterNum is the index of the filter in the dropdown menu.
0362              */
0363         void checkFilter();
0364 
0365         /**
0366              * @brief checkCameraExposureProgress Track the progress of CCD exposure
0367              * @param targetChip Target chip under exposure
0368              * @param remaining how many seconds remaining
0369              * @param state status of exposure
0370              */
0371         void checkCameraExposureProgress(ISD::CameraChip *targetChip, double remaining, IPState state);
0372         /**
0373              * @brief Process new FITS received from CCD.
0374              * @param bp pointer to blob property
0375              */
0376         void processData(const QSharedPointer<FITSData> &data);
0377 
0378         /** DBUS interface function.
0379              * Loads an image (FITS, RAW, or JPG/PNG) and solve its coordinates, then it slews to the solved coordinates and an image is captured and solved to ensure
0380              * the telescope is pointing to the same coordinates of the image.
0381              * @param image buffer to image data.
0382              * @param extension image extension (e.g. cr2, jpg, fits,..etc).
0383              */
0384         bool loadAndSlew(const QByteArray &image, const QString &extension);
0385 
0386         /** \addtogroup AlignDBusInterface
0387              *  @{
0388              */
0389 
0390         /**
0391          * @brief Stop aligning
0392          * @param mode stop mode (abort or suspend)
0393          */
0394         void stop(Ekos::AlignState mode);
0395 
0396         /** DBUS interface function.
0397              * Aborts the solving operation, handle outside of the align module.
0398              */
0399         Q_SCRIPTABLE Q_NOREPLY void abort()
0400         {
0401             stop(ALIGN_ABORTED);
0402         }
0403 
0404         /**
0405          * @brief Suspend aligning, recovery handled by the align module itself.
0406          */
0407         void suspend()
0408         {
0409             stop(ALIGN_SUSPENDED);
0410         }
0411 
0412         /** DBUS interface function.
0413              * Select the solver mode
0414              * @param type Set solver type. 0 LOCAL, 1 REMOTE (requires remote astrometry driver to be activated)
0415              */
0416         Q_SCRIPTABLE Q_NOREPLY void setSolverMode(int mode);
0417 
0418         /** DBUS interface function.
0419              * Capture and solve an image using the astrometry.net engine
0420              * @return Returns true if the procedure started successful, false otherwise (return true, not false, when retrying!)
0421              */
0422         Q_SCRIPTABLE bool captureAndSolve();
0423 
0424         /** DBUS interface function.
0425              * Loads an image (FITS, RAW, or JPG/PNG) and solve its coordinates, then it slews to the solved coordinates and an image is captured and solved to ensure
0426              * the telescope is pointing to the same coordinates of the image.
0427              * @param fileURL URL to the image to solve
0428              */
0429         Q_SCRIPTABLE bool loadAndSlew(QString fileURL = QString());
0430 
0431         /** DBUS interface function.
0432              * Sets the target coordinates that the solver compares the solution coordinates to.
0433              * By default, the target coordinates are those of the current mount when the capture and
0434              * solve operation is started. In case of SYNC, only the error between the solution and target
0435              * coordinates is calculated. When Slew to Target is selected, the mount would be slewed afterwards to
0436              * this target coordinate.
0437              * @param ra0 J2000 Right Ascension in hours.
0438              * @param de0 J2000 Declination in degrees.
0439              */
0440         Q_SCRIPTABLE Q_NOREPLY void setTargetCoords(double ra0, double de0);
0441 
0442         /**
0443          * @brief getTargetCoords QList of target coordinates.
0444          * @return First value is J2000 RA in hours. Second value is J2000 DE in degrees.
0445          */
0446         Q_SCRIPTABLE QList<double> getTargetCoords();
0447 
0448 
0449         /**
0450           * @brief Set the alignment target where the mount is expected to point at.
0451           * @param targetCoord exact coordinates of the target position.
0452           */
0453         void setTarget(const SkyPoint &targetCoord);
0454 
0455         /**
0456          * @brief Set the coordinates that the mount reports as its position
0457          * @param position current mount position
0458          */
0459         void setTelescopeCoordinates(const SkyPoint &position)
0460         {
0461             m_TelescopeCoord = position;
0462         }
0463 
0464         Q_SCRIPTABLE Q_NOREPLY void setTargetPositionAngle(double value);
0465 
0466         /** DBUS interface function.
0467              * Sets the binning of the selected CCD device.
0468              * @param binIndex Index of binning value. Default values range from 0 (binning 1x1) to 3 (binning 4x4)
0469              */
0470         Q_SCRIPTABLE Q_NOREPLY void setBinningIndex(int binIndex);
0471 
0472         /** @}*/
0473 
0474         /**
0475              * @brief Solver finished successfully, process the data and execute the required actions depending on the mode.
0476              * @param orientation Orientation of image in degrees (East of North)
0477              * @param ra Center RA in solved image, degrees.
0478              * @param dec Center DEC in solved image, degrees.
0479              * @param pixscale Image scale is arcsec/pixel
0480              * @param eastToTheRight When the image is rotated, so that North is up, East would be to the right.
0481              */
0482         void solverFinished(double orientation, double ra, double dec, double pixscale, bool eastToTheRight);
0483 
0484         void solverComplete();
0485 
0486         /**
0487              * @brief Process solver failure.
0488              */
0489         void solverFailed();
0490 
0491         /**
0492              * @brief We received new telescope info, process them and update FOV.
0493              */
0494         bool syncTelescopeInfo();
0495 
0496         void setFocusStatus(Ekos::FocusState state);
0497 
0498         // Log
0499         void appendLogText(const QString &);
0500 
0501         // Capture
0502         void setCaptureComplete();
0503 
0504         // Update Capture Module status
0505         void setCaptureStatus(Ekos::CaptureState newState);
0506         // Update Mount module status
0507         void setMountStatus(ISD::Mount::Status newState);
0508 
0509         void zoomAlignView();
0510         void setAlignZoom(double scale);
0511 
0512         // Manual Rotator Dialog
0513         void toggleManualRotator(bool toggled);
0514 
0515         /**
0516          * @brief checkIfRotationRequired Check whether we need to perform an ALIGN_ROTATING action, whether manual or automatic.
0517          * @return True if rotation is required as per the settings, false is not required.
0518          */
0519         bool checkIfRotationRequired();
0520 
0521         // Settings
0522         QVariantMap getAllSettings() const;
0523         void setAllSettings(const QVariantMap &settings);
0524 
0525         // Trains
0526         QString opticalTrain() const
0527         {
0528             return opticalTrainCombo->currentText();
0529         }
0530         void setOpticalTrain(const QString &value)
0531         {
0532             opticalTrainCombo->setCurrentText(value);
0533         }
0534 
0535         Ekos::OpsAlign *getAlignOptionsModule()
0536         {
0537             return opsAlign;
0538         }
0539 
0540     private slots:
0541         // Solver timeout
0542         void checkAlignmentTimeout();
0543         void setAlignTableResult(AlignResult result);
0544 
0545         // External View
0546         void showFITSViewer();
0547         void toggleAlignWidgetFullScreen();
0548 
0549         /**
0550          * @brief prepareCapture Set common settings for capture for align module
0551          * @param targetChip target Chip
0552          */
0553         void prepareCapture(ISD::CameraChip *targetChip);
0554 
0555         //Solutions Display slots
0556         void buildTarget();
0557         void handlePointTooltip(QMouseEvent *event);
0558         void handleVerticalPlotSizeChange();
0559         void handleHorizontalPlotSizeChange();
0560         void selectSolutionTableRow(int row, int column);
0561         void slotClearAllSolutionPoints();
0562         void slotRemoveSolutionPoint();
0563         void slotAutoScaleGraph();
0564 
0565         // Model
0566         void slotMountModel();
0567 
0568         // Capture Timeout
0569         void processCaptureTimeout();
0570 
0571     protected slots:
0572         /**
0573              * @brief After a solver process is completed successfully, sync, slew to target, or do nothing as set by the user.
0574              */
0575         void executeGOTO();
0576 
0577         /**
0578          * @brief refreshAlignOptions is called when settings are updated in OpsAlign.
0579          */
0580         void refreshAlignOptions();
0581 
0582         void processPAHStage(int stage);
0583 
0584     signals:
0585         void newLog(const QString &text);
0586         void newStatus(Ekos::AlignState state);
0587         void newPAAStage(int stage);
0588         void newSolution(const QVariantMap &solution);
0589 
0590         // This is sent when we load an image in the view
0591         void newImage(const QSharedPointer<FITSView> &view);
0592         // This is sent when the pixmap is updated within the view
0593         void newFrame(const QSharedPointer<FITSView> &view);
0594         // Send new solver results
0595         void newSolverResults(double orientation, double ra, double dec, double pixscale);
0596 
0597         // Train changed
0598         void trainChanged();
0599 
0600         // Settings
0601         void settingsUpdated(const QVariantMap &settings);
0602 
0603         // Manual Rotator
0604         void manualRotatorChanged(double currentPA, double targetPA, double pixscale);
0605 
0606     private:
0607 
0608         void setupOpticalTrainManager();
0609         void refreshOpticalTrain();
0610 
0611         ////////////////////////////////////////////////////////////////////
0612         /// Settings
0613         ////////////////////////////////////////////////////////////////////
0614 
0615         /**
0616          * @brief Connect GUI elements to sync settings once updated.
0617          */
0618         void connectSettings();
0619         /**
0620          * @brief Stop updating settings when GUI elements are updated.
0621          */
0622         void disconnectSettings();
0623         /**
0624          * @brief loadSettings Load setting from Options and set them accordingly.
0625          */
0626         void loadGlobalSettings();
0627 
0628         /**
0629          * @brief syncSettings When checkboxes, comboboxes, or spin boxes are updated, save their values in the
0630          * global and per-train settings.
0631          */
0632         void syncSettings();
0633 
0634         /**
0635          * @brief syncControl Sync setting to widget. The value depends on the widget type.
0636          * @param settings Map of all settings
0637          * @param key name of widget to sync
0638          * @param widget pointer of widget to set
0639          * @return True if sync successful, false otherwise
0640          */
0641         bool syncControl(const QVariantMap &settings, const QString &key, QWidget * widget);
0642 
0643         void setState (AlignState value);
0644         /**
0645          * @brief Retrieve the align status indicator
0646          */
0647         QProgressIndicator *getProgressStatus();
0648 
0649         /**
0650          * @brief Stop the progress animation in the solution table
0651          */
0652         void stopProgressAnimation();
0653 
0654         void exportSolutionPoints();
0655 
0656         /**
0657             * @brief Calculate Field of View of CCD+Telescope combination that we need to pass to astrometry.net solver.
0658             */
0659         void calculateFOV();
0660 
0661         /**
0662          * @brief calculateEffectiveFocalLength Calculate Focal Length purely form astrometric data.
0663          */
0664         void calculateEffectiveFocalLength(double newFOVW);
0665 
0666         /**
0667          * @brief calculateAlignTargetDiff Find the difference between aligned vs. target coordinates and update
0668          * the GUI accordingly.
0669          */
0670         void calculateAlignTargetDiff();
0671 
0672         /**
0673              * @brief Get formatted RA & DEC coordinates compatible with astrometry.net format.
0674              * @param ra Right ascension
0675              * @param dec Declination
0676              * @param ra_str will contain the formatted RA string
0677              * @param dec_str will contain the formatted DEC string
0678              */
0679         void getFormattedCoords(double ra, double dec, QString &ra_str, QString &dec_str);
0680 
0681         uint8_t getSolverDownsample(uint16_t binnedW);
0682 
0683         /**
0684              * @brief setWCSEnabled enables/disables World Coordinate System settings in the CCD driver.
0685              * @param enable true to enable WCS, false to disable.
0686              */
0687         void setWCSEnabled(bool enable);
0688 
0689         void resizeEvent(QResizeEvent *event) override;
0690 
0691         KPageWidgetItem *m_IndexFilesPage;
0692         QString savedOptionsProfiles;
0693 
0694         /**
0695          * @brief React when a mount motion has been detected
0696          */
0697         void handleMountMotion();
0698 
0699         /**
0700          * @brief Continue aligning according to the current mount status
0701          */
0702         void handleMountStatus();
0703 
0704         /**
0705          * @brief initPolarAlignmentAssistant Initialize Polar Alignment Asssistant Tool
0706          */
0707         void setupPolarAlignmentAssistant();
0708 
0709         void setupRotatorControl();
0710 
0711         /**
0712          * @brief initManualRotator Initialize Manual Rotator Tool
0713          */
0714         void setupManualRotator();
0715 
0716         /**
0717          * @brief initDarkProcessor Initialize Dark Processor
0718          */
0719         void setuptDarkProcessor();
0720 
0721         bool matchPAHStage(uint32_t stage);
0722 
0723 
0724         // Effective FOV
0725 
0726         /**
0727          * @brief getEffectiveFOV Search database for effective FOV that matches the current profile and settings
0728          * @return Variant Map containing effect FOV data or empty variant map if none found
0729          */
0730         QVariantMap getEffectiveFOV();
0731         void saveNewEffectiveFOV(double newFOVW, double newFOVH);
0732         QList<QVariantMap> effectiveFOVs;
0733         void syncFOV();
0734 
0735         // We are using calculated FOV now until a more accurate effective FOV is found.
0736         bool m_EffectiveFOVPending { false };
0737         /// Which chip should we invoke in the current CCD?
0738         bool useGuideHead { false };
0739         /// Can the mount sync its coordinates to those set by Ekos?
0740         bool canSync { false };
0741         // m_SolveFromFile is true we load an image and solve it, no capture is done.
0742         bool m_SolveFromFile { false };
0743         // Target Position Angle of solver Load&Slew image to be used for rotator if necessary
0744         double m_TargetPositionAngle { std::numeric_limits<double>::quiet_NaN() };
0745         // Target Pierside of solver Load&Slew image to be used
0746         ISD::Mount::PierSide m_TargetPierside = ISD::Mount::PIER_UNKNOWN;
0747         double currentRotatorPA { -1 };
0748         /// Solver iterations count
0749         uint8_t solverIterations { 0 };
0750         /// Was solving with scale off used?
0751         BlindState useBlindScale {BLIND_IDLE};
0752         /// Was solving with position off used?
0753         BlindState useBlindPosition {BLIND_IDLE};
0754 
0755         // FOV
0756         double m_CameraPixelWidth { -1 };
0757         double m_CameraPixelHeight { -1 };
0758         uint16_t m_CameraWidth { 0 };
0759         uint16_t m_CameraHeight { 0 };
0760 
0761         double m_FocalLength {-1};
0762         double m_Aperture {-1};
0763         double m_FocalRatio {-1};
0764         double m_Reducer = {-1};
0765 
0766         double m_FOVWidth { 0 };
0767         double m_FOVHeight { 0 };
0768         double m_FOVPixelScale { 0 };
0769 
0770         // Keep raw rotator angle
0771         double sRawAngle { INVALID_VALUE };
0772         // Keep track of solver results
0773         double sOrientation { INVALID_VALUE };
0774         double sRA { INVALID_VALUE };
0775         double sDEC { INVALID_VALUE };
0776 
0777         /// Solver alignment coordinates
0778         SkyPoint m_AlignCoord;
0779         /// Target coordinates we need to slew to
0780         SkyPoint m_TargetCoord;
0781         /// Current telescope coordinates
0782         SkyPoint m_TelescopeCoord;
0783         /// Difference between solution and target coordinate
0784         double m_TargetDiffTotal { 1e6 };
0785         double m_TargetDiffRA { 1e6 };
0786         double m_TargetDiffDE { 1e6 };
0787 
0788         /// Progress icon if the solver is running
0789         std::unique_ptr<QProgressIndicator> pi;
0790 
0791         /// Keep track of how long the solver is running
0792         QElapsedTimer solverTimer;
0793 
0794         // The StellarSolver
0795         std::unique_ptr<StellarSolver> m_StellarSolver;
0796         // StellarSolver Profiles
0797         QList<SSolver::Parameters> m_StellarSolverProfiles;
0798 
0799         /// Have we slewed?
0800         bool m_wasSlewStarted { false };
0801         // Above flag only stays false for 10s after slew start.
0802         QElapsedTimer slewStartTimer;
0803         bool didSlewStart();
0804         // Only wait this many milliseconds for slew to start.
0805         // Otherwise assume it has begun.
0806         static constexpr int MAX_WAIT_FOR_SLEW_START_MSEC = 10000;
0807 
0808         // Online and Offline parsers
0809         AstrometryParser* parser { nullptr };
0810         std::unique_ptr<RemoteAstrometryParser> remoteParser;
0811         QSharedPointer<ISD::GenericDevice> m_RemoteParserDevice;
0812 
0813         // Pointers to our devices
0814         ISD::Mount *m_Mount { nullptr };
0815         ISD::Dome *m_Dome { nullptr };
0816         ISD::Camera *m_Camera { nullptr };
0817         ISD::Rotator *m_Rotator { nullptr };
0818         ISD::FilterWheel *m_FilterWheel { nullptr };
0819 
0820         int currentFilterPosition { -1 };
0821         /// True if we need to change filter position and wait for result before continuing capture
0822         bool filterPositionPending { false };
0823 
0824         /// Keep track of solver FOV to be plotted in the skymap after each successful solve operation
0825         std::shared_ptr<FOV> solverFOV;
0826         std::shared_ptr<FOV> sensorFOV;
0827 
0828         /// WCS
0829         bool m_wcsSynced { false };
0830 
0831         /// Log
0832         QStringList m_LogText;
0833 
0834         /// Issue counters
0835         uint8_t m_CaptureTimeoutCounter { 0 };
0836         uint8_t m_CaptureErrorCounter { 0 };
0837         uint8_t m_SlewErrorCounter { 0 };
0838         bool m_resetCaptureTimeoutCounter = false;
0839 
0840         QTimer m_CaptureTimer;
0841 
0842         // State
0843         AlignState state { ALIGN_IDLE };
0844         FocusState m_FocusState { FOCUS_IDLE };
0845         CaptureState m_CaptureState { CAPTURE_IDLE };
0846 
0847         // Track which upload mode the CCD is set to. If set to UPLOAD_LOCAL, then we need to switch it to UPLOAD_CLIENT in order to do focusing, and then switch it back to UPLOAD_LOCAL
0848         ISD::Camera::UploadMode rememberUploadMode { ISD::Camera::UPLOAD_CLIENT };
0849 
0850         GotoMode m_CurrentGotoMode;
0851 
0852         QString dirPath;
0853 
0854         // Timer
0855         QTimer m_AlignTimer;
0856 
0857         // Align Frame
0858         QSharedPointer<AlignView> m_AlignView;
0859 
0860         // FITS Viewer in case user want to display in it instead of internal view
0861         QSharedPointer<FITSViewer> fv;
0862 
0863         QUrl alignURL;
0864         QUrl alignURLPath;
0865 
0866         // keep track of autoWSC
0867         bool rememberAutoWCS { false };
0868         bool rememberSolverWCS { false };
0869 
0870         // Differential Slewing
0871         bool differentialSlewingActivated { false };
0872         bool targetAccuracyNotMet { false };
0873 
0874         // Astrometry Options
0875         OpsAstrometry *opsAstrometry { nullptr };
0876         OpsAlign *opsAlign { nullptr };
0877         OpsPrograms *opsPrograms { nullptr };
0878         OpsAstrometryIndexFiles *opsAstrometryIndexFiles { nullptr };
0879         OpsASTAP *opsASTAP { nullptr };
0880         StellarSolverProfileEditor *optionsProfileEditor { nullptr };
0881 
0882         // Drawing
0883         QCPCurve *centralTarget { nullptr };
0884         QCPCurve *yellowTarget { nullptr };
0885         QCPCurve *redTarget { nullptr };
0886         QCPCurve *concentricRings { nullptr };
0887 
0888         // Telescope Settings
0889         double m_EffectiveFocalLength = -1;
0890         bool m_isRateSynced = false;
0891         bool domeReady = true;
0892 
0893         // CCD Exposure Looping
0894         bool m_RememberCameraFastExposure = { false };
0895 
0896         // Controls
0897         double alignGainSpecialValue {INVALID_VALUE};
0898         double TargetCustomGainValue {-1};
0899 
0900         // Data
0901         QSharedPointer<FITSData> m_ImageData;
0902 
0903         // Active Profile
0904         QSharedPointer<ProfileInfo> m_ActiveProfile;
0905 
0906         // Threshold to notify settle time is 3 seconds
0907         static constexpr uint16_t DELAY_THRESHOLD_NOTIFY { 3000 };
0908 
0909         // Mount Model
0910         // N.B. We do not need to use "smart pointer" here as the object memroy
0911         // is taken care of by the Qt framework.
0912         MountModel *m_MountModel {nullptr};
0913         PolarAlignmentAssistant *m_PolarAlignmentAssistant {nullptr};
0914         ManualRotator *m_ManualRotator {nullptr};
0915 
0916         // Dark Processor
0917         QPointer<DarkProcessor> m_DarkProcessor;
0918 
0919         // Filter Manager
0920         QSharedPointer<FilterManager> m_FilterManager;
0921 
0922         // Rotator Control
0923         QSharedPointer<RotatorSettings> m_RotatorControlPanel;
0924         int m_RotatorTimeFrame = 0;
0925         bool m_estimateRotatorTimeFrame = false;
0926 
0927         // Settings
0928         QVariantMap m_Settings;
0929         QVariantMap m_GlobalSettings;
0930 
0931         bool m_UsedScale = false;
0932         bool m_UsedPosition = false;
0933         double m_ScaleUsed = 0;
0934         double m_RAUsed = 0;
0935         double m_DECUsed = 0;
0936 };
0937 }