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
22 m_sequenceQueue.reset(
new SequenceQueue());
23 m_refocusState.reset(
new RefocusState());
24 m_TargetADUTolerance = Options::calibrationADUValueTolerance();
25 connect(m_refocusState.get(), &RefocusState::newLog,
this, &CameraState::newLog);
27 getGuideDeviationTimer().setInterval(GD_TIMER_TIMEOUT);
30 setCalibrationPreAction(Options::calibrationPreActionIndex());
31 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(Options::calibrationFlatDurationIndex()));
32 wallCoord().setAz(Options::calibrationWallAz());
33 wallCoord().setAlt(Options::calibrationWallAlt());
34 setTargetADU(Options::calibrationADUValue());
35 setSkyFlat(Options::calibrationSkyFlat());
40 return m_sequenceQueue->allJobs();
43const QUrl &CameraState::sequenceURL()
const
45 return m_sequenceQueue->sequenceURL();
48void CameraState::setSequenceURL(
const QUrl &newSequenceURL)
50 m_sequenceQueue->setSequenceURL(newSequenceURL);
51 placeholderPath().setSeqFilename(newSequenceURL.
toLocalFile());
57 if (m_activeJob == value)
61 if (m_activeJob !=
nullptr)
63 disconnect(
this,
nullptr, m_activeJob,
nullptr);
64 disconnect(m_activeJob,
nullptr,
this,
nullptr);
66 m_activeJob->disconnectDeviceAdaptor();
73 if (m_activeJob !=
nullptr)
76 m_activeJob->connectDeviceAdaptor();
78 connect(
this, &CameraState::newGuiderDrift, m_activeJob, &SequenceJob::updateGuiderDrift);
80 connect(m_activeJob, &SequenceJob::prepareState,
this, &CameraState::updatePrepareState);
81 connect(m_activeJob, &SequenceJob::prepareComplete,
this, [
this](
bool success)
85 setCaptureState(CAPTURE_PROGRESS);
86 emit executeActiveJob();
90 qWarning(KSTARS_EKOS_CAPTURE) <<
"Capture preparation failed, aborting.";
91 setCaptureState(CAPTURE_ABORTED);
95 connect(m_activeJob, &SequenceJob::abortCapture,
this, &CameraState::abortCapture);
96 connect(m_activeJob, &SequenceJob::captureStarted,
this, &CameraState::captureStarted);
97 connect(m_activeJob, &SequenceJob::newLog,
this, &CameraState::newLog);
99 m_activeJob->updateDeviceStates();
100 m_activeJob->setAutoFocusReady(getRefocusState()->isAutoFocusReady());
105int CameraState::activeJobID()
107 if (m_activeJob ==
nullptr)
110 for (
int i = 0; i < allJobs().count(); i++)
112 if (m_activeJob == allJobs().at(i))
120void CameraState::initCapturePreparation()
122 setStartingCapture(
false);
125 setIgnoreJobProgress(!hasCapturedFramesMap() && Options::alwaysResetSequenceWhenStarting());
128 if (isGuidingDeviationDetected() ==
false && getCaptureState() != CAPTURE_SUSPENDED)
131 getRefocusState()->startRefocusTimer();
136 if (isGuidingDeviationDetected() ==
false)
138 resetDitherCounter();
139 getRefocusState()->resetInSequenceFocusCounter();
140 getRefocusState()->setAdaptiveFocusDone(
false);
143 setGuidingDeviationDetected(
false);
144 resetSpikesDetected();
146 setCaptureState(CAPTURE_PROGRESS);
149 initPlaceholderPath();
151 if (Options::enforceGuideDeviation() && isGuidingOn() ==
false)
152 emit newLog(
i18n(
"Warning: Guide deviation is selected but autoguide process was not started."));
155void CameraState::setCaptureState(CaptureState value)
157 bool pause_planned =
false;
166 if (mf_state->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
167 mf_state->updateMeridianFlipStage(MeridianFlipState::MF_READY);
179 if (m_CaptureState != value)
181 qCDebug(KSTARS_EKOS_CAPTURE()) <<
"Capture State changes from" << getCaptureStatusString(
182 m_CaptureState) <<
"to" << getCaptureStatusString(value);
183 m_CaptureState = value;
184 getMeridianFlipState()->setCaptureState(m_CaptureState);
185 emit newStatus(m_CaptureState);
190 emit newStatus(m_CaptureState);
195void CameraState::setGuideState(GuideState state)
197 if (state != m_GuideState)
198 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Guiding state changed from" <<
199 Ekos::getGuideStatusString(m_GuideState)
200 <<
"to" << Ekos::getGuideStatusString(state);
205 case GUIDE_CALIBRATION_SUCCESS:
209 case GUIDE_CALIBRATION_ERROR:
210 processGuidingFailed();
213 case GUIDE_DITHERING_SUCCESS:
214 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Dithering succeeded, capture state" << getCaptureStatusString(
217 appendLogText(
i18n(
"Dithering succeeded."));
218 if (getCaptureState() != CAPTURE_DITHERING)
221 if (Options::guidingSettle() > 0)
224 appendLogText(
i18n(
"Dither complete. Resuming in %1 seconds...", Options::guidingSettle()));
227 setDitheringState(IPS_OK);
232 appendLogText(
i18n(
"Dither complete."));
233 setDitheringState(IPS_OK);
237 case GUIDE_DITHERING_ERROR:
238 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Dithering failed, capture state" << getCaptureStatusString(
240 if (getCaptureState() != CAPTURE_DITHERING)
243 if (Options::guidingSettle() > 0)
246 appendLogText(
i18n(
"Warning: Dithering failed. Resuming in %1 seconds...", Options::guidingSettle()));
250 setDitheringState(IPS_OK);
255 appendLogText(
i18n(
"Warning: Dithering failed."));
257 setDitheringState(IPS_OK);
266 m_GuideState = state;
268 if (m_activeJob !=
nullptr)
269 m_activeJob->setCoreProperty(SequenceJob::SJ_GuiderActive, isActivelyGuiding());
274void CameraState::setCurrentFilterPosition(
int position,
const QString &name,
const QString &focusFilterName)
276 m_CurrentFilterPosition = position;
279 m_CurrentFilterName =
name;
280 m_CurrentFocusFilterName = focusFilterName;
284 m_CurrentFilterName =
"--";
285 m_CurrentFocusFilterName =
"--";
289void CameraState::dustCapStateChanged(ISD::DustCap::Status status)
293 case ISD::DustCap::CAP_ERROR:
294 setDustCapState(CameraState::CAP_ERROR);
295 emit newLog(
i18n(
"Dust cap error."));
297 case ISD::DustCap::CAP_PARKED:
298 setDustCapState(CameraState::CAP_PARKED);
299 emit newLog(
i18n(
"Dust cap parked."));
301 case ISD::DustCap::CAP_IDLE:
302 setDustCapState(CameraState::CAP_IDLE);
303 emit newLog(
i18n(
"Dust cap unparked."));
305 case ISD::DustCap::CAP_UNPARKING:
306 setDustCapState(CameraState::CAP_UNPARKING);
308 case ISD::DustCap::CAP_PARKING:
309 setDustCapState(CameraState::CAP_PARKING);
317 if (mf_state.isNull())
318 mf_state.
reset(
new MeridianFlipState());
326 if (! mf_state.isNull())
328 mf_state->disconnect(
this);
329 mf_state->deleteLater();
333 connect(mf_state.data(), &Ekos::MeridianFlipState::newMountMFStatus,
this, &Ekos::CameraState::updateMFMountState,
337void CameraState::setObserverName(
const QString &value)
339 m_ObserverName = value;
340 Options::setDefaultObserver(value);
343void CameraState::setBusy(
bool busy)
346 emit captureBusy(busy);
351bool CameraState::generateFilename(
const QString &extension,
QString *filename)
353 *filename = placeholderPath().generateOutputFilename(
true,
true, nextSequenceID(), extension,
"");
356 if (currentDir.
exists() ==
false)
362 QString oldFilename = *filename;
363 *filename = placeholderPath().repairFilename(*filename);
364 if (filename != oldFilename)
365 qCWarning(KSTARS_EKOS_CAPTURE) <<
"File over-write detected: changing" << oldFilename <<
"to" << *filename;
367 qCWarning(KSTARS_EKOS_CAPTURE) <<
"File over-write detected for" << oldFilename <<
"but could not correct filename";
370 QFile test_file(*filename);
378void CameraState::decreaseDitherCounter()
380 if (m_ditherCounter > 0)
384void CameraState::resetDitherCounter()
388 value = m_activeJob->getCoreProperty(SequenceJob::SJ_DitherPerJobFrequency).toInt(0);
391 m_ditherCounter = value;
393 m_ditherCounter = Options::ditherFrames();
396bool CameraState::checkDithering()
399 if (m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
402 if ( (Options::ditherEnabled() || Options::ditherNoGuiding())
404 && getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_GUIDING
406 && (getGuideState() == GUIDE_GUIDING || Options::ditherNoGuiding())
408 && (m_activeJob !=
nullptr && m_activeJob->getFrameType() == FRAME_LIGHT)
410 && m_ditherCounter == 0)
413 resetDitherCounter();
415 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Dithering...";
416 appendLogText(
i18n(
"Dithering..."));
418 setCaptureState(CAPTURE_DITHERING);
419 setDitheringState(IPS_BUSY);
427void CameraState::updateMFMountState(MeridianFlipState::MeridianFlipMountState status)
429 qCDebug(KSTARS_EKOS_CAPTURE) <<
"updateMFMountState: " << MeridianFlipState::meridianFlipStatusString(status);
433 case MeridianFlipState::MOUNT_FLIP_NONE:
435 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
436 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
439 case MeridianFlipState::MOUNT_FLIP_PLANNED:
440 if (getMeridianFlipState()->getMeridianFlipStage() > MeridianFlipState::MF_REQUESTED)
443 qCritical(KSTARS_EKOS_CAPTURE) <<
"Accepting meridian flip request while being in stage " <<
444 getMeridianFlipState()->getMeridianFlipStage();
448 getMeridianFlipState()->setResumeGuidingAfterFlip(isGuidingOn());
451 updateMeridianFlipStage(MeridianFlipState::MF_REQUESTED);
453 if (m_CaptureState == CAPTURE_IDLE || m_CaptureState == CAPTURE_ABORTED
454 || m_CaptureState == CAPTURE_COMPLETE || m_CaptureState == CAPTURE_PAUSED)
455 getMeridianFlipState()->updateMFMountState(MeridianFlipState::MOUNT_FLIP_ACCEPTED);
459 case MeridianFlipState::MOUNT_FLIP_RUNNING:
460 updateMeridianFlipStage(MeridianFlipState::MF_INITIATED);
461 setCaptureState(CAPTURE_MERIDIAN_FLIP);
464 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
465 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
474void CameraState::updateMeridianFlipStage(
const MeridianFlipState::MFStage &stage)
477 getMeridianFlipState()->updateMeridianFlipStage(stage);
482 case MeridianFlipState::MF_READY:
485 case MeridianFlipState::MF_INITIATED:
486 emit meridianFlipStarted();
489 case MeridianFlipState::MF_COMPLETED:
492 if (getRefocusState()->isInSequenceFocus())
494 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Resetting HFR Check counter after meridian flip.";
496 getRefocusState()->setInSequenceFocusCounter(0);
500 if ( Options::ditherEnabled() || Options::ditherNoGuiding())
501 resetDitherCounter();
504 if (Options::refocusAfterMeridianFlip() ==
true)
505 getRefocusState()->setRefocusAfterMeridianFlip(
true);
507 if (hasDome && (m_domeState == ISD::Dome::DOME_MOVING_CW || m_domeState == ISD::Dome::DOME_MOVING_CCW))
510 KSNotification::event(
QLatin1String(
"MeridianFlipCompleted"),
i18n(
"Meridian flip is successfully completed"),
511 KSNotification::Capture);
513 getMeridianFlipState()->processFlipCompleted();
516 setCaptureState(m_ContinueAction == CONTINUE_ACTION_NONE ? CAPTURE_IDLE :
CAPTURE_PAUSED);
523 emit newMeridianFlipStage(stage);
526bool CameraState::checkMeridianFlipActive()
528 return (getMeridianFlipState()->checkMeridianFlipRunning() ||
529 checkPostMeridianFlipActions() ||
530 checkMeridianFlipReady());
533bool CameraState::checkMeridianFlipReady()
535 if (hasTelescope ==
false)
540 if (m_activeJob && m_activeJob->getFrameType() == FRAME_FLAT && m_activeJob->getCalibrationPreAction() & ACTION_WALL)
543 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED)
550 if (m_refocusState->isInSequenceFocus() ||
551 (Options::enforceRefocusEveryN() && m_refocusState->getRefocusEveryNTimerElapsedSec() > 0))
555 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
556 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_READY);
562bool CameraState::checkPostMeridianFlipActions()
565 if (m_CaptureState == CAPTURE_ALIGNING || checkAlignmentAfterFlip())
570 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED && m_GuideState != GUIDE_GUIDING
571 && checkGuidingAfterFlip())
577 if (m_CaptureState == CAPTURE_CALIBRATING
578 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
580 if (Options::enforceGuideDeviation() || Options::enforceStartGuiderDrift())
583 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
590bool CameraState::checkGuidingAfterFlip()
593 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
596 if (getMeridianFlipState()->resumeGuidingAfterFlip() ==
false)
598 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
603 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED
604 && getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_GUIDING)
606 appendLogText(
i18n(
"Performing post flip re-calibration and guiding..."));
608 setCaptureState(CAPTURE_CALIBRATING);
610 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_GUIDING);
611 emit guideAfterMeridianFlip();
614 else if (m_CaptureState == CAPTURE_CALIBRATING)
616 if (getGuideState() == GUIDE_CALIBRATION_ERROR || getGuideState() == GUIDE_ABORTED)
619 appendLogText(
i18n(
"Post meridian flip calibration error. Restarting..."));
620 emit guideAfterMeridianFlip();
623 else if (getGuideState() != GUIDE_GUIDING)
635void CameraState::processGuidingFailed()
637 if (m_FocusState > FOCUS_PROGRESS)
639 appendLogText(
i18n(
"Autoguiding stopped. Waiting for autofocus to finish..."));
642 else if (isGuidingOn()
643 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_NONE &&
645 ((m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT) ||
646 m_CaptureState == CAPTURE_SUSPENDED || m_CaptureState == CAPTURE_PAUSED))
648 appendLogText(
i18n(
"Autoguiding stopped. Aborting..."));
651 else if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
653 if (increaseAlignmentRetries() >= 3)
655 appendLogText(
i18n(
"Post meridian flip calibration error. Aborting..."));
661void CameraState::updateAdaptiveFocusState(
bool success)
663 m_refocusState->setAdaptiveFocusDone(
true);
667 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus completed successfully";
669 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus failed";
671 m_refocusState->setAutoFocusReady(
true);
673 if (m_activeJob !=
nullptr)
674 m_activeJob->setAutoFocusReady(
true);
676 setFocusState(FOCUS_COMPLETE);
677 emit newLog(
i18n(success ?
"Adaptive focus complete." :
"Adaptive focus failed. Continuing..."));
680void CameraState::updateFocusState(FocusState state)
682 if (state != m_FocusState)
683 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Focus State changed from" <<
684 Ekos::getFocusStatusString(m_FocusState) <<
685 "to" << Ekos::getFocusStatusString(state);
686 setFocusState(state);
689 if (getMeridianFlipState()->checkMeridianFlipRunning())
696 if (!(getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_INITIATED))
700 emit newFocusStatus(state);
701 appendLogText(
i18n(
"Autofocus failed. Aborting exposure..."));
707 m_refocusState->setAutoFocusReady(
true);
709 if (m_activeJob !=
nullptr)
710 m_activeJob->setAutoFocusReady(
true);
712 if (m_refocusState->getFocusHFRInAutofocus())
713 m_refocusState->startRefocusTimer(
true);
716 if (Options::hFRCheckAlgorithm() == HFR_CHECK_MEDIAN_MEASURE ||
717 (m_refocusState->getFocusHFRInAutofocus() && Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS))
719 m_refocusState->addHFRValue(getFocusFilterName());
720 updateHFRThreshold();
722 emit newFocusStatus(state);
728 if (m_activeJob !=
nullptr)
729 m_activeJob->setFocusStatus(state);
732AutofocusReason CameraState::getAFReason(RefocusState::RefocusReason state,
QString &reasonInfo)
734 AutofocusReason afReason;
738 case RefocusState::REFOCUS_USER_REQUEST:
739 afReason = AutofocusReason::FOCUS_USER_REQUEST;
741 case RefocusState::REFOCUS_TEMPERATURE:
742 afReason = AutofocusReason::FOCUS_TEMPERATURE;
743 reasonInfo =
i18n(
"Limit: %1 °C",
QString::number(Options::maxFocusTemperatureDelta(),
'f', 2));
745 case RefocusState::REFOCUS_TIME_ELAPSED:
746 afReason = AutofocusReason::FOCUS_TIME;
747 reasonInfo =
i18n(
"Limit: %1 mins", Options::refocusEveryN());
749 case RefocusState::REFOCUS_POST_MF:
750 afReason = AutofocusReason::FOCUS_MERIDIAN_FLIP;
753 afReason = AutofocusReason::FOCUS_NONE;
758bool CameraState::startFocusIfRequired()
764 if (m_activeJob ==
nullptr || m_activeJob->getFrameType() != FRAME_LIGHT
765 || m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
768 RefocusState::RefocusReason reason = m_refocusState->checkFocusRequired();
771 if (reason == RefocusState::REFOCUS_NONE)
775 m_refocusState->setRefocusAfterMeridianFlip(
false);
781 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_NONE)
783 int targetFilterPosition = m_activeJob->getTargetFilter();
784 if (targetFilterPosition > 0 && targetFilterPosition != getCurrentFilterPosition())
785 emit newFilterPosition(targetFilterPosition);
788 emit abortFastExposure();
789 updateFocusState(FOCUS_PROGRESS);
791 AutofocusReason afReason;
795 case RefocusState::REFOCUS_HFR:
796 m_refocusState->resetInSequenceFocusCounter();
797 emit checkFocus(Options::hFRDeviation());
798 qCDebug(KSTARS_EKOS_CAPTURE) <<
"In-sequence focusing started...";
800 case RefocusState::REFOCUS_ADAPTIVE:
801 m_refocusState->setAdaptiveFocusDone(
true);
802 emit adaptiveFocus();
803 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus started...";
805 case RefocusState::REFOCUS_USER_REQUEST:
806 case RefocusState::REFOCUS_TEMPERATURE:
807 case RefocusState::REFOCUS_TIME_ELAPSED:
808 case RefocusState::REFOCUS_POST_MF:
810 if (m_refocusState->getRefocusEveryNTimerElapsedSec() >= 1800)
814 afReason = getAFReason(reason, reasonInfo);
815 emit runAutoFocus(afReason, reasonInfo);
817 m_refocusState->resetInSequenceFocusCounter();
818 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Refocusing started...";
825 setCaptureState(CAPTURE_FOCUSING);
829void CameraState::updateHFRThreshold()
832 if (Options::hFRCheckAlgorithm() == HFR_CHECK_FIXED)
835 QString finalFilter = getFocusFilterName();
836 QList<double> filterHFRList = m_refocusState->getHFRMap()[finalFilter];
839 if (filterHFRList.
empty())
843 if (Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS)
844 value = filterHFRList.
last();
847 int count = filterHFRList.
size();
849 value = (count % 2) ? filterHFRList[count / 2] : (filterHFRList[count / 2 - 1] + filterHFRList[count / 2]) / 2.0;
851 value = filterHFRList[0];
853 value += value * (Options::hFRThresholdPercentage() / 100.0);
854 Options::setHFRDeviation(value);
855 emit newLimitFocusHFR(value);
858QString CameraState::getFocusFilterName()
861 if (m_CurrentFilterPosition > 0)
866 finalFilter = (m_CurrentFocusFilterName ==
"--" ? m_CurrentFilterName : m_CurrentFocusFilterName);
873bool CameraState::checkAlignmentAfterFlip()
876 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
879 if (getMeridianFlipState()->resumeAlignmentAfterFlip() ==
false)
883 if (m_CaptureState < CAPTURE_ALIGNING)
885 appendLogText(
i18n(
"Performing post flip re-alignment..."));
887 resetAlignmentRetries();
888 setCaptureState(CAPTURE_ALIGNING);
890 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_ALIGNING);
898void CameraState::checkGuideDeviationTimeout()
900 if (m_activeJob && m_activeJob->getStatus() == JOB_ABORTED
901 && isGuidingDeviationDetected())
903 appendLogText(
i18n(
"Guide module timed out."));
904 setGuidingDeviationDetected(
false);
907 if (m_CaptureState == CAPTURE_SUSPENDED)
909 setCaptureState(CAPTURE_ABORTED);
914void CameraState::setGuideDeviation(
double deviation_rms)
917 emit newGuiderDrift(deviation_rms);
922 if (m_activeJob ==
nullptr && checkMeridianFlipReady())
926 if (m_CaptureState == CAPTURE_PROGRESS &&
927 getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED &&
928 getMeridianFlipState()->checkMeridianFlipRunning() ==
false)
931 if (Options::enforceStartGuiderDrift() ==
false || deviation_rms < Options::startGuideDeviation())
933 setCaptureState(CAPTURE_CALIBRATING);
934 if (Options::enforceStartGuiderDrift())
935 appendLogText(
i18n(
"Initial guiding deviation %1 below limit value of %2 arcsecs",
936 deviationText, Options::startGuideDeviation()));
937 setGuidingDeviationDetected(
false);
938 setStartingCapture(
false);
943 if (isGuidingDeviationDetected() ==
false)
944 appendLogText(
i18n(
"Initial guiding deviation %1 exceeded limit value of %2 arcsecs",
945 deviationText, Options::startGuideDeviation()));
947 setGuidingDeviationDetected(
true);
951 if (checkMeridianFlipReady())
961 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
964 if (Options::enforceGuideDeviation() ==
false || deviation_rms < Options::guideDeviation())
966 appendLogText(
i18n(
"Post meridian flip calibration completed successfully."));
968 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
974 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT
975 && isStartingCapture() && Options::enforceStartGuiderDrift())
977 setStartingCapture(
false);
978 if (deviation_rms > Options::startGuideDeviation())
980 appendLogText(
i18n(
"Guiding deviation at capture startup %1 exceeded limit %2 arcsecs.",
981 deviationText, Options::startGuideDeviation()));
982 emit suspendCapture();
983 setGuidingDeviationDetected(
true);
987 if (checkMeridianFlipReady())
990 getGuideDeviationTimer().start();
994 appendLogText(
i18n(
"Guiding deviation at capture startup %1 below limit value of %2 arcsecs",
995 deviationText, Options::startGuideDeviation()));
998 if (m_CaptureState != CAPTURE_SUSPENDED)
1002 if ((Options::enforceGuideDeviation() ==
false)
1004 (m_activeJob && (m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW ||
1005 m_activeJob->getExposeLeft() == 0.0 ||
1006 m_activeJob->getFrameType() != FRAME_LIGHT)))
1011 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT)
1013 if (deviation_rms <= Options::guideDeviation())
1014 resetSpikesDetected();
1018 if (increaseSpikesDetected() < Options::guideDeviationReps())
1021 appendLogText(
i18n(
"Guiding deviation %1 exceeded limit value of %2 arcsecs for %4 consecutive samples, "
1022 "suspending exposure and waiting for guider up to %3 seconds.",
1023 deviationText, Options::guideDeviation(),
1024 QString(
"%L1").arg(getGuideDeviationTimer().interval() / 1000.0, 0,
'f', 3),
1025 Options::guideDeviationReps()));
1027 emit suspendCapture();
1029 resetSpikesDetected();
1030 setGuidingDeviationDetected(
true);
1034 if (checkMeridianFlipReady())
1035 emit startCapture();
1037 getGuideDeviationTimer().start();
1045 for(
auto &job : allJobs())
1047 if (job->getStatus() == JOB_ABORTED)
1054 if (abortedJob !=
nullptr && isGuidingDeviationDetected())
1056 if (deviation_rms <= Options::startGuideDeviation())
1058 getGuideDeviationTimer().stop();
1061 if (! getCaptureDelayTimer().isActive())
1064 if (m_CaptureState == CAPTURE_SUSPENDED)
1066 const int seqDelay = abortedJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1068 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1069 "resuming exposure.",
1070 deviationText, Options::startGuideDeviation()));
1072 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1073 "resuming exposure in %3 seconds.",
1074 deviationText, Options::startGuideDeviation(), seqDelay / 1000.0));
1076 emit startCapture();
1084 if (getCaptureDelayTimer().isActive())
1085 getCaptureDelayTimer().stop();
1087 appendLogText(
i18n(
"Guiding deviation %1 is still higher than limit value of %2 arcsecs.",
1088 deviationText, Options::startGuideDeviation()));
1093void CameraState::addDownloadTime(
double time)
1095 totalDownloadTime += time;
1099int CameraState::pendingJobCount()
1101 int completedJobs = 0;
1105 if (job->getStatus() == JOB_DONE)
1109 return (allJobs().count() - completedJobs);
1113QString CameraState::jobState(
int id)
1115 if (
id < allJobs().count())
1118 return job->getStatusString();
1125QString CameraState::jobFilterName(
int id)
1127 if (
id < allJobs().count())
1130 return job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1137CCDFrameType CameraState::jobFrameType(
int id)
1139 if (
id < allJobs().count())
1142 return job->getFrameType();
1148int CameraState::jobImageProgress(
int id)
1150 if (
id < allJobs().count())
1153 return job->getCompleted();
1159int CameraState::jobImageCount(
int id)
1161 if (
id < allJobs().count())
1164 return job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1170double CameraState::jobExposureProgress(
int id)
1172 if (
id < allJobs().count())
1175 return job->getExposeLeft();
1181double CameraState::jobExposureDuration(
int id)
1183 if (
id < allJobs().count())
1186 return job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1192double CameraState::progressPercentage()
1194 int totalImageCount = 0;
1195 int totalImageCompleted = 0;
1199 totalImageCount += job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1200 totalImageCompleted += job->getCompleted();
1203 if (totalImageCount != 0)
1204 return ((
static_cast<double>(totalImageCompleted) / totalImageCount) * 100.0);
1209bool CameraState::isActiveJobPreview()
1211 return m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW;
1214int CameraState::activeJobRemainingTime()
1216 if (m_activeJob ==
nullptr)
1219 return m_activeJob->getJobRemainingTime(averageDownloadTime());
1222int CameraState::overallRemainingTime()
1225 double estimatedDownloadTime = averageDownloadTime();
1228 remaining += job->getJobRemainingTime(estimatedDownloadTime);
1233QString CameraState::sequenceQueueStatus()
1235 if (allJobs().count() == 0)
1241 int idle = 0,
error = 0, complete = 0, aborted = 0, running = 0;
1245 switch (job->getStatus())
1270 if (m_CaptureState == CAPTURE_SUSPENDED)
1279 if (idle == allJobs().count())
1282 if (complete == allJobs().count())
1292 {
"preAction",
static_cast<int>(calibrationPreAction())},
1293 {
"duration", flatFieldDuration()},
1294 {
"az", wallCoord().az().Degrees()},
1295 {
"al", wallCoord().alt().Degrees()},
1296 {
"adu", targetADU()},
1297 {
"tolerance", targetADUTolerance()},
1298 {
"skyflat", skyFlat()},
1304void CameraState::setCalibrationSettings(
const QJsonObject &settings)
1306 const int preAction = settings[
"preAction"].toInt(calibrationPreAction());
1307 const int duration = settings[
"duration"].toInt(flatFieldDuration());
1308 const double az = settings[
"az"].toDouble(wallCoord().az().Degrees());
1309 const double al = settings[
"al"].toDouble(wallCoord().alt().Degrees());
1310 const int adu = settings[
"adu"].toInt(
static_cast<int>(std::round(targetADU())));
1311 const int tolerance = settings[
"tolerance"].toInt(
static_cast<int>(std::round(targetADUTolerance())));
1312 const int skyflat = settings[
"skyflat"].toBool();
1314 setCalibrationPreAction(
static_cast<CalibrationPreActions
>(preAction));
1315 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(duration));
1316 wallCoord().setAz(az);
1317 wallCoord().setAlt(al);
1319 setTargetADUTolerance(tolerance);
1320 setSkyFlat(skyflat);
1323bool CameraState::setDarkFlatExposure(
SequenceJob *job)
1325 const auto darkFlatFilter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1326 const auto darkFlatBinning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1327 const auto darkFlatADU = job->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1329 for (
auto &oneJob : allJobs())
1331 if (oneJob->getFrameType() != FRAME_FLAT)
1334 const auto filter = oneJob->getCoreProperty(SequenceJob::SJ_Filter).toString();
1337 if (!darkFlatFilter.isEmpty() && darkFlatFilter != filter)
1341 const auto binning = oneJob->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1342 if (darkFlatBinning != binning)
1346 const auto adu = oneJob->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1347 if (job->getFlatFieldDuration() == DURATION_ADU)
1349 if (darkFlatADU != adu)
1354 job->setCoreProperty(SequenceJob::SJ_Exposure, oneJob->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
1361void CameraState::checkSeqBoundary()
1364 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1367 setNextSequenceID(placeholderPath().checkSeqBoundary(*getActiveJob()));
1370bool CameraState::isModelinDSLRInfo(
const QString &model)
1374 return (oneDSLRInfo[
"Model"] == model);
1377 return (pos != m_DSLRInfos.end());
1380void CameraState::setCapturedFramesCount(
const QString &signature, uint16_t count)
1382 m_capturedFramesMap[signature] = count;
1383 qCDebug(KSTARS_EKOS_CAPTURE) <<
1384 QString(
"Client module indicates that storage for '%1' has already %2 captures processed.").
arg(signature).
arg(count);
1386 setIgnoreJobProgress(
false);
1389void CameraState::changeSequenceValue(
int index,
QString key,
QString value)
1392 QJsonObject oneSequence = seqArray[index].toObject();
1393 oneSequence[key] = value;
1394 seqArray.
replace(index, oneSequence);
1395 setSequence(seqArray);
1396 emit sequenceChanged(seqArray);
1399void CameraState::addCapturedFrame(
const QString &signature)
1401 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1402 if (m_capturedFramesMap.end() != frame_item)
1403 frame_item.value()++;
1404 else m_capturedFramesMap[signature] = 1;
1407void CameraState::removeCapturedFrameCount(
const QString &signature, uint16_t count)
1409 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1410 if (m_capturedFramesMap.end() != frame_item)
1412 if (frame_item.value() <= count)
1414 m_capturedFramesMap.remove(signature);
1417 frame_item.value() = frame_item.value() - count;
1423void CameraState::appendLogText(
const QString &message)
1425 qCInfo(KSTARS_EKOS_CAPTURE()) << message;
1426 emit newLog(message);
1429bool CameraState::isGuidingOn()
1432 if (Options::ditherNoGuiding())
1435 return (m_GuideState == GUIDE_GUIDING ||
1436 m_GuideState == GUIDE_CALIBRATING ||
1437 m_GuideState == GUIDE_CALIBRATION_SUCCESS ||
1438 m_GuideState == GUIDE_DARK ||
1439 m_GuideState == GUIDE_SUBFRAME ||
1440 m_GuideState == GUIDE_STAR_SELECT ||
1441 m_GuideState == GUIDE_REACQUIRE ||
1442 m_GuideState == GUIDE_DITHERING ||
1443 m_GuideState == GUIDE_DITHERING_SUCCESS ||
1444 m_GuideState == GUIDE_DITHERING_ERROR ||
1445 m_GuideState == GUIDE_DITHERING_SETTLE ||
1446 m_GuideState == GUIDE_SUSPENDED
1450bool CameraState::isActivelyGuiding()
1452 return isGuidingOn() && (m_GuideState == GUIDE_GUIDING);
1455void CameraState::setAlignState(AlignState value)
1457 if (value != m_AlignState)
1458 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Align State changed from" << Ekos::getAlignStatusString(
1459 m_AlignState) <<
"to" << Ekos::getAlignStatusString(value);
1460 m_AlignState = value;
1462 getMeridianFlipState()->setResumeAlignmentAfterFlip(
true);
1467 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1469 appendLogText(
i18n(
"Post flip re-alignment completed successfully."));
1470 resetAlignmentRetries();
1472 if (checkGuidingAfterFlip() ==
false)
1475 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
1476 setCaptureState(CAPTURE_WAITING);
1484 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1486 if (increaseAlignmentRetries() >= 3)
1488 appendLogText(
i18n(
"Post-flip alignment failed."));
1489 emit abortCapture();
1493 appendLogText(
i18n(
"Post-flip alignment failed. Retrying..."));
1495 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
Sequence Job is a container for the details required to capture a series of images.
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.
QString name(GameStandardAction id)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
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
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)
QString toLocalFile() const const