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