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 KSNotification::event(
QLatin1String(
"MeridianFlipCompleted"),
i18n(
"Meridian flip is successfully completed"),
504 KSNotification::Capture);
506 getMeridianFlipState()->processFlipCompleted();
509 setCaptureState(m_ContinueAction == CAPTURE_CONTINUE_ACTION_NONE ? CAPTURE_IDLE : CAPTURE_PAUSED);
516 emit newMeridianFlipStage(stage);
519bool CameraState::checkMeridianFlipActive()
521 return (getMeridianFlipState()->checkMeridianFlipRunning() ||
522 checkPostMeridianFlipActions() ||
523 checkMeridianFlipReady());
526bool CameraState::checkMeridianFlipReady()
528 if (hasTelescope ==
false)
533 if (m_activeJob && m_activeJob->getFrameType() == FRAME_FLAT
534 && m_activeJob->getCalibrationPreAction() & CAPTURE_PREACTION_WALL)
537 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED)
544 if (m_refocusState->isInSequenceFocus() ||
545 (Options::enforceRefocusEveryN() && m_refocusState->getRefocusEveryNTimerElapsedSec() > 0))
546 emit resetFocusFrame();
549 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
550 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_READY);
556bool CameraState::checkPostMeridianFlipActions()
559 if (hasDome && (m_domeState == ISD::Dome::DOME_MOVING_CW || m_domeState == ISD::Dome::DOME_MOVING_CCW))
563 if (m_CaptureState == CAPTURE_ALIGNING || checkAlignmentAfterFlip())
568 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED && m_GuideState != GUIDE_GUIDING
569 && checkGuidingAfterFlip())
575 if (m_CaptureState == CAPTURE_CALIBRATING
576 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
578 if (Options::enforceGuideDeviation() || Options::enforceStartGuiderDrift())
581 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
588bool CameraState::checkGuidingAfterFlip()
591 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
594 if (getMeridianFlipState()->resumeGuidingAfterFlip() ==
false)
596 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
601 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED
602 && getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_GUIDING)
604 appendLogText(
i18n(
"Performing post flip re-calibration and guiding..."));
606 setCaptureState(CAPTURE_CALIBRATING);
608 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_GUIDING);
609 emit guideAfterMeridianFlip();
612 else if (m_CaptureState == CAPTURE_CALIBRATING)
614 if (getGuideState() == GUIDE_CALIBRATION_ERROR || getGuideState() == GUIDE_ABORTED)
617 appendLogText(
i18n(
"Post meridian flip calibration error. Restarting..."));
618 emit guideAfterMeridianFlip();
621 else if (getGuideState() != GUIDE_GUIDING)
633void CameraState::processGuidingFailed()
635 if (m_FocusState > FOCUS_PROGRESS)
637 appendLogText(
i18n(
"Autoguiding stopped. Waiting for autofocus to finish..."));
640 else if (isGuidingOn()
641 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_NONE &&
643 ((m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT) ||
644 m_CaptureState == CAPTURE_SUSPENDED || m_CaptureState == CAPTURE_PAUSED))
646 appendLogText(
i18n(
"Autoguiding stopped. Aborting..."));
649 else if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
651 if (increaseAlignmentRetries() >= 3)
653 appendLogText(
i18n(
"Post meridian flip calibration error. Aborting..."));
659void CameraState::updateAdaptiveFocusState(
bool success)
661 m_refocusState->setAdaptiveFocusDone(
true);
665 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus completed successfully";
667 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus failed";
669 m_refocusState->setAutoFocusReady(
true);
671 if (m_activeJob !=
nullptr)
672 m_activeJob->setAutoFocusReady(
true);
674 setFocusState(FOCUS_COMPLETE);
675 emit newLog(
i18n(success ?
"Adaptive focus complete." :
"Adaptive focus failed. Continuing..."));
678void CameraState::updateFocusState(FocusState state)
680 if (state != m_FocusState)
681 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Focus State changed from" <<
682 Ekos::getFocusStatusString(m_FocusState) <<
683 "to" << Ekos::getFocusStatusString(state);
684 setFocusState(state);
687 if (getMeridianFlipState()->checkMeridianFlipRunning())
694 if (!(getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_INITIATED))
698 emit newFocusStatus(state);
699 appendLogText(
i18n(
"Autofocus failed. Aborting exposure..."));
705 m_refocusState->setAutoFocusReady(
true);
707 if (m_activeJob !=
nullptr)
708 m_activeJob->setAutoFocusReady(
true);
710 if (m_refocusState->getFocusHFRInAutofocus())
711 m_refocusState->startRefocusTimer(
true);
714 if (Options::hFRCheckAlgorithm() == HFR_CHECK_MEDIAN_MEASURE ||
715 (m_refocusState->getFocusHFRInAutofocus() && Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS))
717 m_refocusState->addHFRValue(getFocusFilterName());
718 updateHFRThreshold();
720 emit newFocusStatus(state);
726 if (m_activeJob !=
nullptr)
731 if (state == FOCUS_COMPLETE && Options::guidingSettle() > 0 && Options::focusSuspendGuiding()
732 && m_GuideState == GUIDE_GUIDING)
735 appendLogText(
i18n(
"Focus complete. Resuming in %1 seconds...", Options::guidingSettle()));
738 if (m_activeJob !=
nullptr)
739 m_activeJob->setFocusStatus(state);
743 m_activeJob->setFocusStatus(state);
747AutofocusReason CameraState::getAFReason(RefocusState::RefocusReason state,
QString &reasonInfo)
749 AutofocusReason afReason;
753 case RefocusState::REFOCUS_USER_REQUEST:
754 afReason = AutofocusReason::FOCUS_USER_REQUEST;
756 case RefocusState::REFOCUS_TEMPERATURE:
757 afReason = AutofocusReason::FOCUS_TEMPERATURE;
758 reasonInfo =
i18n(
"Limit: %1 °C",
QString::number(Options::maxFocusTemperatureDelta(),
'f', 2));
760 case RefocusState::REFOCUS_TIME_ELAPSED:
761 afReason = AutofocusReason::FOCUS_TIME;
762 reasonInfo =
i18n(
"Limit: %1 mins", Options::refocusEveryN());
764 case RefocusState::REFOCUS_POST_MF:
765 afReason = AutofocusReason::FOCUS_MERIDIAN_FLIP;
768 afReason = AutofocusReason::FOCUS_NONE;
773bool CameraState::startFocusIfRequired()
779 if (m_activeJob ==
nullptr || m_activeJob->getFrameType() != FRAME_LIGHT
780 || m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
783 RefocusState::RefocusReason reason = m_refocusState->checkFocusRequired();
786 if (reason == RefocusState::REFOCUS_NONE)
790 m_refocusState->setRefocusAfterMeridianFlip(
false);
796 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_NONE)
798 int targetFilterPosition = m_activeJob->getTargetFilter();
799 if (targetFilterPosition > 0 && targetFilterPosition != getCurrentFilterPosition())
800 emit newFilterPosition(targetFilterPosition);
803 emit abortFastExposure();
804 updateFocusState(FOCUS_PROGRESS);
806 AutofocusReason afReason;
810 case RefocusState::REFOCUS_HFR:
811 m_refocusState->resetInSequenceFocusCounter();
812 emit checkFocus(Options::hFRDeviation());
813 qCDebug(KSTARS_EKOS_CAPTURE) <<
"In-sequence focusing started...";
815 case RefocusState::REFOCUS_ADAPTIVE:
816 m_refocusState->setAdaptiveFocusDone(
true);
817 emit adaptiveFocus();
818 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus started...";
820 case RefocusState::REFOCUS_USER_REQUEST:
821 case RefocusState::REFOCUS_TEMPERATURE:
822 case RefocusState::REFOCUS_TIME_ELAPSED:
823 case RefocusState::REFOCUS_POST_MF:
825 if (m_refocusState->getRefocusEveryNTimerElapsedSec() >= 1800)
826 emit resetFocusFrame();
829 afReason = getAFReason(reason, reasonInfo);
830 emit runAutoFocus(afReason, reasonInfo);
832 m_refocusState->resetInSequenceFocusCounter();
833 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Refocusing started...";
840 setCaptureState(CAPTURE_FOCUSING);
844void CameraState::updateHFRThreshold()
847 if (Options::hFRCheckAlgorithm() == HFR_CHECK_FIXED)
850 QString finalFilter = getFocusFilterName();
851 QList<double> filterHFRList = m_refocusState->getHFRMap()[finalFilter];
854 if (filterHFRList.
empty())
858 if (Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS)
859 value = filterHFRList.
last();
862 int count = filterHFRList.
size();
864 value = (count % 2) ? filterHFRList[count / 2] : (filterHFRList[count / 2 - 1] + filterHFRList[count / 2]) / 2.0;
866 value = filterHFRList[0];
868 value += value * (Options::hFRThresholdPercentage() / 100.0);
869 Options::setHFRDeviation(value);
870 emit newLimitFocusHFR(value);
873QString CameraState::getFocusFilterName()
876 if (m_CurrentFilterPosition > 0)
881 finalFilter = (m_CurrentFocusFilterName ==
"--" ? m_CurrentFilterName : m_CurrentFocusFilterName);
888bool CameraState::checkAlignmentAfterFlip()
891 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
893 qCDebug(KSTARS_EKOS_CAPTURE) <<
"checkAlignmentAfterFlip too early, meridian flip stage =" <<
894 getMeridianFlipState()->getMeridianFlipStage();
898 if (getMeridianFlipState()->resumeAlignmentAfterFlip() ==
false)
900 qCDebug(KSTARS_EKOS_CAPTURE) <<
"No alignment after flip required.";
905 if (m_CaptureState < CAPTURE_ALIGNING)
907 appendLogText(
i18n(
"Performing post flip re-alignment..."));
909 resetAlignmentRetries();
910 setCaptureState(CAPTURE_ALIGNING);
912 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_ALIGNING);
920void CameraState::checkGuideDeviationTimeout()
922 if (m_activeJob && m_activeJob->getStatus() == JOB_ABORTED
923 && isGuidingDeviationDetected())
925 appendLogText(
i18n(
"Guide module timed out."));
926 setGuidingDeviationDetected(
false);
929 if (m_CaptureState == CAPTURE_SUSPENDED)
931 setCaptureState(CAPTURE_ABORTED);
936void CameraState::setGuideDeviation(
double deviation_rms)
939 emit newGuiderDrift(deviation_rms);
944 if (m_activeJob ==
nullptr && checkMeridianFlipReady())
948 if (m_CaptureState == CAPTURE_PROGRESS &&
949 getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED &&
950 getMeridianFlipState()->checkMeridianFlipRunning() ==
false)
953 if (Options::enforceStartGuiderDrift() ==
false || deviation_rms < Options::startGuideDeviation())
955 setCaptureState(CAPTURE_CALIBRATING);
956 if (Options::enforceStartGuiderDrift())
957 appendLogText(
i18n(
"Initial guiding deviation %1 below limit value of %2 arcsecs",
958 deviationText, Options::startGuideDeviation()));
959 setGuidingDeviationDetected(
false);
960 setStartingCapture(
false);
965 if (isGuidingDeviationDetected() ==
false)
966 appendLogText(
i18n(
"Initial guiding deviation %1 exceeded limit value of %2 arcsecs",
967 deviationText, Options::startGuideDeviation()));
969 setGuidingDeviationDetected(
true);
973 if (checkMeridianFlipReady())
983 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
986 if (Options::enforceGuideDeviation() ==
false || deviation_rms < Options::guideDeviation())
988 appendLogText(
i18n(
"Post meridian flip calibration completed successfully."));
990 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
996 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT
997 && isStartingCapture() && Options::enforceStartGuiderDrift())
999 setStartingCapture(
false);
1000 if (deviation_rms > Options::startGuideDeviation())
1002 appendLogText(
i18n(
"Guiding deviation at capture startup %1 exceeded limit %2 arcsecs.",
1003 deviationText, Options::startGuideDeviation()));
1004 emit suspendCapture();
1005 setGuidingDeviationDetected(
true);
1009 if (checkMeridianFlipReady())
1010 emit startCapture();
1012 getGuideDeviationTimer().start();
1016 appendLogText(
i18n(
"Guiding deviation at capture startup %1 below limit value of %2 arcsecs",
1017 deviationText, Options::startGuideDeviation()));
1020 if (m_CaptureState != CAPTURE_SUSPENDED)
1024 if ((Options::enforceGuideDeviation() ==
false)
1026 (m_activeJob && (m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW ||
1027 m_activeJob->getExposeLeft() == 0.0 ||
1028 m_activeJob->getFrameType() != FRAME_LIGHT)))
1033 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT)
1035 if (deviation_rms <= Options::guideDeviation())
1036 resetSpikesDetected();
1040 if (increaseSpikesDetected() < Options::guideDeviationReps())
1043 appendLogText(
i18n(
"Guiding deviation %1 exceeded limit value of %2 arcsecs for %4 consecutive samples, "
1044 "suspending exposure and waiting for guider up to %3 seconds.",
1045 deviationText, Options::guideDeviation(),
1046 QString(
"%L1").arg(getGuideDeviationTimer().interval() / 1000.0, 0,
'f', 3),
1047 Options::guideDeviationReps()));
1049 emit suspendCapture();
1051 resetSpikesDetected();
1052 setGuidingDeviationDetected(
true);
1056 if (checkMeridianFlipReady())
1057 emit startCapture();
1059 getGuideDeviationTimer().start();
1067 for(
auto &job : allJobs())
1069 if (job->getStatus() == JOB_ABORTED)
1076 if (abortedJob !=
nullptr && isGuidingDeviationDetected())
1078 if (deviation_rms <= Options::startGuideDeviation())
1080 getGuideDeviationTimer().stop();
1083 if (! getCaptureDelayTimer().isActive())
1086 if (m_CaptureState == CAPTURE_SUSPENDED)
1088 const int seqDelay = abortedJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1090 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1091 "resuming exposure.",
1092 deviationText, Options::startGuideDeviation()));
1094 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1095 "resuming exposure in %3 seconds.",
1096 deviationText, Options::startGuideDeviation(), seqDelay / 1000.0));
1098 emit startCapture();
1106 if (getCaptureDelayTimer().isActive())
1107 getCaptureDelayTimer().stop();
1109 appendLogText(
i18n(
"Guiding deviation %1 is still higher than limit value of %2 arcsecs.",
1110 deviationText, Options::startGuideDeviation()));
1115void CameraState::addDownloadTime(
double time)
1117 totalDownloadTime += time;
1121int CameraState::pendingJobCount()
1123 int completedJobs = 0;
1127 if (job->getStatus() == JOB_DONE)
1131 return (allJobs().count() - completedJobs);
1135QString CameraState::jobState(
int id)
1137 if (
id < allJobs().count())
1140 return job->getStatusString();
1147QString CameraState::jobFilterName(
int id)
1149 if (
id < allJobs().count())
1152 return job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1159CCDFrameType CameraState::jobFrameType(
int id)
1161 if (
id < allJobs().count())
1164 return job->getFrameType();
1170int CameraState::jobImageProgress(
int id)
1172 if (
id < allJobs().count())
1175 return job->getCompleted();
1181int CameraState::jobImageCount(
int id)
1183 if (
id < allJobs().count())
1186 return job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1192double CameraState::jobExposureProgress(
int id)
1194 if (
id < allJobs().count())
1197 return job->getExposeLeft();
1203double CameraState::jobExposureDuration(
int id)
1205 if (
id < allJobs().count())
1208 return job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1214double CameraState::progressPercentage()
1216 int totalImageCount = 0;
1217 int totalImageCompleted = 0;
1221 totalImageCount += job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1222 totalImageCompleted += job->getCompleted();
1225 if (totalImageCount != 0)
1226 return ((
static_cast<double>(totalImageCompleted) / totalImageCount) * 100.0);
1231bool CameraState::isActiveJobPreview()
1233 return m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW;
1236int CameraState::activeJobRemainingTime()
1238 if (m_activeJob ==
nullptr)
1241 return m_activeJob->getJobRemainingTime(averageDownloadTime());
1244int CameraState::overallRemainingTime()
1247 double estimatedDownloadTime = averageDownloadTime();
1250 remaining += job->getJobRemainingTime(estimatedDownloadTime);
1255QString CameraState::sequenceQueueStatus()
1257 if (allJobs().count() == 0)
1263 int idle = 0,
error = 0, complete = 0, aborted = 0, running = 0;
1267 switch (job->getStatus())
1292 if (m_CaptureState == CAPTURE_SUSPENDED)
1301 if (idle == allJobs().count())
1304 if (complete == allJobs().count())
1314 {
"preAction",
static_cast<int>(calibrationPreAction())},
1315 {
"duration", flatFieldDuration()},
1316 {
"az", wallCoord().az().Degrees()},
1317 {
"al", wallCoord().alt().Degrees()},
1318 {
"adu", targetADU()},
1319 {
"tolerance", targetADUTolerance()},
1320 {
"skyflat", skyFlat()},
1326void CameraState::setCalibrationSettings(
const QJsonObject &settings)
1328 const int preAction = settings[
"preAction"].toInt(calibrationPreAction());
1329 const int duration = settings[
"duration"].toInt(flatFieldDuration());
1330 const double az = settings[
"az"].toDouble(wallCoord().az().Degrees());
1331 const double al = settings[
"al"].toDouble(wallCoord().alt().Degrees());
1332 const int adu = settings[
"adu"].toInt(
static_cast<int>(std::round(targetADU())));
1333 const int tolerance = settings[
"tolerance"].toInt(
static_cast<int>(std::round(targetADUTolerance())));
1334 const int skyflat = settings[
"skyflat"].toBool();
1336 setCalibrationPreAction(
static_cast<CalibrationPreActions
>(preAction));
1337 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(duration));
1338 wallCoord().setAz(az);
1339 wallCoord().setAlt(al);
1341 setTargetADUTolerance(tolerance);
1342 setSkyFlat(skyflat);
1345bool CameraState::setDarkFlatExposure(
SequenceJob *job)
1347 const auto darkFlatFilter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1348 const auto darkFlatBinning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1349 const auto darkFlatADU = job->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1351 for (
auto &oneJob : allJobs())
1353 if (oneJob->getFrameType() != FRAME_FLAT)
1356 const auto filter = oneJob->getCoreProperty(SequenceJob::SJ_Filter).toString();
1359 if (!darkFlatFilter.isEmpty() && darkFlatFilter != filter)
1363 const auto binning = oneJob->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1364 if (darkFlatBinning != binning)
1368 const auto adu = oneJob->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1369 if (job->getFlatFieldDuration() == DURATION_ADU)
1371 if (darkFlatADU != adu)
1376 job->setCoreProperty(SequenceJob::SJ_Exposure, oneJob->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
1383void CameraState::checkSeqBoundary()
1386 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1389 setNextSequenceID(placeholderPath().checkSeqBoundary(*getActiveJob()));
1392bool CameraState::isModelinDSLRInfo(
const QString &model)
1396 return (oneDSLRInfo[
"Model"] == model);
1399 return (pos != m_DSLRInfos.end());
1402void CameraState::setCapturedFramesCount(
const QString &signature, uint16_t count)
1404 m_capturedFramesMap[signature] = count;
1405 qCDebug(KSTARS_EKOS_CAPTURE) <<
1406 QString(
"Client module indicates that storage for '%1' has already %2 captures processed.").
arg(signature).
arg(count);
1408 setIgnoreJobProgress(
false);
1411void CameraState::changeSequenceValue(
int index,
QString key,
QString value)
1414 QJsonObject oneSequence = seqArray[index].toObject();
1415 oneSequence[key] = value;
1416 seqArray.
replace(index, oneSequence);
1417 setSequence(seqArray);
1418 emit sequenceChanged(seqArray);
1421void CameraState::addCapturedFrame(
const QString &signature)
1423 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1424 if (m_capturedFramesMap.end() != frame_item)
1425 frame_item.value()++;
1426 else m_capturedFramesMap[signature] = 1;
1429void CameraState::removeCapturedFrameCount(
const QString &signature, uint16_t count)
1431 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1432 if (m_capturedFramesMap.end() != frame_item)
1434 if (frame_item.value() <= count)
1436 m_capturedFramesMap.remove(signature);
1439 frame_item.value() = frame_item.value() - count;
1443void CameraState::appendLogText(
const QString &message)
1445 qCInfo(KSTARS_EKOS_CAPTURE()) << message;
1446 emit newLog(message);
1449bool CameraState::isGuidingOn()
1452 if (Options::ditherNoGuiding())
1455 return (m_GuideState == GUIDE_GUIDING ||
1456 m_GuideState == GUIDE_CALIBRATING ||
1457 m_GuideState == GUIDE_CALIBRATION_SUCCESS ||
1458 m_GuideState == GUIDE_DARK ||
1459 m_GuideState == GUIDE_SUBFRAME ||
1460 m_GuideState == GUIDE_STAR_SELECT ||
1461 m_GuideState == GUIDE_REACQUIRE ||
1462 m_GuideState == GUIDE_DITHERING ||
1463 m_GuideState == GUIDE_DITHERING_SUCCESS ||
1464 m_GuideState == GUIDE_DITHERING_ERROR ||
1465 m_GuideState == GUIDE_DITHERING_SETTLE ||
1466 m_GuideState == GUIDE_SUSPENDED
1470bool CameraState::isActivelyGuiding()
1472 return isGuidingOn() && (m_GuideState == GUIDE_GUIDING);
1475void CameraState::setAlignState(AlignState value)
1477 if (value != m_AlignState)
1478 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Align State changed from" << Ekos::getAlignStatusString(
1479 m_AlignState) <<
"to" << Ekos::getAlignStatusString(value);
1480 m_AlignState = value;
1482 getMeridianFlipState()->setResumeAlignmentAfterFlip(
true);
1487 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1489 appendLogText(
i18n(
"Post flip re-alignment completed successfully."));
1490 resetAlignmentRetries();
1492 if (checkGuidingAfterFlip() ==
false)
1495 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
1496 setCaptureState(CAPTURE_WAITING);
1504 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1506 if (increaseAlignmentRetries() >= 3)
1508 appendLogText(
i18n(
"Post-flip alignment failed."));
1509 emit abortCapture();
1513 appendLogText(
i18n(
"Post-flip alignment failed. Retrying..."));
1515 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
1525void CameraState::setPrepareComplete(
bool success)
1530 setCaptureState(CAPTURE_PROGRESS);
1531 emit executeActiveJob();
1535 qWarning(KSTARS_EKOS_CAPTURE) <<
"Capture preparation failed, aborting.";
1536 setCaptureState(CAPTURE_ABORTED);
1537 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 init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
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
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