1 /*
2     SPDX-FileCopyrightText: 2013 Jasem Mutlaq <mutlaqja@ikarustech.com>
3     SPDX-FileCopyrightText: 2013-2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
4     SPDX-FileCopyrightText: 2018-2020 Robert Lancaster <rlancaste@gmail.com>
5     SPDX-FileCopyrightText: 2019-2021 Hy Murveit <hy@murveit.com>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #pragma once
11 
12 #include "ui_align.h"
13 #include "ekos/ekos.h"
14 #include "indi/indiccd.h"
15 #include "indi/indistd.h"
16 #include "indi/inditelescope.h"
17 #include "indi/indidome.h"
18 #include "ksuserdb.h"
19 #include "ekos/auxiliary/filtermanager.h"
20 #include "ekos/auxiliary/darkprocessor.h"
21 
22 #include <QTime>
23 #include <QTimer>
24 #include <QElapsedTimer>
25 #include <KConfigDialog>
26 
27 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
28 #include <QtDBus/qtdbusglobal.h>
29 #else
30 #include <QtDBus/qdbusmacros.h>
31 #endif
32 
33 #include <stellarsolver.h>
34 #include <memory>
35 
36 class QProgressIndicator;
37 
38 class AlignView;
39 class FOV;
40 class StarObject;
41 class ProfileInfo;
42 
43 namespace Ekos
44 {
45 class AstrometryParser;
46 class RemoteAstrometryParser;
47 class OpsAstrometry;
48 class OpsAlign;
49 class StellarSolverProfileEditor;
50 class OpsPrograms;
51 class OpsASTAP;
52 class OpsAstrometryIndexFiles;
53 class MountModel;
54 class PolarAlignmentAssistant;
55 class ManualRotator;
56 
57 /**
58  *@class Align
59  *@short Align class handles plate-solving and polar alignment measurement and correction using astrometry.net
60  * The align class employs StellarSolver library for local solvers and supports remote INDI-based solver.
61  * StellarSolver supports internal and external solvers (Astrometry.net, ASTAP, Online Astrometry).
62  * If an image is solved successfully, the image central J2000 RA & DE coordinates along with pixel scale, rotation, and partiy are
63  * reported back.
64  * Index files management is supported with ability to download astrometry.net files. The user may select and edit different solver
65  * profiles that provide settings to control both extraction and solving profiles in detail. Manual and automatic field rotation
66  * is supported in order to align the solved images to a particular orientation in the sky. The manual rotation assistant is an interactive
67  * tool that helps the user to arrive at the desired framing.
68  * Align module provide Polar Align Helper tool which enables easy-to-follow polar alignment procedure given wide FOVs (> 1.5 degrees)
69  * Legacy polar aligment is deprecated.
70  *@author Jasem Mutlaq
71  *@version 1.5
72  */
73 class Align : public QWidget, public Ui::Align
74 {
75         Q_OBJECT
76         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Align")
77         Q_PROPERTY(Ekos::AlignState status READ status NOTIFY newStatus)
78         Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
79         Q_PROPERTY(QString camera READ camera WRITE setCamera)
80         Q_PROPERTY(QString filterWheel READ filterWheel WRITE setFilterWheel)
81         Q_PROPERTY(QString filter READ filter WRITE setFilter)
82         Q_PROPERTY(double exposure READ exposure WRITE setExposure)
83         Q_PROPERTY(QList<double> fov READ fov)
84         Q_PROPERTY(QList<double> cameraInfo READ cameraInfo)
85         Q_PROPERTY(QList<double> telescopeInfo READ telescopeInfo)
86         //Q_PROPERTY(QString solverArguments READ solverArguments WRITE setSolverArguments)
87 
88     public:
89         explicit Align(ProfileInfo *activeProfile);
90         virtual ~Align() override;
91 
92         typedef enum { GOTO_SYNC, GOTO_SLEW, GOTO_NOTHING } GotoMode;
93         typedef enum { SOLVER_LOCAL, SOLVER_REMOTE } SolverMode;
94         typedef enum
95         {
96             ALIGN_RESULT_SUCCESS,
97             ALIGN_RESULT_WARNING,
98             ALIGN_RESULT_FAILED
99         } AlignResult;
100 
101         typedef enum
102         {
103             BLIND_IDLE,
104             BLIND_ENGAGNED,
105             BLIND_USED
106         } BlindState;
107 
108         /** @defgroup AlignDBusInterface Ekos DBus Interface - Align Module
109              * Ekos::Align interface provides advanced scripting capabilities to solve images using online or offline astrometry.net
110             */
111 
112         /*@{*/
113 
114         /** DBUS interface function.
115              * Select CCD
116              * @param device CCD device name
117              * @return Returns true if device if found and selected, false otherwise.
118              */
119         Q_SCRIPTABLE bool setCamera(const QString &device);
120         Q_SCRIPTABLE QString camera();
121 
122         /** DBUS interface function.
123              * 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.
124              * @param device The filter device name
125              * @return Returns true if filter device is found and set, false otherwise.
126              */
127         Q_SCRIPTABLE bool setFilterWheel(const QString &device);
128         Q_SCRIPTABLE QString filterWheel();
129 
130         /** DBUS interface function.
131              * select the filter from the available filters.
132              * @param filter The filter name
133              * @return Returns true if filter is found and set, false otherwise.
134              */
135         Q_SCRIPTABLE bool setFilter(const QString &filter);
136         Q_SCRIPTABLE QString filter();
137 
138         /** DBUS interface function.
139              * Start the plate-solving process given the passed image file.
140              * @param filename Name of image file to solve. FITS and JPG/JPG/TIFF formats are accepted.
141              * @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.
142              * @return Returns true if device if found and selected, false otherwise.
143              */
144         Q_SCRIPTABLE Q_NOREPLY void startSolving();
145 
146         /** DBUS interface function.
147              * Select Solver Action after successfully solving an image.
148              * @param mode 0 for Sync, 1 for Slew To Target, 2 for Nothing (just display solution results)
149              */
150         Q_SCRIPTABLE Q_NOREPLY void setSolverAction(int mode);
151 
152         /** DBUS interface function.
153              * Returns the solver's solution results
154              * @return Returns array of doubles. First item is RA in degrees. Second item is DEC in degrees.
155              */
156         Q_SCRIPTABLE QList<double> getSolutionResult();
157 
158         /** DBUS interface function.
159              * Returns the solver's current status
160              * @return Returns solver status (Ekos::AlignState)
161              */
status()162         Q_SCRIPTABLE Ekos::AlignState status()
163         {
164             return state;
165         }
166 
167         /** DBUS interface function.
168              * @return Returns State of load slew procedure. Idle if not started. Busy if in progress. Ok if complete. Alert if procedure failed.
169              */
getLoadAndSlewStatus()170         Q_SCRIPTABLE int getLoadAndSlewStatus()
171         {
172             return m_SolveFromFile;
173         }
174 
175         /** DBUS interface function.
176              * Sets the exposure of the selected CCD device.
177              * @param value Exposure value in seconds
178              */
179         Q_SCRIPTABLE Q_NOREPLY void setExposure(double value);
exposure()180         Q_SCRIPTABLE double exposure()
181         {
182             return exposureIN->value();
183         }
184 
185         /** DBUS interface function.
186              * Sets the telescope type (PRIMARY or GUIDE) that should be used for FOV calculations. This value is loaded form driver settings by default.
187              * @param index 0 for PRIMARY telescope, 1 for GUIDE telescope
188              */
189         Q_SCRIPTABLE Q_NOREPLY void setFOVTelescopeType(int index);
FOVTelescopeType()190         int FOVTelescopeType()
191         {
192             return FOVScopeCombo->currentIndex();
193         }
194 
195         /** DBUS interface function.
196          * Get currently active camera info in this order:
197          * width, height, pixel_size_x, pixel_size_y
198          */
199         Q_SCRIPTABLE QList<double> cameraInfo();
200 
201         /** DBUS interface function.
202          * Get current active telescope info in this order:
203          * focal length, aperture
204          */
205         Q_SCRIPTABLE QList<double> telescopeInfo();
206 
207         /** @}*/
208 
209         /**
210              * @brief Add CCD to the list of available CCD.
211              * @param newCCD pointer to CCD device.
212              */
213         void addCCD(ISD::GDInterface *newCCD);
214 
215         /**
216              * @brief addFilter Add filter to the list of available filters.
217              * @param newFilter pointer to filter device.
218              */
219         void addFilter(ISD::GDInterface *newFilter);
220 
221         /**
222              * @brief Set the current telescope
223              * @param newTelescope pointer to telescope device.
224              */
225         void setTelescope(ISD::GDInterface *newTelescope);
226 
227         /**
228              * @brief Set the current dome
229              * @param newDome pointer to telescope device.
230              */
231         void setDome(ISD::GDInterface *newDome);
232 
233         void setRotator(ISD::GDInterface *newRotator);
234 
235         void removeDevice(ISD::GDInterface *device);
236 
237         /**
238              * @brief Set telescope and guide scope info. All measurements is in millimeters.
239              * @param primaryFocalLength Primary Telescope Focal Length. Set to 0 to skip setting this value.
240              * @param primaryAperture Primary Telescope Aperture. Set to 0 to skip setting this value.
241              * @param guideFocalLength Guide Telescope Focal Length. Set to 0 to skip setting this value.
242              * @param guideAperture Guide Telescope Aperture. Set to 0 to skip setting this value.
243         */
244         void setTelescopeInfo(double primaryFocalLength, double primaryAperture, double guideFocalLength, double guideAperture);
245 
246         /**
247              * @brief setAstrometryDevice
248              * @param newAstrometry
249              */
250         void setAstrometryDevice(ISD::GDInterface *newAstrometry);
251 
252         /**
253              * @brief CCD information is updated, sync them.
254              */
255         void syncCCDInfo();
256 
257         /**
258              * @brief Generate arguments we pass to the remote solver.
259              */
260         QStringList generateRemoteArgs(const QSharedPointer<FITSData> &imageData);
261 
262         /**
263              * @brief Does our parser exist in the system?
264              */
265         bool isParserOK();
266 
267         // Log
logText()268         QStringList logText()
269         {
270             return m_LogText;
271         }
getLogText()272         QString getLogText()
273         {
274             return m_LogText.join("\n");
275         }
276         void clearLog();
277 
278         /**
279              * @brief getFOVScale Returns calculated FOV values
280              * @param fov_w FOV width in arcmins
281              * @param fov_h FOV height in arcmins
282              * @param fov_scale FOV scale in arcsec per pixel
283              */
284         void getFOVScale(double &fov_w, double &fov_h, double &fov_scale);
285         QList<double> fov();
286 
287         /**
288          * @brief getCalculatedFOVScale Get calculated FOV scales from the current CCD+Telescope combination.
289          * @param fov_w return calculated fov width in arcminutes
290          * @param fov_h return calculated fov height in arcminutes
291          * @param fov_scale return calculated fov pixcale in arcsecs per pixel.
292          * @note This is NOT the same as effective FOV which is the measured FOV from astrometry. It is the
293          * theoretical FOV from calculated values.
294          */
295         void getCalculatedFOVScale(double &fov_w, double &fov_h, double &fov_scale);
296 
297         void setFilterManager(const QSharedPointer<FilterManager> &manager);
298 
299         /**
300              * @brief Sync the telescope to the solved alignment coordinate.
301              */
302         void Sync();
303 
304         /**
305              * @brief Slew the telescope to the solved alignment coordinate.
306              */
307         void Slew();
308 
309         /**
310              * @brief Sync the telescope to the solved alignment coordinate, and then slew to the target coordinate.
311              */
312         void SlewToTarget();
313 
314         /**
315          * @brief getStellarSolverProfiles
316          * @return list of StellarSolver profile names
317          */
318         QStringList getStellarSolverProfiles();
319 
currentGOTOMode()320         GotoMode currentGOTOMode() const
321         {
322             return m_CurrentGotoMode;
323         }
324 
325         /**
326              * @brief generateOptions Generate astrometry.net option given the supplied map
327              * @param optionsMap List of key=value pairs for all astrometry.net options
328              * @return String List of valid astrometry.net options
329              */
330         static QStringList generateRemoteOptions(const QVariantMap &optionsMap);
331         static void generateFOVBounds(double fov_h, QString &fov_low, QString &fov_high, double tolerance = 0.05);
332 
333         // access to the mount model UI, required for testing
mountModel()334         MountModel * mountModel() const
335         {
336             return m_MountModel;
337         }
338 
polarAlignmentAssistant()339         PolarAlignmentAssistant *polarAlignmentAssistant() const
340         {
341             return m_PolarAlignmentAssistant;
342         }
343 
wcsSynced()344         bool wcsSynced() const
345         {
346             return m_wcsSynced;
347         }
348 
349         /**
350              * @brief Process updated device properties
351              * @param nvp pointer to updated property.
352              */
353         void processNumber(INumberVectorProperty *nvp);
354 
355         /**
356              * @brief Process updated device properties
357              * @param svp pointer to updated property.
358              */
359         void processSwitch(ISwitchVectorProperty *svp);
360 
361         /**
362              * @brief Check CCD and make sure information is updated and FOV is re-calculated.
363              * @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.
364              */
365         void checkCCD(int CCDNum = -1);
366 
367         /**
368              * @brief Check Filter and make sure information is updated accordingly.
369              * @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.
370              *  filterNum is the index of the filter in the dropdown menu.
371              */
372         void checkFilter(int filterNum = -1);
373 
374         /**
375              * @brief checkCCDExposureProgress Track the progress of CCD exposure
376              * @param targetChip Target chip under exposure
377              * @param remaining how many seconds remaining
378              * @param state status of exposure
379              */
380         void checkCCDExposureProgress(ISD::CCDChip *targetChip, double remaining, IPState state);
381         /**
382              * @brief Process new FITS received from CCD.
383              * @param bp pointer to blob property
384              */
385         void processData(const QSharedPointer<FITSData> &data);
386 
387         /** DBUS interface function.
388              * 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
389              * the telescope is pointing to the same coordinates of the image.
390              * @param image buffer to image data.
391              * @param extension image extension (e.g. cr2, jpg, fits,..etc).
392              */
393         bool loadAndSlew(const QByteArray &image, const QString &extension);
394 
395         /** \addtogroup AlignDBusInterface
396              *  @{
397              */
398 
399         /**
400          * @brief Stop aligning
401          * @param mode stop mode (abort or suspend)
402          */
403         void stop(Ekos::AlignState mode);
404 
405         /** DBUS interface function.
406              * Aborts the solving operation, handle outside of the align module.
407              */
abort()408         Q_SCRIPTABLE Q_NOREPLY void abort()
409         {
410             stop(ALIGN_ABORTED);
411         }
412 
413         /**
414          * @brief Suspend aligning, recovery handled by the align module itself.
415          */
suspend()416         void suspend()
417         {
418             stop(ALIGN_SUSPENDED);
419         }
420 
421         /** DBUS interface function.
422              * Select the solver mode
423              * @param type Set solver type. 0 LOCAL, 1 REMOTE (requires remote astrometry driver to be activated)
424              */
425         Q_SCRIPTABLE Q_NOREPLY void setSolverMode(int mode);
426 
427         /** DBUS interface function.
428              * Capture and solve an image using the astrometry.net engine
429              * @return Returns true if the procedure started successful, false otherwise.
430              */
431         Q_SCRIPTABLE bool captureAndSolve();
432 
433         /** DBUS interface function.
434              * 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
435              * the telescope is pointing to the same coordinates of the image.
436              * @param fileURL URL to the image to solve
437              */
438         Q_SCRIPTABLE bool loadAndSlew(QString fileURL = QString());
439 
440         /** DBUS interface function.
441              * Sets the target coordinates that the solver compares the solution coordinates to.
442              * By default, the target coordinates are those of the current mount when the capture and
443              * solve operation is started. In case of SYNC, only the error between the the solution and target
444              * coordinates is calculated. When Slew to Target is selected, the mount would be slewed afterwards to
445              * this target coordinate.
446              * @param ra0 J2000 Right Ascension in hours.
447              * @param de0 J2000 Declination in degrees.
448              */
449         Q_SCRIPTABLE Q_NOREPLY void setTargetCoords(double ra0, double de0);
450 
451         /**
452          * @brief getTargetCoords QList of target coordinates.
453          * @return First value is J2000 RA in hours. Second value is J2000 DE in degrees.
454          */
455         Q_SCRIPTABLE QList<double> getTargetCoords();
456 
457 
458         /**
459           * @brief Set the alignment target where the mount is expected to point at.
460           * @param targetObject object close to the target position
461           * @param targetCoord exact coordinates of the target position (could slightly differ to targetObject)
462           */
463         void setTarget(const SkyObject &targetObject, const SkyPoint &targetCoord);
464 
465         /**
466          * @brief Clear the target, make it invalid.
467          */
clearTarget()468         Q_SCRIPTABLE Q_NOREPLY void clearTarget()
469         {
470             m_targetCoordValid = false;
471         }
472 
473         /**
474          * @brief Set the coordinates that the mount reports as its position
475          * @param position current mount position
476          */
setTelescopeCoordinates(const SkyPoint & position)477         void setTelescopeCoordinates(const SkyPoint &position)
478         {
479             telescopeCoord = position;
480         }
481 
482 
483         Q_SCRIPTABLE Q_NOREPLY void setTargetRotation(double rotation);
484 
485         /** DBUS interface function.
486              * Sets the binning of the selected CCD device.
487              * @param binIndex Index of binning value. Default values range from 0 (binning 1x1) to 3 (binning 4x4)
488              */
489         Q_SCRIPTABLE Q_NOREPLY void setBinningIndex(int binIndex);
490 
491         /** @}*/
492 
493         /**
494              * @brief Solver finished successfully, process the data and execute the required actions depending on the mode.
495              * @param orientation Orientation of image in degrees (East of North)
496              * @param ra Center RA in solved image, degrees.
497              * @param dec Center DEC in solved image, degrees.
498              * @param pixscale Image scale is arcsec/pixel
499              * @param eastToTheRight When the image is rotated, so that North is up, East would be to the right.
500              */
501         void solverFinished(double orientation, double ra, double dec, double pixscale, bool eastToTheRight);
502 
503         void solverComplete();
504 
505         /**
506          * @brief If the target is valid (see m_targetCoordValid), simply return. If the target is not valid,
507          * use thecurrent mount coordinates as target coordinates.
508          */
509         void updateTargetCoords();
510 
511         /**
512              * @brief Process solver failure.
513              */
514         void solverFailed();
515 
516         /**
517              * @brief We received new telescope info, process them and update FOV.
518              */
519         bool syncTelescopeInfo();
520 
521         void setFocusStatus(Ekos::FocusState state);
522 
523         // Log
524         void appendLogText(const QString &);
525 
526         // Capture
527         void setCaptureComplete();
528 
529         // Update Capture Module status
530         void setCaptureStatus(Ekos::CaptureState newState);
531         // Update Mount module status
532         void setMountStatus(ISD::Telescope::Status newState);
533         //        void setMountCoords(const QString &ra, const QString &dec, const QString &az,
534         //                            const QString &alt, int pierSide, const QString &ha);
535 
536         // Align Settings
537         QJsonObject getSettings() const;
538         void setSettings(const QJsonObject &settings);
539 
540         void zoomAlignView();
541         void setAlignZoom(double scale);
542 
543     private slots:
544 
545         void setDefaultCCD(QString ccd);
546 
547         void saveSettleTime();
548 
549         // Solver timeout
550         void checkAlignmentTimeout();
551         void setAlignTableResult(AlignResult result);
552 
553         void updateTelescopeType(int index);
554 
555         // External View
556         void showFITSViewer();
557         void toggleAlignWidgetFullScreen();
558 
559         /**
560          * @brief prepareCapture Set common settings for capture for align module
561          * @param targetChip target Chip
562          */
563         void prepareCapture(ISD::CCDChip *targetChip);
564 
565         //Solutions Display slots
566         void buildTarget();
567         void handlePointTooltip(QMouseEvent *event);
568         void handleVerticalPlotSizeChange();
569         void handleHorizontalPlotSizeChange();
570         void selectSolutionTableRow(int row, int column);
571         void slotClearAllSolutionPoints();
572         void slotRemoveSolutionPoint();
573         void slotAutoScaleGraph();
574 
575         void slotMountModel();
576 
577         // Settings
578         void syncSettings();
579 
580     protected slots:
581         /**
582              * @brief After a solver process is completed successfully, sync, slew to target, or do nothing as set by the user.
583              */
584         void executeGOTO();
585 
586         /**
587          * @brief refreshAlignOptions is called when settings are updated in OpsAlign.
588          */
589         void refreshAlignOptions();
590 
591         void processPAHStage(int stage);
592 
593     signals:
594         void newLog(const QString &text);
595         void newStatus(Ekos::AlignState state);
596         void newSolution(const QVariantMap &solution);
597 
598         // This is sent when we load an image in the view
599         void newImage(FITSView *view);
600         // This is sent when the pixmap is updated within the view
601         void newFrame(FITSView *view);
602         // Send new solver results
603         void newSolverResults(double orientation, double ra, double dec, double pixscale);
604 
605         // Settings
606         void settingsUpdated(const QJsonObject &settings);
607 
608     private:
609         /**
610          * @brief Retrieve the align status indicator
611          */
612         QProgressIndicator *getProgressStatus();
613 
614         /**
615          * @brief Stop the progress animation in the solution table
616          */
617         void stopProgressAnimation();
618 
619         void exportSolutionPoints();
620 
621         /**
622             * @brief Calculate Field of View of CCD+Telescope combination that we need to pass to astrometry.net solver.
623             */
624         void calculateFOV();
625 
626         /**
627          * @brief calculateEffectiveFocalLength Calculate Focal Length purely form astrometric data.
628          */
629         void calculateEffectiveFocalLength(double newFOVW);
630 
631         /**
632          * @brief calculateAlignTargetDiff Find the difference between aligned vs. target coordinates and update
633          * the GUI accordingly.
634          */
635         void calculateAlignTargetDiff();
636 
637         /**
638              * @brief Get formatted RA & DEC coordinates compatible with astrometry.net format.
639              * @param ra Right ascension
640              * @param dec Declination
641              * @param ra_str will contain the formatted RA string
642              * @param dec_str will contain the formatted DEC string
643              */
644         void getFormattedCoords(double ra, double dec, QString &ra_str, QString &dec_str);
645 
646         uint8_t getSolverDownsample(uint16_t binnedW);
647 
648         /**
649              * @brief setWCSEnabled enables/disables World Coordinate System settings in the CCD driver.
650              * @param enable true to enable WCS, false to disable.
651              */
652         void setWCSEnabled(bool enable);
653 
654         void resizeEvent(QResizeEvent *event) override;
655 
656         KPageWidgetItem *m_IndexFilesPage;
657         QString savedOptionsProfiles;
658 
659         /**
660          * @brief React when a mount motion has been detected
661          */
662         void handleMountMotion();
663 
664         /**
665          * @brief Continue aligning according to the current mount status
666          */
667         void handleMountStatus();
668 
669         /**
670          * @brief initPolarAlignmentAssistant Initialize Polar Alignment Asssistant Tool
671          */
672         void initPolarAlignmentAssistant();
673 
674         /**
675          * @brief initManualRotator Initialize Manual Rotator Tool
676          */
677         void initManualRotator();
678 
679         /**
680          * @brief initDarkProcessor Initialize Dark Processor
681          */
682         void initDarkProcessor();
683 
684         bool matchPAHStage(uint32_t stage);
685 
686 
687         // Effective FOV
688 
689         /**
690          * @brief getEffectiveFOV Search database for effective FOV that matches the current profile and settings
691          * @return Variant Map containing effect FOV data or empty variant map if none found
692          */
693         QVariantMap getEffectiveFOV();
694         void saveNewEffectiveFOV(double newFOVW, double newFOVH);
695         QList<QVariantMap> effectiveFOVs;
696         void syncFOV();
697 
698         // We are using calculated FOV now until a more accurate effective FOV is found.
699         bool m_EffectiveFOVPending { false };
700         /// Which chip should we invoke in the current CCD?
701         bool useGuideHead { false };
702         /// Can the mount sync its coordinates to those set by Ekos?
703         bool canSync { false };
704         // m_SolveFromFile is true we load an image and solve it, no capture is done.
705         bool m_SolveFromFile { false };
706         // Target Position Angle of solver Load&Slew image to be used for rotator if necessary
707         double loadSlewTargetPA { std::numeric_limits<double>::quiet_NaN() };
708         double currentRotatorPA { -1 };
709         /// Solver iterations count
710         uint8_t solverIterations { 0 };
711         /// Was solving with scale off used?
712         BlindState useBlindScale {BLIND_IDLE};
713         /// Was solving with position off used?
714         BlindState useBlindPosition {BLIND_IDLE};
715 
716         // FOV
717         double ccd_hor_pixel { -1 };
718         double ccd_ver_pixel { -1 };
719         double focal_length { -1 };
720         double aperture { -1 };
721         double fov_x { 0 };
722         double fov_y { 0 };
723         double fov_pixscale { 0 };
724         int ccd_width { 0 };
725         int ccd_height { 0 };
726 
727         // Keep track of solver results
728         double sOrientation { INVALID_VALUE };
729         double sRA { INVALID_VALUE };
730         double sDEC { INVALID_VALUE };
731 
732         /// Solver alignment coordinates
733         SkyPoint alignCoord;
734         /// Target coordinates we need to slew to
735         SkyPoint m_targetCoord;
736         /// do we have valid target coordinates?
737         bool m_targetCoordValid = false;
738         /// Current telescope coordinates
739         SkyPoint telescopeCoord;
740         /// Coord from Load & Slew
741         SkyPoint loadSlewCoord;
742         /// Difference between solution and target coordinate
743         double m_TargetDiffTotal { 1e6 };
744         double m_TargetDiffRA { 1e6 };
745         double m_TargetDiffDE { 1e6 };
746 
747         /// Progress icon if the solver is running
748         std::unique_ptr<QProgressIndicator> pi;
749 
750         /// Keep track of how long the solver is running
751         QElapsedTimer solverTimer;
752 
753         // StellarSolver Profiles
754         std::unique_ptr<StellarSolver> m_StellarSolver;
755         QList<SSolver::Parameters> m_StellarSolverProfiles;
756 
757         /// Have we slewed?
758         bool m_wasSlewStarted { false };
759         // Above flag only stays false for 10s after slew start.
760         QElapsedTimer slewStartTimer;
761         bool didSlewStart();
762         // Only wait this many milliseconds for slew to start.
763         // Otherwise assume it has begun.
764         static constexpr int MAX_WAIT_FOR_SLEW_START_MSEC = 10000;
765 
766         // Online and Offline parsers
767         AstrometryParser* parser { nullptr };
768         std::unique_ptr<RemoteAstrometryParser> remoteParser;
769         ISD::GDInterface *remoteParserDevice { nullptr };
770 
771         // Pointers to our devices
772         ISD::Telescope *currentTelescope { nullptr };
773         ISD::Dome *currentDome { nullptr };
774         ISD::CCD *currentCCD { nullptr };
775         ISD::GDInterface *currentRotator { nullptr };
776         QList<ISD::CCD *> CCDs;
777 
778         /// Optional device filter
779         ISD::GDInterface *currentFilter { nullptr };
780         /// They're generic GDInterface because they could be either ISD::CCD or ISD::Filter
781         QList<ISD::GDInterface *> Filters;
782         int currentFilterPosition { -1 };
783         /// True if we need to change filter position and wait for result before continuing capture
784         bool filterPositionPending { false };
785 
786         /// Keep track of solver FOV to be plotted in the skymap after each successful solve operation
787         std::shared_ptr<FOV> solverFOV;
788         std::shared_ptr<FOV> sensorFOV;
789 
790         /// WCS
791         bool m_wcsSynced { false };
792 
793         /// Log
794         QStringList m_LogText;
795 
796         /// Issue counters
797         uint8_t m_CaptureTimeoutCounter { 0 };
798         uint8_t m_CaptureErrorCounter { 0 };
799         uint8_t m_SlewErrorCounter { 0 };
800 
801         QTimer m_CaptureTimer;
802 
803         // State
804         AlignState state { ALIGN_IDLE };
805         FocusState m_FocusState { FOCUS_IDLE };
806         CaptureState m_CaptureState { CAPTURE_IDLE };
807 
808         // 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
809         ISD::CCD::UploadMode rememberUploadMode { ISD::CCD::UPLOAD_CLIENT };
810 
811         GotoMode m_CurrentGotoMode;
812 
813         QString dirPath;
814 
815         // Timer
816         QTimer m_AlignTimer;
817 
818         // Align Frame
819         AlignView *alignView { nullptr };
820 
821         // FITS Viewer in case user want to display in it instead of internal view
822         QPointer<FITSViewer> fv;
823 
824         QUrl alignURL;
825         QUrl alignURLPath;
826 
827         // keep track of autoWSC
828         bool rememberAutoWCS { false };
829         bool rememberSolverWCS { false };
830         //bool rememberMeridianFlip { false };
831 
832         // Differential Slewing
833         bool differentialSlewingActivated { false };
834         bool targetAccuracyNotMet { false };
835 
836         // Astrometry Options
837         OpsAstrometry *opsAstrometry { nullptr };
838         OpsAlign *opsAlign { nullptr };
839         OpsPrograms *opsPrograms { nullptr };
840         OpsAstrometryIndexFiles *opsAstrometryIndexFiles { nullptr };
841         OpsASTAP *opsASTAP { nullptr };
842         StellarSolverProfileEditor *optionsProfileEditor { nullptr };
843 
844         // Drawing
845         QCPCurve *centralTarget { nullptr };
846         QCPCurve *yellowTarget { nullptr };
847         QCPCurve *redTarget { nullptr };
848         QCPCurve *concentricRings { nullptr };
849 
850         // Telescope Settings
851         ISD::CCD::TelescopeType rememberTelescopeType = { ISD::CCD::TELESCOPE_UNKNOWN };
852         double primaryFL = -1, primaryAperture = -1, guideFL = -1, guideAperture = -1;
853         double primaryEffectiveFL = -1, guideEffectiveFL = -1;
854         bool m_isRateSynced = false;
855         bool domeReady = true;
856 
857         // CCD Exposure Looping
858         bool m_RememberCameraFastExposure = { false };
859 
860         // Controls
861         double GainSpinSpecialValue {INVALID_VALUE};
862         double TargetCustomGainValue {-1};
863 
864         // Filter Manager
865         QSharedPointer<FilterManager> filterManager;
866 
867         // Data
868         QSharedPointer<FITSData> m_ImageData;
869 
870         // Active Profile
871         ProfileInfo *m_ActiveProfile { nullptr };
872 
873         // Threshold to notify settle time is 3 seconds
874         static constexpr uint16_t DELAY_THRESHOLD_NOTIFY { 3000 };
875 
876         // Mount Model
877         // N.B. We do not need to use "smart pointer" here as the object memroy
878         // is taken care of by the Qt framework.
879         MountModel *m_MountModel {nullptr};
880         PolarAlignmentAssistant *m_PolarAlignmentAssistant {nullptr};
881         ManualRotator *m_ManualRotator {nullptr};
882 
883         // Dark Processor
884         QPointer<DarkProcessor> m_DarkProcessor;
885 
886 };
887 }
888