File indexing completed on 2024-04-28 03:43:15

0001 /*  Ekos state machine for the Capture module
0002     SPDX-FileCopyrightText: 2022 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QObject>
0010 
0011 #include "Options.h"
0012 
0013 #include "ekos/ekos.h"
0014 #include "indiapi.h"
0015 #include "indi/indistd.h"
0016 #include "indi/indidustcap.h"
0017 #include "indi/indicamera.h"
0018 #include "indi/indimount.h"
0019 #include "indi/indidome.h"
0020 
0021 #include "ekos/manager/meridianflipstate.h"
0022 #include "ekos/auxiliary/filtermanager.h"
0023 #include "ekos/scheduler/schedulerjob.h"
0024 
0025 // Wait 3-minutes as maximum beyond exposure
0026 // value.
0027 #define CAPTURE_TIMEOUT_THRESHOLD  180000
0028 
0029 class FITSData;
0030 
0031 namespace Ekos
0032 {
0033 
0034 // Typedef for HFR Check algorithms.
0035 typedef enum
0036 {
0037     HFR_CHECK_LAST_AUTOFOCUS, /* Use last Autofocus as reference   */
0038     HFR_CHECK_FIXED,          /* User supplied fixed reference     */
0039     HFR_CHECK_MEDIAN_MEASURE, /* Use median algorithm as reference */
0040     HFR_CHECK_MAX_ALGO        /* Max counter for enum              */
0041 } HFR_CHECK_ALGORITHM;
0042 constexpr double HFR_CHECK_DEFAULT_THRESHOLD = 10.0;
0043 
0044 class SequenceJob;
0045 class SequenceQueue;
0046 class CaptureDeviceAdaptor;
0047 class RefocusState;
0048 
0049 class CaptureModuleState: public QObject
0050 {
0051         Q_OBJECT
0052     public:
0053         /* Action types to be executed before capturing may start. */
0054         typedef enum
0055         {
0056             ACTION_FILTER,              /* Change the filter and wait until the correct filter is set.                                    */
0057             ACTION_TEMPERATURE,         /* Set the camera chip target temperature and wait until the target temperature has been reached. */
0058             ACTION_ROTATOR,             /* Set the camera rotator target angle and wait until the target angle has been reached.          */
0059             ACTION_PREPARE_LIGHTSOURCE, /* Setup the selected flat lights source.                                                         */
0060             ACTION_MOUNT_PARK,          /* Park the mount.                                                                                */
0061             ACTION_DOME_PARK,           /* Park the dome.                                                                                 */
0062             ACTION_FLAT_SYNC_FOCUS,     /* Move the focuser to the focus position for the selected filter.                                */
0063             ACTION_SCOPE_COVER,         /* Ensure that the scope cover (if present) is opened.                                            */
0064             ACTION_AUTOFOCUS            /* Execute autofocus (might be triggered due to filter change).                                   */
0065         } PrepareActions;
0066 
0067         typedef enum
0068         {
0069             SHUTTER_YES,    /* the CCD has a shutter                             */
0070             SHUTTER_NO,     /* the CCD has no shutter                            */
0071             SHUTTER_BUSY,   /* determining whether the CCD has a shutter running */
0072             SHUTTER_UNKNOWN /* unknown whether the CCD has a shutter             */
0073         } ShutterStatus;
0074 
0075         typedef enum
0076         {
0077             CAP_IDLE,
0078             CAP_PARKING,
0079             CAP_UNPARKING,
0080             CAP_PARKED,
0081             CAP_ERROR,
0082             CAP_UNKNOWN
0083         } CapState;
0084 
0085         typedef enum
0086         {
0087             CAP_LIGHT_OFF,     /* light is on               */
0088             CAP_LIGHT_ON,      /* light is off              */
0089             CAP_LIGHT_UNKNOWN, /* unknown whether on or off */
0090             CAP_LIGHT_BUSY     /* light state changing      */
0091         } LightState;
0092 
0093         typedef enum
0094         {
0095             CONTINUE_ACTION_NONE,            /* do nothing              */
0096             CONTINUE_ACTION_NEXT_EXPOSURE,   /* start next exposure     */
0097             CONTINUE_ACTION_CAPTURE_COMPLETE /* recall capture complete */
0098         } ContinueAction;
0099 
0100         /* Result when starting to capture {@see SequenceJob::capture(bool, FITSMode)}. */
0101         typedef enum
0102         {
0103             CAPTURE_OK,               /* Starting a new capture succeeded.                                          */
0104             CAPTURE_FRAME_ERROR,      /* Setting frame parameters failed, capture not started.                      */
0105             CAPTURE_BIN_ERROR,        /* Setting binning parameters failed, capture not started.                    */
0106             CAPTURE_FOCUS_ERROR,      /* NOT USED.                                                                  */
0107         } CAPTUREResult;
0108 
0109         /* Interval with double lower and upper bound */
0110         typedef struct
0111         {
0112             double min, max;
0113         } DoubleRange;
0114 
0115         CaptureModuleState(QObject *parent = nullptr);
0116 
0117         // ////////////////////////////////////////////////////////////////////
0118         // sequence jobs
0119         // ////////////////////////////////////////////////////////////////////
0120         QList<SequenceJob *> &allJobs();
0121 
0122         QSharedPointer<SequenceQueue> getSequenceQueue()
0123         {
0124             return m_sequenceQueue;
0125         }
0126 
0127         SequenceJob *getActiveJob() const
0128         {
0129             return m_activeJob;
0130         }
0131         void setActiveJob(SequenceJob *value);
0132 
0133         const QUrl &sequenceURL() const;
0134         void setSequenceURL(const QUrl &newSequenceURL);
0135 
0136         // ////////////////////////////////////////////////////////////////////
0137         // pointer to last captured frame
0138         // ////////////////////////////////////////////////////////////////////
0139         QSharedPointer<FITSData> imageData()
0140         {
0141             return m_ImageData;
0142         }
0143         void setImageData(QSharedPointer<FITSData> newImageData)
0144         {
0145             m_ImageData = newImageData;
0146         }
0147 
0148         // ////////////////////////////////////////////////////////////////////
0149         // capture attributes
0150         // ////////////////////////////////////////////////////////////////////
0151         // current filter ID
0152         int currentFilterID { Ekos::INVALID_VALUE };
0153         // Map tracking whether the current value has been initialized.
0154         // With this construct we could do a lazy initialization of current values if setCurrent...()
0155         // sets this flag to true. This is necessary since we listen to the events, but as long as
0156         // the value does not change, there won't be an event.
0157         QMap<PrepareActions, bool> isInitialized;
0158 
0159         // ////////////////////////////////////////////////////////////////////
0160         // flat preparation attributes
0161         // ////////////////////////////////////////////////////////////////////
0162         // flag if telescope has been covered
0163         typedef enum
0164         {
0165             MANAUL_COVER_OPEN,
0166             MANUAL_COVER_CLOSED_LIGHT,
0167             MANUAL_COVER_CLOSED_DARK
0168         } ManualCoverState;
0169 
0170         ManualCoverState m_ManualCoverState { MANAUL_COVER_OPEN };
0171         // flag if there is a light box device
0172         bool hasLightBox { false };
0173         // flag if there is a dust cap device
0174         bool hasDustCap { false };
0175         // flag if there is a telescope device
0176         bool hasTelescope { false };
0177         // flag if there is a dome device
0178         bool hasDome { false };
0179 
0180         // ////////////////////////////////////////////////////////////////////
0181         // dark preparation attributes
0182         // ////////////////////////////////////////////////////////////////////
0183         ShutterStatus shutterStatus { SHUTTER_UNKNOWN };
0184 
0185 
0186         // ////////////////////////////////////////////////////////////////////
0187         // state changes
0188         // ////////////////////////////////////////////////////////////////////
0189         /**
0190          * @brief captureStarted The preparation to capture have been started.
0191          */
0192         void initCapturePreparation();
0193 
0194         // ////////////////////////////////////////////////////////////////////
0195         // state accessors
0196         // ////////////////////////////////////////////////////////////////////
0197         CaptureState getCaptureState() const
0198         {
0199             return m_CaptureState;
0200         }
0201         void setCaptureState(CaptureState value);
0202 
0203         bool isStartingCapture() const
0204         {
0205             return m_StartingCapture;
0206         }
0207         void setStartingCapture(bool newStartingCapture)
0208         {
0209             m_StartingCapture = newStartingCapture;
0210         }
0211 
0212         ContinueAction getContinueAction() const
0213         {
0214             return m_ContinueAction;
0215         }
0216         void setContinueAction(ContinueAction newPauseFunction)
0217         {
0218             m_ContinueAction = newPauseFunction;
0219         }
0220 
0221         FocusState getFocusState() const
0222         {
0223             return m_FocusState;
0224         }
0225         void setFocusState(FocusState value)
0226         {
0227             m_FocusState = value;
0228         }
0229 
0230         GuideState getGuideState() const
0231         {
0232             return m_GuideState;
0233         }
0234         void setGuideState(GuideState state);
0235 
0236         // short cut for all guiding states that indicate guiding is on
0237         bool isGuidingOn();
0238         // short cut for all guiding states that indicate guiding in state GUIDING
0239         bool isActivelyGuiding();
0240 
0241         bool useGuideHead() const
0242         {
0243             return m_useGuideHead;
0244         }
0245         void setUseGuideHead(bool value)
0246         {
0247             m_useGuideHead = value;
0248         }
0249         bool isGuidingDeviationDetected() const
0250         {
0251             return m_GuidingDeviationDetected;
0252         }
0253         void setGuidingDeviationDetected(bool newDeviationDetected)
0254         {
0255             m_GuidingDeviationDetected = newDeviationDetected;
0256         }
0257 
0258         bool suspendGuidingOnDownload() const
0259         {
0260             return m_SuspendGuidingOnDownload;
0261         }
0262         void setSuspendGuidingOnDownload(bool value)
0263         {
0264             m_SuspendGuidingOnDownload = value;
0265         }
0266 
0267         int SpikesDetected() const
0268         {
0269             return m_SpikesDetected;
0270         }
0271         int increaseSpikesDetected()
0272         {
0273             return ++m_SpikesDetected;
0274         }
0275         void resetSpikesDetected()
0276         {
0277             m_SpikesDetected = 0;
0278         }
0279 
0280         IPState getDitheringState() const
0281         {
0282             return m_DitheringState;
0283         }
0284         void setDitheringState(IPState value)
0285         {
0286             m_DitheringState = value;
0287         }
0288 
0289         AlignState getAlignState() const
0290         {
0291             return m_AlignState;
0292         }
0293         void setAlignState(AlignState value);
0294 
0295         FilterState getFilterManagerState() const
0296         {
0297             return m_FilterManagerState;
0298         }
0299         void setFilterManagerState(FilterState value)
0300         {
0301             m_FilterManagerState = value;
0302         }
0303 
0304         int getCurrentFilterPosition() const
0305         {
0306             return m_CurrentFilterPosition;
0307         }
0308 
0309         const QString &getCurrentFilterName() const
0310         {
0311             return m_CurrentFilterName;
0312         }
0313 
0314         const QString &CurrentFocusFilterName() const
0315         {
0316             return m_CurrentFocusFilterName;
0317         }
0318 
0319         void setCurrentFilterPosition(int position, const QString &name, const QString &focusFilterName);
0320 
0321         LightState getLightBoxLightState() const
0322         {
0323             return m_lightBoxLightState;
0324         }
0325         void setLightBoxLightState(LightState value)
0326         {
0327             m_lightBoxLightState = value;
0328         }
0329 
0330         bool lightBoxLightEnabled() const
0331         {
0332             return m_lightBoxLightEnabled;
0333         }
0334         void setLightBoxLightEnabled(bool value)
0335         {
0336             m_lightBoxLightEnabled = value;
0337         }
0338 
0339         CapState getDustCapState() const
0340         {
0341             return m_dustCapState;
0342         }
0343         void setDustCapState(CapState value)
0344         {
0345             m_dustCapState = value;
0346         }
0347 
0348         ISD::Mount::Status getScopeState() const
0349         {
0350             return m_scopeState;
0351         }
0352         void setScopeState(ISD::Mount::Status value)
0353         {
0354             m_scopeState = value;
0355         }
0356 
0357         ISD::Mount::PierSide getPierSide() const
0358         {
0359             return m_pierSide;
0360         }
0361         void setPierSide(ISD::Mount::PierSide value)
0362         {
0363             m_pierSide = value;
0364         }
0365 
0366         ISD::ParkStatus getScopeParkState() const
0367         {
0368             return m_scopeParkState;
0369         }
0370         void setScopeParkState(ISD::ParkStatus value)
0371         {
0372             m_scopeParkState = value;
0373         }
0374 
0375         ISD::Dome::Status getDomeState() const
0376         {
0377             return m_domeState;
0378         }
0379         void setDomeState(ISD::Dome::Status value)
0380         {
0381             m_domeState = value;
0382         }
0383 
0384         QSharedPointer<MeridianFlipState> getMeridianFlipState();
0385         void setMeridianFlipState(QSharedPointer<MeridianFlipState> state);
0386 
0387         QSharedPointer<RefocusState> getRefocusState() const
0388         {
0389             return m_refocusState;
0390         }
0391 
0392         const QString &observerName() const
0393         {
0394             return m_ObserverName;
0395         }
0396         void setObserverName(const QString &value);
0397 
0398         bool ignoreJobProgress() const
0399         {
0400             return m_ignoreJobProgress;
0401         }
0402         void setIgnoreJobProgress(bool value)
0403         {
0404             m_ignoreJobProgress = value;
0405         }
0406 
0407         bool isRememberFastExposure() const
0408         {
0409             return m_RememberFastExposure;
0410         }
0411         void setRememberFastExposure(bool value)
0412         {
0413             m_RememberFastExposure = value;
0414         }
0415 
0416         bool dirty() const
0417         {
0418             return m_Dirty;
0419         }
0420         void setDirty(bool value)
0421         {
0422             m_Dirty = value;
0423         }
0424 
0425         bool isBusy() const
0426         {
0427             return m_Busy;
0428         }
0429         void setBusy(bool busy);
0430 
0431         bool isLooping() const
0432         {
0433             return m_Looping;
0434         }
0435         void setLooping(bool newLooping)
0436         {
0437             m_Looping = newLooping;
0438         }
0439 
0440         QJsonArray &getSequence()
0441         {
0442             return m_SequenceArray;
0443         }
0444         void setSequence(const QJsonArray &value)
0445         {
0446             m_SequenceArray = value;
0447         }
0448 
0449         // ////////////////////////////////////////////////////////////////////
0450         // counters
0451         // ////////////////////////////////////////////////////////////////////
0452 
0453         int getAlignmentRetries() const
0454         {
0455             return m_AlignmentRetries;
0456         }
0457         int increaseAlignmentRetries()
0458         {
0459             return ++m_AlignmentRetries;
0460         }
0461         void resetAlignmentRetries()
0462         {
0463             m_AlignmentRetries = 0;
0464         }
0465 
0466         int getDitherCounter() const
0467         {
0468             return m_ditherCounter;
0469         }
0470         void decreaseDitherCounter();
0471 
0472         /**
0473          * @brief resetDitherCounter Reset the dither counter to its start value. If a per job counter is
0474          * set to > 0, this value is used and the general dither counter otherwise.
0475          */
0476         void resetDitherCounter();
0477 
0478         /**
0479          * @brief checkSeqBoundary Determine the next file number sequence.
0480          *        That is, if we have file1.png and file2.png, then the next
0481          *        sequence should be file3.png.
0482          */
0483         void checkSeqBoundary();
0484 
0485         int nextSequenceID() const
0486         {
0487             return m_nextSequenceID;
0488         }
0489         void setNextSequenceID(int id)
0490         {
0491             m_nextSequenceID = id;
0492         }
0493 
0494         uint16_t capturedFramesCount(const QString &signature) const
0495         {
0496             return m_capturedFramesMap[signature];
0497         }
0498         void setCapturedFramesCount(const QString &signature, uint16_t count);
0499 
0500         double lastRemainingFrameTimeMS() const
0501         {
0502             return m_lastRemainingFrameTimeMS;
0503         }
0504         void setLastRemainingFrameTimeMS(double value)
0505         {
0506             m_lastRemainingFrameTimeMS = value;
0507         }
0508 
0509         // ////////////////////////////////////////////////////////////////////
0510         // Timers
0511         // ////////////////////////////////////////////////////////////////////
0512         QTimer &getCaptureDelayTimer()
0513         {
0514             return m_captureDelayTimer;
0515         }
0516         QTimer &getCaptureTimeout()
0517         {
0518             return m_captureTimeout;
0519         }
0520         uint8_t captureTimeoutCounter() const
0521         {
0522             return m_CaptureTimeoutCounter;
0523         }
0524         void setCaptureTimeoutCounter(uint8_t value)
0525         {
0526             m_CaptureTimeoutCounter = value;
0527         }
0528         uint8_t deviceRestartCounter() const
0529         {
0530             return m_DeviceRestartCounter;
0531         }
0532         void setDeviceRestartCounter(uint8_t value)
0533         {
0534             m_DeviceRestartCounter = value;
0535         }
0536         QTimer &downloadProgressTimer()
0537         {
0538             return m_downloadProgressTimer;
0539         }
0540         QElapsedTimer &downloadTimer()
0541         {
0542             return m_DownloadTimer;
0543         }
0544         QTimer &getSeqDelayTimer()
0545         {
0546             return m_seqDelayTimer;
0547         }
0548         QTimer &getGuideDeviationTimer()
0549         {
0550             return m_guideDeviationTimer;
0551         }
0552 
0553         QTime &imageCountDown()
0554         {
0555             return m_imageCountDown;
0556         }
0557         void imageCountDownAddMSecs(int value)
0558         {
0559             m_imageCountDown = m_imageCountDown.addMSecs(value);
0560         }
0561 
0562         QTime &sequenceCountDown()
0563         {
0564             return m_sequenceCountDown;
0565         }
0566         void sequenceCountDownAddMSecs(int value)
0567         {
0568             m_sequenceCountDown = m_sequenceCountDown.addMSecs(value);
0569         }
0570 
0571         /**
0572          * @brief changeSequenceValue Change a single sequence in the sequence array
0573          * @param index position in the array
0574          * @param key sequence key
0575          * @param value new sequence value
0576          */
0577         void changeSequenceValue(int index, QString key, QString value);
0578 
0579         // ////////////////////////////////////////////////////////////////////
0580         // Action checks
0581         // ////////////////////////////////////////////////////////////////////
0582         /**
0583              * @brief Check, whether dithering is necessary and, in that case initiate it.
0584              *
0585              *  Dithering is only required for batch images and does not apply for PREVIEW.
0586              *
0587              * There are several situations that determine, if dithering is necessary:
0588              * 1. the current job captures light frames AND the dither counter has reached 0 AND
0589              * 2. guiding is running OR the manual dithering option is selected AND
0590              * 3. there is a guiding camera active AND
0591              * 4. there hasn't just a meridian flip been finised.
0592              *
0593              * @return true iff dithering is necessary.
0594              */
0595 
0596         bool checkDithering();
0597 
0598         bool checkCapturing()
0599         {
0600             return (m_CaptureState == CAPTURE_CAPTURING || m_CaptureState == CAPTURE_PAUSE_PLANNED);
0601         }
0602 
0603         /**
0604          * @brief updateMFMountState Handle changes of the meridian flip mount state
0605          */
0606         void updateMFMountState(MeridianFlipState::MeridianFlipMountState status);
0607 
0608         /**
0609          * @brief updateMeridianFlipStage Update the meridian flip stage
0610          */
0611         void updateMeridianFlipStage(const MeridianFlipState::MFStage &stage);
0612 
0613         /**
0614          * @brief checkMeridianFlipActive
0615          * @return true iff the meridian flip itself or post flip actions are running
0616          */
0617         bool checkMeridianFlipActive();
0618 
0619         /**
0620          * @brief Check whether a meridian flip has been requested and trigger it
0621          * @return true iff a meridian flip has been triggered
0622          */
0623         bool checkMeridianFlipReady();
0624 
0625         /**
0626          * @brief checkPostMeridianFlipActions Execute the checks necessary after the mount
0627          * has completed the meridian flip.
0628          * @return true iff the post meridian flip actions are ongoing, false if completed or not necessary
0629          */
0630         bool checkPostMeridianFlipActions();
0631 
0632         /**
0633          * @brief Check if an alignment needs to be executed after completing
0634          * a meridian flip.
0635          * @return
0636          */
0637         bool checkAlignmentAfterFlip();
0638 
0639         /**
0640          * @brief checkGuideDeviationTimeout Handle timeout when no guide deviation has been received.
0641          */
0642         void checkGuideDeviationTimeout();
0643 
0644         /**
0645          * @brief Check if the mount's flip has been completed and start guiding
0646          * if necessary. Starting guiding after the meridian flip works through
0647          * the signal {@see startGuidingAfterFlip()}
0648          * @return true if guiding needs to start but is not running yet
0649          */
0650         bool checkGuidingAfterFlip();
0651 
0652         /**
0653          * @brief processGuidingFailed React when guiding failed.
0654          *
0655          * If aguiding has been started before and stopped, capturing aborts except
0656          * for the case that either
0657          *  - a meridian flip is running
0658          *  - a job is running for non light frames
0659          *  - capturing is either paused or suspended
0660          *  In these case, nothing is done.
0661          */
0662         void processGuidingFailed();
0663 
0664         /**
0665          * @brief Process changes necessary when the focus state changes.
0666          */
0667         void updateFocusState(FocusState state);
0668 
0669         /**
0670          * @brief Check if focusing is running (abbreviating function for focus state
0671          * neither idle nor completed nor aborted).
0672          */
0673         bool checkFocusRunning()
0674         {
0675             return (m_FocusState != FOCUS_IDLE && m_FocusState != FOCUS_COMPLETE && m_FocusState != FOCUS_ABORTED);
0676         }
0677 
0678         /**
0679          * @brief Start focusing if necessary (see {@see RefocusState#checkFocusRequired()}).
0680          * @return TRUE if we need to run focusing, false if not necessary
0681          */
0682         bool startFocusIfRequired();
0683 
0684         /**
0685          * @brief Start adaptive focus if necessary
0686          * @return TRUE if we need adaptive focus, false if not necessary
0687          */
0688 
0689         void updateAdaptiveFocusState(bool success);
0690 
0691         /**
0692          * @brief calculate new HFR threshold based on median value for current selected filter
0693          */
0694         void updateHFRThreshold();
0695 
0696         /**
0697          * @brief get the focus filter for the currently active capture filter
0698          */
0699         QString getFocusFilterName();
0700 
0701         /**
0702          * @brief Slot that listens to guiding deviations reported by the Guide module.
0703          *
0704          * Depending on the current status, it triggers several actions:
0705          * - If there is no active job, it calls {@see m_captureModuleState->checkMeridianFlipReady()}, which may initiate a meridian flip.
0706          * - If guiding has been started after a meridian flip and the deviation is within the expected limits,
0707          *   the meridian flip is regarded as completed by setMeridianFlipStage(MF_NONE) (@see setMeridianFlipStage()).
0708          * - If the deviation is beyond the defined limit, capturing is suspended (@see suspend()) and the
0709          *   #guideDeviationTimer is started.
0710          * - Otherwise, it checks if there has been a job suspended and restarts it, since guiding is within the limits.
0711          */
0712 
0713         void setGuideDeviation(double deviation_rms);
0714 
0715         /**
0716          * @brief addDownloadTime Record a new download time
0717          */
0718         void addDownloadTime(double time);
0719 
0720         /**
0721          * @brief averageDownloadTime Determine the average download time
0722          * @return
0723          */
0724         double averageDownloadTime()
0725         {
0726             return (downloadsCounter == 0 ? 0 : totalDownloadTime / downloadsCounter);
0727         }
0728 
0729         /**
0730          * @brief setDarkFlatExposure Given a dark flat job, find the exposure suitable from it by searching for
0731          * completed flat frames.
0732          * @param job Dark flat job
0733          * @return True if a matching exposure is found and set, false otherwise.
0734          * @warning This only works if the flat frames were captured in the same live session.
0735          * If the flat frames were captured in another session (i.e. Ekos restarted), then all automatic exposure
0736          * calculation results are discarded since Ekos does not save this information to the sequene file.
0737          * Possible solution is to write to a local config file to keep this information persist between sessions.
0738          */
0739         bool setDarkFlatExposure(SequenceJob *job);
0740 
0741         /**
0742          * @brief checkSeqBoundary Determine the next file number sequence.
0743          *        That is, if we have file1.png and file2.png, then the next
0744          *        sequence should be file3.png.
0745          */
0746         void checkSeqBoundary(QUrl sequenceURL);
0747 
0748         /**
0749          * @brief isModelinDSLRInfo Check if the DSLR model is already known
0750          */
0751         bool isModelinDSLRInfo(const QString &model);
0752 
0753         // ////////////////////////////////////////////////////////////////////
0754         // Helper functions
0755         // ////////////////////////////////////////////////////////////////////
0756 
0757         /**
0758          * @brief activeJobID Determine the ID of the currently active job
0759          */
0760         int activeJobID();
0761 
0762         /**
0763          * @brief pPendingJobCount Returns the number of pending uncompleted jobs in the sequence queue.
0764          */
0765         int pendingJobCount();
0766 
0767         /**
0768          * @brief getJobState Returns the job state (Idle, In Progress, Error, Aborted, Complete)
0769          * @param id job number. Job IDs start from 0 to N-1.
0770          */
0771 
0772         QString jobState(int id);
0773 
0774         /**
0775           * @brief jobFilterName Returns the job filter name.
0776           * @param id job number. Job IDs start from 0 to N-1.
0777           */
0778         QString jobFilterName(int id);
0779 
0780         /**
0781          * @param id job number. Job IDs start from 0 to N-1.
0782          * @return Returns the frame type (light, dark, ...) of the job.
0783          */
0784         CCDFrameType jobFrameType(int id);
0785 
0786         /**
0787          *  @brief jobImageProgress Returns The number of images completed capture in the job.
0788          * @param id job number. Job IDs start from 0 to N-1.
0789          */
0790         int jobImageProgress(int id);
0791 
0792         /**
0793          * @param id job number. Job IDs start from 0 to N-1.
0794          * @return Returns the total number of images to capture in the job.
0795          */
0796         int jobImageCount(int id);
0797 
0798         /**
0799          * @param id job number. Job IDs start from 0 to N-1.
0800          * @return Returns the number of seconds left in an exposure operation.
0801          */
0802         double jobExposureProgress(int id);
0803 
0804         /**
0805          * @param id job number. Job IDs start from 0 to N-1.
0806          * @return Returns the total requested exposure duration in the job.
0807          */
0808         double jobExposureDuration(int id);
0809 
0810         /**
0811           * @return Returns the percentage of completed captures in all active jobs
0812           */
0813         double progressPercentage();
0814 
0815         /**
0816          * @return Returns time left in seconds until active job is estimated to be complete.
0817          */
0818         int activeJobRemainingTime();
0819 
0820         /**
0821          * @return Returns overall time left in seconds until all jobs are estimated to be complete
0822          */
0823         int overallRemainingTime();
0824 
0825         /**
0826          * Returns the overall sequence queue status. If there are no jobs pending, it returns "Invalid". If all jobs are idle, it returns "Idle". If all jobs are complete, it returns "Complete". If one or more jobs are aborted
0827          * it returns "Aborted" unless it was temporarily aborted due to guiding deviations, then it would return "Suspended". If one or more jobs have errors, it returns "Error". If any jobs is under progress, returns "Running".
0828          */
0829         QString sequenceQueueStatus();
0830 
0831         /**
0832          * @brief getCalibrationSettings Get Calibration settings
0833          * @return settings as JSON object
0834          */
0835         QJsonObject calibrationSettings();
0836 
0837         /**
0838          * @brief setCalibrationSettings Set Calibration settings
0839          * @param settings as JSON object
0840          */
0841         void setCalibrationSettings(const QJsonObject &settings);
0842 
0843         /**
0844          * @brief hasCapturedFramesMap Check if at least one frame has been recorded
0845          */
0846         bool hasCapturedFramesMap()
0847         {
0848             return m_capturedFramesMap.count() > 0;
0849         }
0850         /**
0851          * @brief addCapturedFrame Record a captured frame
0852          */
0853         void addCapturedFrame(const QString &signature);
0854         /**
0855          * @brief removeCapturedFrameCount Reduce the frame counts for the given signature
0856          */
0857         void removeCapturedFrameCount(const QString &signature, uint16_t count);
0858         /**
0859          * @brief clearCapturedFramesMap Clear the map of captured frames counts
0860          */
0861         void clearCapturedFramesMap()
0862         {
0863             m_capturedFramesMap.clear();
0864         }
0865 
0866         bool isCaptureRunning()
0867         {
0868             return (m_CaptureState != CAPTURE_IDLE && m_CaptureState != CAPTURE_COMPLETE && m_CaptureState != CAPTURE_ABORTED);
0869         }
0870 
0871         ScriptTypes captureScriptType() const
0872         {
0873             return m_CaptureScriptType;
0874         }
0875         void setCaptureScriptType(ScriptTypes value)
0876         {
0877             m_CaptureScriptType = value;
0878         }
0879         double targetADU() const
0880         {
0881             return m_targetADU;
0882         }
0883         void setTargetADU(double value)
0884         {
0885             m_targetADU = value;
0886         }
0887         double targetADUTolerance() const
0888         {
0889             return m_TargetADUTolerance;
0890         }
0891         void setTargetADUTolerance(double value)
0892         {
0893             m_TargetADUTolerance = value;
0894         }
0895         SkyPoint &wallCoord()
0896         {
0897             return m_wallCoord;
0898         }
0899         void setWallCoord(SkyPoint value)
0900         {
0901             m_wallCoord = value;
0902         }
0903         const DoubleRange &exposureRange() const
0904         {
0905             return m_ExposureRange;
0906         }
0907         void setExposureRange(double min, double max)
0908         {
0909             m_ExposureRange.min = min;
0910             m_ExposureRange.max = max;
0911         }
0912 
0913         QMap<ISD::CameraChip *, QVariantMap> &frameSettings()
0914         {
0915             return m_frameSettings;
0916         }
0917         void setFrameSettings(const QMap<ISD::CameraChip *, QVariantMap> &value)
0918         {
0919             m_frameSettings = value;
0920         }
0921 
0922         FlatFieldDuration flatFieldDuration() const
0923         {
0924             return m_flatFieldDuration;
0925         }
0926         void setFlatFieldDuration(FlatFieldDuration value)
0927         {
0928             m_flatFieldDuration = value;
0929         }
0930 
0931         uint32_t calibrationPreAction() const
0932         {
0933             return m_CalibrationPreAction;
0934         }
0935         void setCalibrationPreAction(uint32_t value)
0936         {
0937             m_CalibrationPreAction = value;
0938         }
0939 
0940         QList<QMap<QString, QVariant> > &DSLRInfos()
0941         {
0942             return m_DSLRInfos;
0943         }
0944 
0945     signals:
0946         // controls for capture execution
0947         void captureBusy(bool busy);
0948         void startCapture();
0949         void abortCapture();
0950         void suspendCapture();
0951         void executeActiveJob();
0952         void updatePrepareState(CaptureState state);
0953         void captureStarted(CAPTUREResult rc);
0954         // mount meridian flip status update event
0955         void newMeridianFlipStage(MeridianFlipState::MFStage status);
0956         // meridian flip started
0957         void meridianFlipStarted();
0958         // new guiding deviation measured
0959         void newGuiderDrift(double deviation_rms);
0960         // guiding should be started after a successful meridian flip
0961         void guideAfterMeridianFlip();
0962         // new capture state
0963         void newStatus(Ekos::CaptureState status);
0964         // forward new focus status
0965         void newFocusStatus(FocusState status);
0966         // forward new adaptive focus status
0967         void newAdaptiveFocusStatus(bool success);
0968         // check focusing is necessary for the given HFR
0969         void checkFocus(double hfr);
0970         // run Autofocus
0971         void runAutoFocus(bool);
0972         // reset the focuser to the last known focus position
0973         void resetFocus();
0974         // signal focus module to perform adaptive focus
0975         void adaptiveFocus();
0976         // abort capturing if fast exposure mode is used
0977         void abortFastExposure();
0978         // new HFR focus limit calculated
0979         void newLimitFocusHFR(double hfr);
0980         // Select the filter at the given position
0981         void newFilterPosition(int targetFilterPosition, FilterManager::FilterPolicy policy = FilterManager::ALL_POLICIES);
0982         // capture sequence status changes
0983         void sequenceChanged(const QJsonArray &sequence);
0984         // new log text for the module log window
0985         void newLog(const QString &text);
0986 
0987     private:
0988         // Container for the list of SequenceJobs.
0989         QSharedPointer<SequenceQueue> m_sequenceQueue;
0990         // Currently active sequence job.
0991         SequenceJob *m_activeJob { nullptr };
0992         // pointer to the image data
0993         QSharedPointer<FITSData> m_ImageData;
0994         // CCD Chip frame settings
0995         QMap<ISD::CameraChip *, QVariantMap> m_frameSettings;
0996         // DSLR Infos
0997         QList<QMap<QString, QVariant>> m_DSLRInfos;
0998 
0999         // current filter position
1000         // TODO: check why we have both currentFilterID and this, seems redundant
1001         int m_CurrentFilterPosition { -1 };
1002         // current filter name matching the filter position
1003         QString m_CurrentFilterName { "--" };
1004         // holds the filter name used for focusing or "--" if the current one is used
1005         QString m_CurrentFocusFilterName { "--" };
1006         // Captured Frames Map
1007         CapturedFramesMap m_capturedFramesMap;
1008         // are we in the starting phase of capturing?
1009         bool m_StartingCapture { true };
1010         // Does the camera have a dedicated guiding chip?
1011         bool m_useGuideHead { false };
1012         // Guide Deviation
1013         bool m_GuidingDeviationDetected { false };
1014         // suspend guiding when downloading a captured image
1015         bool m_SuspendGuidingOnDownload { false };
1016         // Guiding spikes
1017         int m_SpikesDetected { 0 };
1018         // Timer for guiding recovery
1019         QTimer m_guideDeviationTimer;
1020         // Timer to start the entire capturing with the delay configured
1021         // for the first capture job that is ready to be executed.
1022         // @see Capture::start().
1023         QTimer m_captureDelayTimer;
1024         // Capture timeout timer
1025         QTimer m_captureTimeout;
1026         uint8_t m_CaptureTimeoutCounter { 0 };
1027         uint8_t m_DeviceRestartCounter { 0 };
1028         // time left of the current exposure
1029         QTime m_imageCountDown;
1030         double m_lastRemainingFrameTimeMS;
1031         // Timer for starting the next capture sequence with delay
1032         // @see Capture::startNextExposure()
1033         QTimer m_seqDelayTimer;
1034         // time left for the current sequence
1035         QTime m_sequenceCountDown;
1036         // timer for updating the download progress
1037         QTimer m_downloadProgressTimer;
1038         // timer measuring the download time
1039         QElapsedTimer m_DownloadTimer;
1040         // sum over all recorded list of download times
1041         double totalDownloadTime {0};
1042         // number of downloaded frames
1043         uint downloadsCounter {0};
1044         // next capture sequence ID
1045         int m_nextSequenceID { 0 };
1046         // how to continue after pausing
1047         ContinueAction m_ContinueAction { CONTINUE_ACTION_NONE };
1048         // name of the observer
1049         QString m_ObserverName;
1050         // ignore already captured files
1051         bool m_ignoreJobProgress { true };
1052         // Fast Exposure
1053         bool m_RememberFastExposure {false};
1054         // Set dirty bit to indicate sequence queue file was modified and needs saving.
1055         bool m_Dirty { false };
1056         // Capturing (incl. preparation actions) is active
1057         bool m_Busy { false };
1058         // preview loop running
1059         bool m_Looping { false };
1060         // script type of the currently running script
1061         ScriptTypes m_CaptureScriptType { SCRIPT_N };
1062         // Flat field automation
1063         double m_TargetADUTolerance { 1000 };
1064         double m_targetADU { 0 };
1065         SkyPoint m_wallCoord;
1066         FlatFieldDuration m_flatFieldDuration { DURATION_MANUAL };
1067         uint32_t m_CalibrationPreAction { ACTION_NONE };
1068         bool m_lightBoxLightEnabled { false };
1069         // Allowed camera exposure times
1070         DoubleRange m_ExposureRange;
1071         // Misc
1072         QJsonArray m_SequenceArray;
1073 
1074         // ////////////////////////////////////////////////////////////////////
1075         // device states
1076         // ////////////////////////////////////////////////////////////////////
1077         CaptureState m_CaptureState { CAPTURE_IDLE };
1078         FocusState m_FocusState { FOCUS_IDLE };
1079         GuideState m_GuideState { GUIDE_IDLE };
1080         IPState m_DitheringState {IPS_IDLE};
1081         AlignState m_AlignState { ALIGN_IDLE };
1082         FilterState m_FilterManagerState { FILTER_IDLE };
1083         LightState m_lightBoxLightState { CAP_LIGHT_UNKNOWN };
1084         CapState m_dustCapState { CAP_UNKNOWN };
1085         ISD::Mount::Status m_scopeState { ISD::Mount::MOUNT_IDLE };
1086         ISD::Mount::PierSide m_pierSide { ISD::Mount::PIER_UNKNOWN };
1087         ISD::ParkStatus m_scopeParkState { ISD::PARK_UNKNOWN };
1088         ISD::Dome::Status m_domeState { ISD::Dome::DOME_IDLE };
1089 
1090         // ////////////////////////////////////////////////////////////////////
1091         // counters
1092         // ////////////////////////////////////////////////////////////////////
1093         // Number of alignment retries
1094         int m_AlignmentRetries { 0 };
1095         // How many images to capture before dithering operation is executed?
1096         uint m_ditherCounter { 0 };
1097 
1098         /* Refocusing */
1099         QSharedPointer<RefocusState> m_refocusState;
1100         /* Meridian Flip */
1101         QSharedPointer<MeridianFlipState> mf_state;
1102 
1103         /**
1104              * @brief Add log message
1105              */
1106         void appendLogText(const QString &message);
1107 
1108 };
1109 
1110 }; // namespace