7#include "camerastate.h"
8#include "ekos/manager/meridianflipstate.h"
9#include "ekos/capture/sequencejob.h"
10#include "ekos/capture/sequencequeue.h"
11#include "fitsviewer/fitsdata.h"
13#include "ksnotification.h"
14#include <ekos_capture_debug.h>
16#define GD_TIMER_TIMEOUT 60000
20void CameraState::init()
22 m_sequenceQueue.
reset(
new SequenceQueue());
23 m_refocusState.
reset(
new RefocusState());
24 m_TargetADUTolerance = Options::calibrationADUValueTolerance();
25 connect(m_sequenceQueue.
get(), &SequenceQueue::newLog,
this, &CameraState::newLog);
26 connect(m_refocusState.
get(), &RefocusState::newLog,
this, &CameraState::newLog);
28 getGuideDeviationTimer().
setInterval(GD_TIMER_TIMEOUT);
31 setCalibrationPreAction(Options::calibrationPreActionIndex());
32 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(Options::calibrationFlatDurationIndex()));
33 wallCoord().
setAz(Options::calibrationWallAz());
34 wallCoord().
setAlt(Options::calibrationWallAlt());
35 setTargetADU(Options::calibrationADUValue());
36 setSkyFlat(Options::calibrationSkyFlat());
46 return m_sequenceQueue->allJobs();
49const QUrl &CameraState::sequenceURL()
const
51 return m_sequenceQueue->sequenceURL();
54void CameraState::setSequenceURL(
const QUrl &newSequenceURL)
56 m_sequenceQueue->setSequenceURL(newSequenceURL);
63 if (m_activeJob == value)
67 if (m_activeJob !=
nullptr)
69 disconnect(
this,
nullptr, m_activeJob,
nullptr);
70 disconnect(m_activeJob,
nullptr,
this,
nullptr);
72 m_activeJob->disconnectDeviceAdaptor();
79 if (m_activeJob !=
nullptr)
82 m_activeJob->connectDeviceAdaptor();
84 connect(
this, &CameraState::newGuiderDrift, m_activeJob, &SequenceJob::updateGuiderDrift);
86 connect(m_activeJob, &SequenceJob::prepareState,
this, &CameraState::updatePrepareState);
88 connect(m_activeJob, &SequenceJob::abortCapture,
this, &CameraState::abortCapture);
89 connect(m_activeJob, &SequenceJob::captureStarted,
this, &CameraState::captureStarted);
90 connect(m_activeJob, &SequenceJob::newLog,
this, &CameraState::newLog);
92 m_activeJob->updateDeviceStates();
93 m_activeJob->setAutoFocusReady(getRefocusState()->isAutoFocusReady());
98int CameraState::activeJobID()
100 if (m_activeJob ==
nullptr)
103 for (
int i = 0; i < allJobs().count(); i++)
105 if (m_activeJob == allJobs().at(i))
113void CameraState::initCapturePreparation()
115 setStartingCapture(
false);
118 setIgnoreJobProgress(!hasCapturedFramesMap() && Options::alwaysResetSequenceWhenStarting());
121 if (isGuidingDeviationDetected() ==
false && getCaptureState() != CAPTURE_SUSPENDED)
124 getRefocusState()->startRefocusTimer();
129 if (isGuidingDeviationDetected() ==
false)
131 resetDitherCounter();
132 getRefocusState()->resetInSequenceFocusCounter();
133 getRefocusState()->setAdaptiveFocusDone(
false);
136 setGuidingDeviationDetected(
false);
137 resetSpikesDetected();
139 setCaptureState(CAPTURE_PROGRESS);
142 initPlaceholderPath();
144 if (Options::enforceGuideDeviation() && isGuidingOn() ==
false)
145 emit newLog(
i18n(
"Warning: Guide deviation is selected but autoguide process was not started."));
148void CameraState::setCaptureState(CaptureState value)
150 bool pause_planned =
false;
156 emit resetNonGuidedDither();
161 if (mf_state->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
162 mf_state->updateMeridianFlipStage(MeridianFlipState::MF_READY);
169 emit requestAction(CAPTURE_ACTION_DITHER_REQUEST);
176 if (m_CaptureState != value)
178 qCDebug(KSTARS_EKOS_CAPTURE()) <<
"Capture State changes from" << getCaptureStatusString(
179 m_CaptureState) <<
"to" << getCaptureStatusString(value);
180 m_CaptureState = value;
181 getMeridianFlipState()->setCaptureState(m_CaptureState);
182 emit newStatus(m_CaptureState);
187 emit newStatus(m_CaptureState);
192void CameraState::setGuideState(GuideState state)
194 if (state != m_GuideState)
195 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Guiding state changed from" <<
196 Ekos::getGuideStatusString(m_GuideState)
197 <<
"to" << Ekos::getGuideStatusString(state);
202 case GUIDE_CALIBRATION_SUCCESS:
206 case GUIDE_CALIBRATION_ERROR:
207 processGuidingFailed();
210 case GUIDE_DITHERING_SUCCESS:
211 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Dithering succeeded, capture state" << getCaptureStatusString(
214 appendLogText(
i18n(
"Dithering succeeded."));
215 if (getCaptureState() != CAPTURE_DITHERING)
218 if (Options::guidingSettle() > 0)
221 appendLogText(
i18n(
"Dither complete. Resuming in %1 seconds...", Options::guidingSettle()));
224 setDitheringState(IPS_OK);
229 appendLogText(
i18n(
"Dither complete."));
230 setDitheringState(IPS_OK);
234 case GUIDE_DITHERING_ERROR:
235 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Dithering failed, capture state" << getCaptureStatusString(
237 if (getCaptureState() != CAPTURE_DITHERING)
240 if (Options::guidingSettle() > 0)
243 appendLogText(
i18n(
"Warning: Dithering failed. Resuming in %1 seconds...", Options::guidingSettle()));
247 setDitheringState(IPS_OK);
252 appendLogText(
i18n(
"Warning: Dithering failed."));
254 setDitheringState(IPS_OK);
263 m_GuideState = state;
265 if (m_activeJob !=
nullptr)
266 m_activeJob->setCoreProperty(SequenceJob::SJ_GuiderActive, isActivelyGuiding());
271void CameraState::setCurrentFilterPosition(
int position,
const QString &name,
const QString &focusFilterName)
273 m_CurrentFilterPosition = position;
276 m_CurrentFilterName =
name;
277 m_CurrentFocusFilterName = focusFilterName;
281 m_CurrentFilterName =
"--";
282 m_CurrentFocusFilterName =
"--";
286void CameraState::dustCapStateChanged(ISD::DustCap::Status status)
290 case ISD::DustCap::CAP_ERROR:
291 setDustCapState(CAP_ERROR);
292 emit newLog(
i18n(
"Dust cap error."));
294 case ISD::DustCap::CAP_PARKED:
295 setDustCapState(CAP_PARKED);
296 emit newLog(
i18n(
"Dust cap parked."));
298 case ISD::DustCap::CAP_IDLE:
299 setDustCapState(CAP_IDLE);
300 emit newLog(
i18n(
"Dust cap unparked."));
302 case ISD::DustCap::CAP_UNPARKING:
303 setDustCapState(CAP_UNPARKING);
305 case ISD::DustCap::CAP_PARKING:
306 setDustCapState(CAP_PARKING);
314 if (mf_state.isNull())
315 mf_state.
reset(
new MeridianFlipState());
323 if (! mf_state.isNull())
325 mf_state->disconnect(
this);
326 mf_state->deleteLater();
330 connect(mf_state.data(), &Ekos::MeridianFlipState::newMountMFStatus,
this, &Ekos::CameraState::updateMFMountState,
334void CameraState::setObserverName(
const QString &value)
336 m_ObserverName = value;
337 Options::setDefaultObserver(value);
340void CameraState::setBusy(
bool busy)
343 emit captureBusy(busy);
348bool CameraState::generateFilename(
const QString &extension,
QString *filename)
350 *filename = placeholderPath().generateOutputFilename(
true,
true, nextSequenceID(), extension,
"");
353 if (currentDir.
exists() ==
false)
359 QString oldFilename = *filename;
360 *filename = placeholderPath().repairFilename(*filename);
361 if (*filename != oldFilename)
362 qCWarning(KSTARS_EKOS_CAPTURE) <<
"File over-write detected: changing" << oldFilename <<
"to" << *filename;
364 qCWarning(KSTARS_EKOS_CAPTURE) <<
"File over-write detected for" << oldFilename <<
"but could not correct filename";
367 QFile test_file(*filename);
375void CameraState::decreaseDitherCounter()
377 if (m_ditherCounter > 0)
381void CameraState::resetDitherCounter()
385 value = m_activeJob->getCoreProperty(SequenceJob::SJ_DitherPerJobFrequency).toInt(0);
388 m_ditherCounter = value;
390 m_ditherCounter = Options::ditherFrames();
393bool CameraState::checkDithering()
396 if (m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
399 if ( (Options::ditherEnabled() || Options::ditherNoGuiding())
401 && getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_GUIDING
403 && (getGuideState() == GUIDE_GUIDING || Options::ditherNoGuiding())
405 && (m_activeJob !=
nullptr && m_activeJob->getFrameType() == FRAME_LIGHT)
407 && m_ditherCounter == 0)
410 resetDitherCounter();
412 appendLogText(
i18n(
"Dithering requested..."));
414 setCaptureState(CAPTURE_DITHERING);
415 setDitheringState(IPS_BUSY);
423void CameraState::updateMFMountState(MeridianFlipState::MeridianFlipMountState status)
425 qCDebug(KSTARS_EKOS_CAPTURE) <<
"updateMFMountState: " << MeridianFlipState::meridianFlipStatusString(status);
429 case MeridianFlipState::MOUNT_FLIP_NONE:
431 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
432 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
435 case MeridianFlipState::MOUNT_FLIP_PLANNED:
436 if (getMeridianFlipState()->getMeridianFlipStage() > MeridianFlipState::MF_REQUESTED)
439 qCritical(KSTARS_EKOS_CAPTURE) <<
"Accepting meridian flip request while being in stage " <<
440 getMeridianFlipState()->getMeridianFlipStage();
444 getMeridianFlipState()->setResumeGuidingAfterFlip(isGuidingOn());
447 updateMeridianFlipStage(MeridianFlipState::MF_REQUESTED);
449 if (m_CaptureState == CAPTURE_IDLE || m_CaptureState == CAPTURE_ABORTED
450 || m_CaptureState == CAPTURE_COMPLETE || m_CaptureState == CAPTURE_PAUSED)
451 getMeridianFlipState()->updateMFMountState(MeridianFlipState::MOUNT_FLIP_ACCEPTED);
455 case MeridianFlipState::MOUNT_FLIP_RUNNING:
456 updateMeridianFlipStage(MeridianFlipState::MF_INITIATED);
457 setCaptureState(CAPTURE_MERIDIAN_FLIP);
460 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
461 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
470void CameraState::updateMeridianFlipStage(
const MeridianFlipState::MFStage &stage)
473 getMeridianFlipState()->updateMeridianFlipStage(stage);
478 case MeridianFlipState::MF_READY:
481 case MeridianFlipState::MF_INITIATED:
482 emit meridianFlipStarted();
485 case MeridianFlipState::MF_COMPLETED:
488 if (getRefocusState()->isInSequenceFocus())
490 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Resetting HFR Check counter after meridian flip.";
492 getRefocusState()->setInSequenceFocusCounter(0);
496 if ( Options::ditherEnabled() || Options::ditherNoGuiding())
497 resetDitherCounter();
500 if (Options::refocusAfterMeridianFlip() ==
true)
501 getRefocusState()->setRefocusAfterMeridianFlip(
true);
503 if (hasDome && (m_domeState == ISD::Dome::DOME_MOVING_CW || m_domeState == ISD::Dome::DOME_MOVING_CCW))
506 KSNotification::event(
QLatin1String(
"MeridianFlipCompleted"),
i18n(
"Meridian flip is successfully completed"),
507 KSNotification::Capture);
509 getMeridianFlipState()->processFlipCompleted();
512 setCaptureState(m_ContinueAction == CAPTURE_CONTINUE_ACTION_NONE ? CAPTURE_IDLE : CAPTURE_PAUSED);
519 emit newMeridianFlipStage(stage);
522bool CameraState::checkMeridianFlipActive()
524 return (getMeridianFlipState()->checkMeridianFlipRunning() ||
525 checkPostMeridianFlipActions() ||
526 checkMeridianFlipReady());
529bool CameraState::checkMeridianFlipReady()
531 if (hasTelescope ==
false)
536 if (m_activeJob && m_activeJob->getFrameType() == FRAME_FLAT
537 && m_activeJob->getCalibrationPreAction() & CAPTURE_PREACTION_WALL)
540 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED)
547 if (m_refocusState->isInSequenceFocus() ||
548 (Options::enforceRefocusEveryN() && m_refocusState->getRefocusEveryNTimerElapsedSec() > 0))
549 emit resetFocusFrame();
552 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
553 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_READY);
559bool CameraState::checkPostMeridianFlipActions()
562 if (m_CaptureState == CAPTURE_ALIGNING || checkAlignmentAfterFlip())
567 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED && m_GuideState != GUIDE_GUIDING
568 && checkGuidingAfterFlip())
574 if (m_CaptureState == CAPTURE_CALIBRATING
575 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
577 if (Options::enforceGuideDeviation() || Options::enforceStartGuiderDrift())
580 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
587bool CameraState::checkGuidingAfterFlip()
590 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
593 if (getMeridianFlipState()->resumeGuidingAfterFlip() ==
false)
595 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
600 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED
601 && getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_GUIDING)
603 appendLogText(
i18n(
"Performing post flip re-calibration and guiding..."));
605 setCaptureState(CAPTURE_CALIBRATING);
607 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_GUIDING);
608 emit guideAfterMeridianFlip();
611 else if (m_CaptureState == CAPTURE_CALIBRATING)
613 if (getGuideState() == GUIDE_CALIBRATION_ERROR || getGuideState() == GUIDE_ABORTED)
616 appendLogText(
i18n(
"Post meridian flip calibration error. Restarting..."));
617 emit guideAfterMeridianFlip();
620 else if (getGuideState() != GUIDE_GUIDING)
632void CameraState::processGuidingFailed()
634 if (m_FocusState > FOCUS_PROGRESS)
636 appendLogText(
i18n(
"Autoguiding stopped. Waiting for autofocus to finish..."));
639 else if (isGuidingOn()
640 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_NONE &&
642 ((m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT) ||
643 m_CaptureState == CAPTURE_SUSPENDED || m_CaptureState == CAPTURE_PAUSED))
645 appendLogText(
i18n(
"Autoguiding stopped. Aborting..."));
648 else if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
650 if (increaseAlignmentRetries() >= 3)
652 appendLogText(
i18n(
"Post meridian flip calibration error. Aborting..."));
658void CameraState::updateAdaptiveFocusState(
bool success)
660 m_refocusState->setAdaptiveFocusDone(
true);
664 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus completed successfully";
666 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus failed";
668 m_refocusState->setAutoFocusReady(
true);
670 if (m_activeJob !=
nullptr)
671 m_activeJob->setAutoFocusReady(
true);
673 setFocusState(FOCUS_COMPLETE);
674 emit newLog(
i18n(success ?
"Adaptive focus complete." :
"Adaptive focus failed. Continuing..."));
677void CameraState::updateFocusState(FocusState state)
679 if (state != m_FocusState)
680 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Focus State changed from" <<
681 Ekos::getFocusStatusString(m_FocusState) <<
682 "to" << Ekos::getFocusStatusString(state);
683 setFocusState(state);
686 if (getMeridianFlipState()->checkMeridianFlipRunning())
693 if (!(getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_INITIATED))
697 emit newFocusStatus(state);
698 appendLogText(
i18n(
"Autofocus failed. Aborting exposure..."));
704 m_refocusState->setAutoFocusReady(
true);
706 if (m_activeJob !=
nullptr)
707 m_activeJob->setAutoFocusReady(
true);
709 if (m_refocusState->getFocusHFRInAutofocus())
710 m_refocusState->startRefocusTimer(
true);
713 if (Options::hFRCheckAlgorithm() == HFR_CHECK_MEDIAN_MEASURE ||
714 (m_refocusState->getFocusHFRInAutofocus() && Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS))
716 m_refocusState->addHFRValue(getFocusFilterName());
717 updateHFRThreshold();
719 emit newFocusStatus(state);
725 if (m_activeJob !=
nullptr)
730 if (state == FOCUS_COMPLETE && Options::guidingSettle() > 0 && Options::focusSuspendGuiding()
731 && m_GuideState == GUIDE_GUIDING)
734 appendLogText(
i18n(
"Focus complete. Resuming in %1 seconds...", Options::guidingSettle()));
737 if (m_activeJob !=
nullptr)
738 m_activeJob->setFocusStatus(state);
742 m_activeJob->setFocusStatus(state);
746AutofocusReason CameraState::getAFReason(RefocusState::RefocusReason state,
QString &reasonInfo)
748 AutofocusReason afReason;
752 case RefocusState::REFOCUS_USER_REQUEST:
753 afReason = AutofocusReason::FOCUS_USER_REQUEST;
755 case RefocusState::REFOCUS_TEMPERATURE:
756 afReason = AutofocusReason::FOCUS_TEMPERATURE;
757 reasonInfo =
i18n(
"Limit: %1 °C",
QString::number(Options::maxFocusTemperatureDelta(),
'f', 2));
759 case RefocusState::REFOCUS_TIME_ELAPSED:
760 afReason = AutofocusReason::FOCUS_TIME;
761 reasonInfo =
i18n(
"Limit: %1 mins", Options::refocusEveryN());
763 case RefocusState::REFOCUS_POST_MF:
764 afReason = AutofocusReason::FOCUS_MERIDIAN_FLIP;
767 afReason = AutofocusReason::FOCUS_NONE;
772bool CameraState::startFocusIfRequired()
778 if (m_activeJob ==
nullptr || m_activeJob->getFrameType() != FRAME_LIGHT
779 || m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
782 RefocusState::RefocusReason reason = m_refocusState->checkFocusRequired();
785 if (reason == RefocusState::REFOCUS_NONE)
789 m_refocusState->setRefocusAfterMeridianFlip(
false);
795 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_NONE)
797 int targetFilterPosition = m_activeJob->getTargetFilter();
798 if (targetFilterPosition > 0 && targetFilterPosition != getCurrentFilterPosition())
799 emit newFilterPosition(targetFilterPosition);
802 emit abortFastExposure();
803 updateFocusState(FOCUS_PROGRESS);
805 AutofocusReason afReason;
809 case RefocusState::REFOCUS_HFR:
810 m_refocusState->resetInSequenceFocusCounter();
811 emit checkFocus(Options::hFRDeviation());
812 qCDebug(KSTARS_EKOS_CAPTURE) <<
"In-sequence focusing started...";
814 case RefocusState::REFOCUS_ADAPTIVE:
815 m_refocusState->setAdaptiveFocusDone(
true);
816 emit adaptiveFocus();
817 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus started...";
819 case RefocusState::REFOCUS_USER_REQUEST:
820 case RefocusState::REFOCUS_TEMPERATURE:
821 case RefocusState::REFOCUS_TIME_ELAPSED:
822 case RefocusState::REFOCUS_POST_MF:
824 if (m_refocusState->getRefocusEveryNTimerElapsedSec() >= 1800)
825 emit resetFocusFrame();
828 afReason = getAFReason(reason, reasonInfo);
829 emit runAutoFocus(afReason, reasonInfo);
831 m_refocusState->resetInSequenceFocusCounter();
832 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Refocusing started...";
839 setCaptureState(CAPTURE_FOCUSING);
843void CameraState::updateHFRThreshold()
846 if (Options::hFRCheckAlgorithm() == HFR_CHECK_FIXED)
849 QString finalFilter = getFocusFilterName();
850 QList<double> filterHFRList = m_refocusState->getHFRMap()[finalFilter];
853 if (filterHFRList.
empty())
857 if (Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS)
858 value = filterHFRList.
last();
861 int count = filterHFRList.
size();
863 value = (count % 2) ? filterHFRList[count / 2] : (filterHFRList[count / 2 - 1] + filterHFRList[count / 2]) / 2.0;
865 value = filterHFRList[0];
867 value += value * (Options::hFRThresholdPercentage() / 100.0);
868 Options::setHFRDeviation(value);
869 emit newLimitFocusHFR(value);
872QString CameraState::getFocusFilterName()
875 if (m_CurrentFilterPosition > 0)
880 finalFilter = (m_CurrentFocusFilterName ==
"--" ? m_CurrentFilterName : m_CurrentFocusFilterName);
887bool CameraState::checkAlignmentAfterFlip()
890 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
892 qCDebug(KSTARS_EKOS_CAPTURE) <<
"checkAlignmentAfterFlip too early, meridian flip stage =" <<
893 getMeridianFlipState()->getMeridianFlipStage();
897 if (getMeridianFlipState()->resumeAlignmentAfterFlip() ==
false)
899 qCDebug(KSTARS_EKOS_CAPTURE) <<
"No alignment after flip required.";
904 if (m_CaptureState < CAPTURE_ALIGNING)
906 appendLogText(
i18n(
"Performing post flip re-alignment..."));
908 resetAlignmentRetries();
909 setCaptureState(CAPTURE_ALIGNING);
911 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_ALIGNING);
919void CameraState::checkGuideDeviationTimeout()
921 if (m_activeJob && m_activeJob->getStatus() == JOB_ABORTED
922 && isGuidingDeviationDetected())
924 appendLogText(
i18n(
"Guide module timed out."));
925 setGuidingDeviationDetected(
false);
928 if (m_CaptureState == CAPTURE_SUSPENDED)
930 setCaptureState(CAPTURE_ABORTED);
935void CameraState::setGuideDeviation(
double deviation_rms)
938 emit newGuiderDrift(deviation_rms);
943 if (m_activeJob ==
nullptr && checkMeridianFlipReady())
947 if (m_CaptureState == CAPTURE_PROGRESS &&
948 getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED &&
949 getMeridianFlipState()->checkMeridianFlipRunning() ==
false)
952 if (Options::enforceStartGuiderDrift() ==
false || deviation_rms < Options::startGuideDeviation())
954 setCaptureState(CAPTURE_CALIBRATING);
955 if (Options::enforceStartGuiderDrift())
956 appendLogText(
i18n(
"Initial guiding deviation %1 below limit value of %2 arcsecs",
957 deviationText, Options::startGuideDeviation()));
958 setGuidingDeviationDetected(
false);
959 setStartingCapture(
false);
964 if (isGuidingDeviationDetected() ==
false)
965 appendLogText(
i18n(
"Initial guiding deviation %1 exceeded limit value of %2 arcsecs",
966 deviationText, Options::startGuideDeviation()));
968 setGuidingDeviationDetected(
true);
972 if (checkMeridianFlipReady())
982 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
985 if (Options::enforceGuideDeviation() ==
false || deviation_rms < Options::guideDeviation())
987 appendLogText(
i18n(
"Post meridian flip calibration completed successfully."));
989 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
995 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT
996 && isStartingCapture() && Options::enforceStartGuiderDrift())
998 setStartingCapture(
false);
999 if (deviation_rms > Options::startGuideDeviation())
1001 appendLogText(
i18n(
"Guiding deviation at capture startup %1 exceeded limit %2 arcsecs.",
1002 deviationText, Options::startGuideDeviation()));
1003 emit suspendCapture();
1004 setGuidingDeviationDetected(
true);
1008 if (checkMeridianFlipReady())
1009 emit startCapture();
1011 getGuideDeviationTimer().start();
1015 appendLogText(
i18n(
"Guiding deviation at capture startup %1 below limit value of %2 arcsecs",
1016 deviationText, Options::startGuideDeviation()));
1019 if (m_CaptureState != CAPTURE_SUSPENDED)
1023 if ((Options::enforceGuideDeviation() ==
false)
1025 (m_activeJob && (m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW ||
1026 m_activeJob->getExposeLeft() == 0.0 ||
1027 m_activeJob->getFrameType() != FRAME_LIGHT)))
1032 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT)
1034 if (deviation_rms <= Options::guideDeviation())
1035 resetSpikesDetected();
1039 if (increaseSpikesDetected() < Options::guideDeviationReps())
1042 appendLogText(
i18n(
"Guiding deviation %1 exceeded limit value of %2 arcsecs for %4 consecutive samples, "
1043 "suspending exposure and waiting for guider up to %3 seconds.",
1044 deviationText, Options::guideDeviation(),
1045 QString(
"%L1").arg(getGuideDeviationTimer().interval() / 1000.0, 0,
'f', 3),
1046 Options::guideDeviationReps()));
1048 emit suspendCapture();
1050 resetSpikesDetected();
1051 setGuidingDeviationDetected(
true);
1055 if (checkMeridianFlipReady())
1056 emit startCapture();
1058 getGuideDeviationTimer().start();
1066 for(
auto &job : allJobs())
1068 if (job->getStatus() == JOB_ABORTED)
1075 if (abortedJob !=
nullptr && isGuidingDeviationDetected())
1077 if (deviation_rms <= Options::startGuideDeviation())
1079 getGuideDeviationTimer().stop();
1082 if (! getCaptureDelayTimer().isActive())
1085 if (m_CaptureState == CAPTURE_SUSPENDED)
1087 const int seqDelay = abortedJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1089 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1090 "resuming exposure.",
1091 deviationText, Options::startGuideDeviation()));
1093 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1094 "resuming exposure in %3 seconds.",
1095 deviationText, Options::startGuideDeviation(), seqDelay / 1000.0));
1097 emit startCapture();
1105 if (getCaptureDelayTimer().isActive())
1106 getCaptureDelayTimer().stop();
1108 appendLogText(
i18n(
"Guiding deviation %1 is still higher than limit value of %2 arcsecs.",
1109 deviationText, Options::startGuideDeviation()));
1114void CameraState::addDownloadTime(
double time)
1116 totalDownloadTime += time;
1120int CameraState::pendingJobCount()
1122 int completedJobs = 0;
1126 if (job->getStatus() == JOB_DONE)
1130 return (allJobs().count() - completedJobs);
1134QString CameraState::jobState(
int id)
1136 if (
id < allJobs().count())
1139 return job->getStatusString();
1146QString CameraState::jobFilterName(
int id)
1148 if (
id < allJobs().count())
1151 return job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1158CCDFrameType CameraState::jobFrameType(
int id)
1160 if (
id < allJobs().count())
1163 return job->getFrameType();
1169int CameraState::jobImageProgress(
int id)
1171 if (
id < allJobs().count())
1174 return job->getCompleted();
1180int CameraState::jobImageCount(
int id)
1182 if (
id < allJobs().count())
1185 return job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1191double CameraState::jobExposureProgress(
int id)
1193 if (
id < allJobs().count())
1196 return job->getExposeLeft();
1202double CameraState::jobExposureDuration(
int id)
1204 if (
id < allJobs().count())
1207 return job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1213double CameraState::progressPercentage()
1215 int totalImageCount = 0;
1216 int totalImageCompleted = 0;
1220 totalImageCount += job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1221 totalImageCompleted += job->getCompleted();
1224 if (totalImageCount != 0)
1225 return ((
static_cast<double>(totalImageCompleted) / totalImageCount) * 100.0);
1230bool CameraState::isActiveJobPreview()
1232 return m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW;
1235int CameraState::activeJobRemainingTime()
1237 if (m_activeJob ==
nullptr)
1240 return m_activeJob->getJobRemainingTime(averageDownloadTime());
1243int CameraState::overallRemainingTime()
1246 double estimatedDownloadTime = averageDownloadTime();
1249 remaining += job->getJobRemainingTime(estimatedDownloadTime);
1254QString CameraState::sequenceQueueStatus()
1256 if (allJobs().count() == 0)
1262 int idle = 0,
error = 0, complete = 0, aborted = 0, running = 0;
1266 switch (job->getStatus())
1291 if (m_CaptureState == CAPTURE_SUSPENDED)
1300 if (idle == allJobs().count())
1303 if (complete == allJobs().count())
1313 {
"preAction",
static_cast<int>(calibrationPreAction())},
1314 {
"duration", flatFieldDuration()},
1315 {
"az", wallCoord().az().Degrees()},
1316 {
"al", wallCoord().alt().Degrees()},
1317 {
"adu", targetADU()},
1318 {
"tolerance", targetADUTolerance()},
1319 {
"skyflat", skyFlat()},
1325void CameraState::setCalibrationSettings(
const QJsonObject &settings)
1327 const int preAction = settings[
"preAction"].toInt(calibrationPreAction());
1328 const int duration = settings[
"duration"].toInt(flatFieldDuration());
1329 const double az = settings[
"az"].toDouble(wallCoord().az().Degrees());
1330 const double al = settings[
"al"].toDouble(wallCoord().alt().Degrees());
1331 const int adu = settings[
"adu"].toInt(
static_cast<int>(std::round(targetADU())));
1332 const int tolerance = settings[
"tolerance"].toInt(
static_cast<int>(std::round(targetADUTolerance())));
1333 const int skyflat = settings[
"skyflat"].toBool();
1335 setCalibrationPreAction(
static_cast<CalibrationPreActions
>(preAction));
1336 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(duration));
1337 wallCoord().setAz(az);
1338 wallCoord().setAlt(al);
1340 setTargetADUTolerance(tolerance);
1341 setSkyFlat(skyflat);
1344bool CameraState::setDarkFlatExposure(
SequenceJob *job)
1346 const auto darkFlatFilter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1347 const auto darkFlatBinning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1348 const auto darkFlatADU = job->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1350 for (
auto &oneJob : allJobs())
1352 if (oneJob->getFrameType() != FRAME_FLAT)
1355 const auto filter = oneJob->getCoreProperty(SequenceJob::SJ_Filter).toString();
1358 if (!darkFlatFilter.isEmpty() && darkFlatFilter != filter)
1362 const auto binning = oneJob->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1363 if (darkFlatBinning != binning)
1367 const auto adu = oneJob->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1368 if (job->getFlatFieldDuration() == DURATION_ADU)
1370 if (darkFlatADU != adu)
1375 job->setCoreProperty(SequenceJob::SJ_Exposure, oneJob->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
1382void CameraState::checkSeqBoundary()
1385 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1388 setNextSequenceID(placeholderPath().checkSeqBoundary(*getActiveJob()));
1391bool CameraState::isModelinDSLRInfo(
const QString &model)
1395 return (oneDSLRInfo[
"Model"] == model);
1398 return (pos != m_DSLRInfos.end());
1401void CameraState::setCapturedFramesCount(
const QString &signature, uint16_t count)
1403 m_capturedFramesMap[signature] = count;
1404 qCDebug(KSTARS_EKOS_CAPTURE) <<
1405 QString(
"Client module indicates that storage for '%1' has already %2 captures processed.").
arg(signature).
arg(count);
1407 setIgnoreJobProgress(
false);
1410void CameraState::changeSequenceValue(
int index,
QString key,
QString value)
1413 QJsonObject oneSequence = seqArray[index].toObject();
1414 oneSequence[key] = value;
1415 seqArray.
replace(index, oneSequence);
1416 setSequence(seqArray);
1417 emit sequenceChanged(seqArray);
1420void CameraState::addCapturedFrame(
const QString &signature)
1422 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1423 if (m_capturedFramesMap.end() != frame_item)
1424 frame_item.value()++;
1425 else m_capturedFramesMap[signature] = 1;
1428void CameraState::removeCapturedFrameCount(
const QString &signature, uint16_t count)
1430 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1431 if (m_capturedFramesMap.end() != frame_item)
1433 if (frame_item.value() <= count)
1435 m_capturedFramesMap.remove(signature);
1438 frame_item.value() = frame_item.value() - count;
1442void CameraState::appendLogText(
const QString &message)
1444 qCInfo(KSTARS_EKOS_CAPTURE()) << message;
1445 emit newLog(message);
1448bool CameraState::isGuidingOn()
1451 if (Options::ditherNoGuiding())
1454 return (m_GuideState == GUIDE_GUIDING ||
1455 m_GuideState == GUIDE_CALIBRATING ||
1456 m_GuideState == GUIDE_CALIBRATION_SUCCESS ||
1457 m_GuideState == GUIDE_DARK ||
1458 m_GuideState == GUIDE_SUBFRAME ||
1459 m_GuideState == GUIDE_STAR_SELECT ||
1460 m_GuideState == GUIDE_REACQUIRE ||
1461 m_GuideState == GUIDE_DITHERING ||
1462 m_GuideState == GUIDE_DITHERING_SUCCESS ||
1463 m_GuideState == GUIDE_DITHERING_ERROR ||
1464 m_GuideState == GUIDE_DITHERING_SETTLE ||
1465 m_GuideState == GUIDE_SUSPENDED
1469bool CameraState::isActivelyGuiding()
1471 return isGuidingOn() && (m_GuideState == GUIDE_GUIDING);
1474void CameraState::setAlignState(AlignState value)
1476 if (value != m_AlignState)
1477 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Align State changed from" << Ekos::getAlignStatusString(
1478 m_AlignState) <<
"to" << Ekos::getAlignStatusString(value);
1479 m_AlignState = value;
1481 getMeridianFlipState()->setResumeAlignmentAfterFlip(
true);
1486 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1488 appendLogText(
i18n(
"Post flip re-alignment completed successfully."));
1489 resetAlignmentRetries();
1491 if (checkGuidingAfterFlip() ==
false)
1494 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
1495 setCaptureState(CAPTURE_WAITING);
1503 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1505 if (increaseAlignmentRetries() >= 3)
1507 appendLogText(
i18n(
"Post-flip alignment failed."));
1508 emit abortCapture();
1512 appendLogText(
i18n(
"Post-flip alignment failed. Retrying..."));
1514 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
1524void CameraState::setPrepareComplete(
bool success)
1529 setCaptureState(CAPTURE_PROGRESS);
1530 emit executeActiveJob();
1534 qWarning(KSTARS_EKOS_CAPTURE) <<
"Capture preparation failed, aborting.";
1535 setCaptureState(CAPTURE_ABORTED);
1536 emit abortCapture();
Sequence Job is a container for the details required to capture a series of images.
void setAlt(dms alt)
Sets Alt, the Altitude.
void setAz(dms az)
Sets Az, the Azimuth.
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_ABORTED
Alignment aborted by user or agent.
@ ALIGN_COMPLETE
Alignment successfully completed.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString name(StandardAction id)
bool exists() const const
bool mkpath(const QString &dirPath) const const
QString path() const const
bool exists() const const
void replace(qsizetype i, const QJsonValue &value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)
QString toLocalFile() const const