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());
39CameraState::CameraState(QObject *parent): QObject{parent}
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())
400 && (m_activeJob !=
nullptr && m_activeJob->getCoreProperty(SequenceJob::SJ_DitherPerJobEnabled).toBool())
402 && getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_GUIDING
404 && (getGuideState() == GUIDE_GUIDING || Options::ditherNoGuiding())
406 && (m_activeJob !=
nullptr && m_activeJob->getFrameType() == FRAME_LIGHT)
408 && m_ditherCounter == 0)
411 resetDitherCounter();
413 appendLogText(
i18n(
"Dithering requested..."));
415 setCaptureState(CAPTURE_DITHERING);
416 setDitheringState(IPS_BUSY);
424void CameraState::updateMFMountState(MeridianFlipState::MeridianFlipMountState status)
426 qCDebug(KSTARS_EKOS_CAPTURE) <<
"updateMFMountState: " << MeridianFlipState::meridianFlipStatusString(status);
430 case MeridianFlipState::MOUNT_FLIP_NONE:
432 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
433 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
436 case MeridianFlipState::MOUNT_FLIP_PLANNED:
437 if (getMeridianFlipState()->getMeridianFlipStage() > MeridianFlipState::MF_REQUESTED)
440 qCritical(KSTARS_EKOS_CAPTURE) <<
"Accepting meridian flip request while being in stage " <<
441 getMeridianFlipState()->getMeridianFlipStage();
445 getMeridianFlipState()->setResumeGuidingAfterFlip(isGuidingOn());
448 updateMeridianFlipStage(MeridianFlipState::MF_REQUESTED);
450 if (m_CaptureState == CAPTURE_IDLE || m_CaptureState == CAPTURE_ABORTED
451 || m_CaptureState == CAPTURE_COMPLETE || m_CaptureState == CAPTURE_PAUSED)
452 getMeridianFlipState()->updateMFMountState(MeridianFlipState::MOUNT_FLIP_ACCEPTED);
456 case MeridianFlipState::MOUNT_FLIP_RUNNING:
457 updateMeridianFlipStage(MeridianFlipState::MF_INITIATED);
458 setCaptureState(CAPTURE_MERIDIAN_FLIP);
461 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
462 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
471void CameraState::updateMeridianFlipStage(
const MeridianFlipState::MFStage &stage)
474 getMeridianFlipState()->updateMeridianFlipStage(stage);
479 case MeridianFlipState::MF_READY:
482 case MeridianFlipState::MF_INITIATED:
483 emit meridianFlipStarted();
486 case MeridianFlipState::MF_COMPLETED:
489 if (getRefocusState()->isInSequenceFocus())
491 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Resetting HFR Check counter after meridian flip.";
493 getRefocusState()->setInSequenceFocusCounter(0);
497 if ( Options::ditherEnabled() || Options::ditherNoGuiding())
498 resetDitherCounter();
501 if (Options::refocusAfterMeridianFlip() ==
true)
502 getRefocusState()->setRefocusAfterMeridianFlip(
true);
504 KSNotification::event(
QLatin1String(
"MeridianFlipCompleted"),
i18n(
"Meridian flip is successfully completed"),
505 KSNotification::Capture);
507 getMeridianFlipState()->processFlipCompleted();
510 setCaptureState(m_ContinueAction == CAPTURE_CONTINUE_ACTION_NONE ? CAPTURE_IDLE : CAPTURE_PAUSED);
517 emit newMeridianFlipStage(stage);
520bool CameraState::checkMeridianFlipActive()
522 return (getMeridianFlipState()->checkMeridianFlipRunning() ||
523 checkPostMeridianFlipActions() ||
524 checkMeridianFlipReady());
527bool CameraState::checkMeridianFlipReady()
529 if (hasTelescope ==
false)
534 if (m_activeJob && m_activeJob->getFrameType() == FRAME_FLAT
535 && m_activeJob->getCalibrationPreAction() & CAPTURE_PREACTION_WALL)
538 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED)
545 if (m_refocusState->isInSequenceFocus() ||
546 (Options::enforceRefocusEveryN() && m_refocusState->getRefocusEveryNTimerElapsedSec() > 0))
547 emit resetFocusFrame();
550 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_REQUESTED)
551 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_READY);
557bool CameraState::checkPostMeridianFlipActions()
560 if (hasDome && (m_domeState == ISD::Dome::DOME_MOVING_CW || m_domeState == ISD::Dome::DOME_MOVING_CCW))
564 if (m_CaptureState == CAPTURE_ALIGNING || checkAlignmentAfterFlip())
569 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED && m_GuideState != GUIDE_GUIDING
570 && checkGuidingAfterFlip())
576 if (m_CaptureState == CAPTURE_CALIBRATING
577 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
579 if (Options::enforceGuideDeviation() || Options::enforceStartGuiderDrift())
582 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
589bool CameraState::checkGuidingAfterFlip()
592 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
595 if (getMeridianFlipState()->resumeGuidingAfterFlip() ==
false)
597 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
602 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_COMPLETED
603 && getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_GUIDING)
605 appendLogText(
i18n(
"Performing post flip re-calibration and guiding..."));
607 setCaptureState(CAPTURE_CALIBRATING);
609 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_GUIDING);
610 emit guideAfterMeridianFlip();
613 else if (m_CaptureState == CAPTURE_CALIBRATING)
615 if (getGuideState() == GUIDE_CALIBRATION_ERROR || getGuideState() == GUIDE_ABORTED)
618 appendLogText(
i18n(
"Post meridian flip calibration error. Restarting..."));
619 emit guideAfterMeridianFlip();
622 else if (getGuideState() != GUIDE_GUIDING)
634void CameraState::processGuidingFailed()
636 if (m_FocusState > FOCUS_PROGRESS)
638 appendLogText(
i18n(
"Autoguiding stopped. Waiting for autofocus to finish..."));
641 else if (isGuidingOn()
642 && getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_NONE &&
644 ((m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT) ||
645 m_CaptureState == CAPTURE_SUSPENDED || m_CaptureState == CAPTURE_PAUSED))
647 appendLogText(
i18n(
"Autoguiding stopped. Aborting..."));
650 else if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
652 if (increaseAlignmentRetries() >= 3)
654 appendLogText(
i18n(
"Post meridian flip calibration error. Aborting..."));
660void CameraState::updateAdaptiveFocusState(
bool success)
662 m_refocusState->setAdaptiveFocusDone(
true);
666 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus completed successfully";
668 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus failed";
670 m_refocusState->setAutoFocusReady(
true);
672 if (m_activeJob !=
nullptr)
673 m_activeJob->setAutoFocusReady(
true);
675 setFocusState(FOCUS_COMPLETE);
676 emit newLog(
i18n(success ?
"Adaptive focus complete." :
"Adaptive focus failed. Continuing..."));
679void CameraState::updateFocusState(FocusState state)
681 if (state != m_FocusState)
682 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Focus State changed from" <<
683 Ekos::getFocusStatusString(m_FocusState) <<
684 "to" << Ekos::getFocusStatusString(state);
685 setFocusState(state);
688 if (getMeridianFlipState()->checkMeridianFlipRunning())
695 if (!(getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_INITIATED))
699 emit newFocusStatus(state);
700 appendLogText(
i18n(
"Autofocus failed. Aborting exposure..."));
706 m_refocusState->setAutoFocusReady(
true);
708 if (m_activeJob !=
nullptr)
709 m_activeJob->setAutoFocusReady(
true);
711 if (m_refocusState->getFocusHFRInAutofocus())
712 m_refocusState->startRefocusTimer(
true);
715 if (Options::hFRCheckAlgorithm() == HFR_CHECK_MEDIAN_MEASURE ||
716 (m_refocusState->getFocusHFRInAutofocus() && Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS))
718 m_refocusState->addHFRValue(getFocusFilterName());
719 updateHFRThreshold();
721 emit newFocusStatus(state);
727 if (m_activeJob !=
nullptr)
732 if (state == FOCUS_COMPLETE && Options::guidingSettle() > 0 && Options::focusSuspendGuiding()
733 && m_GuideState == GUIDE_GUIDING)
736 appendLogText(
i18n(
"Focus complete. Resuming in %1 seconds...", Options::guidingSettle()));
739 if (m_activeJob !=
nullptr)
740 m_activeJob->setFocusStatus(state);
744 m_activeJob->setFocusStatus(state);
748AutofocusReason CameraState::getAFReason(RefocusState::RefocusReason state,
QString &reasonInfo)
750 AutofocusReason afReason;
754 case RefocusState::REFOCUS_USER_REQUEST:
755 afReason = AutofocusReason::FOCUS_USER_REQUEST;
757 case RefocusState::REFOCUS_TEMPERATURE:
758 afReason = AutofocusReason::FOCUS_TEMPERATURE;
759 reasonInfo =
i18n(
"Limit: %1 °C",
QString::number(Options::maxFocusTemperatureDelta(),
'f', 2));
761 case RefocusState::REFOCUS_TIME_ELAPSED:
762 afReason = AutofocusReason::FOCUS_TIME;
763 reasonInfo =
i18n(
"Limit: %1 mins", Options::refocusEveryN());
765 case RefocusState::REFOCUS_POST_MF:
766 afReason = AutofocusReason::FOCUS_MERIDIAN_FLIP;
769 afReason = AutofocusReason::FOCUS_NONE;
774bool CameraState::startFocusIfRequired()
780 if (m_activeJob ==
nullptr || m_activeJob->getFrameType() != FRAME_LIGHT
781 || m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW)
784 RefocusState::RefocusReason reason = m_refocusState->checkFocusRequired();
787 if (reason == RefocusState::REFOCUS_NONE)
791 m_refocusState->setRefocusAfterMeridianFlip(
false);
797 if (getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_NONE)
799 int targetFilterPosition = m_activeJob->getTargetFilter();
800 if (targetFilterPosition > 0 && targetFilterPosition != getCurrentFilterPosition())
801 emit newFilterPosition(targetFilterPosition);
804 emit abortFastExposure();
805 updateFocusState(FOCUS_PROGRESS);
807 AutofocusReason afReason;
811 case RefocusState::REFOCUS_HFR:
812 m_refocusState->resetInSequenceFocusCounter();
813 emit checkFocus(Options::hFRDeviation());
814 qCDebug(KSTARS_EKOS_CAPTURE) <<
"In-sequence focusing started...";
816 case RefocusState::REFOCUS_ADAPTIVE:
817 m_refocusState->setAdaptiveFocusDone(
true);
818 emit adaptiveFocus();
819 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Adaptive focus started...";
821 case RefocusState::REFOCUS_USER_REQUEST:
822 case RefocusState::REFOCUS_TEMPERATURE:
823 case RefocusState::REFOCUS_TIME_ELAPSED:
824 case RefocusState::REFOCUS_POST_MF:
826 if (m_refocusState->getRefocusEveryNTimerElapsedSec() >= 1800)
827 emit resetFocusFrame();
830 afReason = getAFReason(reason, reasonInfo);
831 emit runAutoFocus(afReason, reasonInfo);
833 m_refocusState->resetInSequenceFocusCounter();
834 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Refocusing started...";
841 setCaptureState(CAPTURE_FOCUSING);
845void CameraState::updateHFRThreshold()
848 if (Options::hFRCheckAlgorithm() == HFR_CHECK_FIXED)
851 QString finalFilter = getFocusFilterName();
852 QList<double> filterHFRList = m_refocusState->getHFRMap()[finalFilter];
855 if (filterHFRList.
empty())
859 if (Options::hFRCheckAlgorithm() == HFR_CHECK_LAST_AUTOFOCUS)
860 value = filterHFRList.
last();
863 int count = filterHFRList.
size();
865 value = (count % 2) ? filterHFRList[count / 2] : (filterHFRList[count / 2 - 1] + filterHFRList[count / 2]) / 2.0;
867 value = filterHFRList[0];
869 value += value * (Options::hFRThresholdPercentage() / 100.0);
870 Options::setHFRDeviation(value);
871 emit newLimitFocusHFR(value);
874QString CameraState::getFocusFilterName()
877 if (m_CurrentFilterPosition > 0)
882 finalFilter = (m_CurrentFocusFilterName ==
"--" ? m_CurrentFilterName : m_CurrentFocusFilterName);
889bool CameraState::checkAlignmentAfterFlip()
892 if (getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_COMPLETED)
894 qCDebug(KSTARS_EKOS_CAPTURE) <<
"checkAlignmentAfterFlip too early, meridian flip stage =" <<
895 getMeridianFlipState()->getMeridianFlipStage();
899 if (getMeridianFlipState()->resumeAlignmentAfterFlip() ==
false)
901 qCDebug(KSTARS_EKOS_CAPTURE) <<
"No alignment after flip required.";
906 if (m_CaptureState < CAPTURE_ALIGNING)
908 appendLogText(
i18n(
"Performing post flip re-alignment..."));
910 resetAlignmentRetries();
911 setCaptureState(CAPTURE_ALIGNING);
913 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_ALIGNING);
921void CameraState::checkGuideDeviationTimeout()
923 if (m_activeJob && m_activeJob->getStatus() == JOB_ABORTED
924 && isGuidingDeviationDetected())
926 appendLogText(
i18n(
"Guide module timed out."));
927 setGuidingDeviationDetected(
false);
930 if (m_CaptureState == CAPTURE_SUSPENDED)
932 setCaptureState(CAPTURE_ABORTED);
937void CameraState::setGuideDeviation(
double deviation_rms)
940 emit newGuiderDrift(deviation_rms);
945 if (m_activeJob ==
nullptr && checkMeridianFlipReady())
949 if (m_CaptureState == CAPTURE_PROGRESS &&
950 getMeridianFlipState()->getMeridianFlipStage() != MeridianFlipState::MF_REQUESTED &&
951 getMeridianFlipState()->checkMeridianFlipRunning() ==
false)
954 if (Options::enforceStartGuiderDrift() ==
false || deviation_rms < Options::startGuideDeviation())
956 setCaptureState(CAPTURE_CALIBRATING);
957 if (Options::enforceStartGuiderDrift())
958 appendLogText(
i18n(
"Initial guiding deviation %1 below limit value of %2 arcsecs",
959 deviationText, Options::startGuideDeviation()));
960 setGuidingDeviationDetected(
false);
961 setStartingCapture(
false);
966 if (isGuidingDeviationDetected() ==
false)
967 appendLogText(
i18n(
"Initial guiding deviation %1 exceeded limit value of %2 arcsecs",
968 deviationText, Options::startGuideDeviation()));
970 setGuidingDeviationDetected(
true);
974 if (checkMeridianFlipReady())
984 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_GUIDING)
987 if (Options::enforceGuideDeviation() ==
false || deviation_rms < Options::guideDeviation())
989 appendLogText(
i18n(
"Post meridian flip calibration completed successfully."));
991 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_NONE);
997 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT
998 && isStartingCapture() && Options::enforceStartGuiderDrift())
1000 setStartingCapture(
false);
1001 if (deviation_rms > Options::startGuideDeviation())
1003 appendLogText(
i18n(
"Guiding deviation at capture startup %1 exceeded limit %2 arcsecs.",
1004 deviationText, Options::startGuideDeviation()));
1005 emit suspendCapture();
1006 setGuidingDeviationDetected(
true);
1010 if (checkMeridianFlipReady())
1011 emit startCapture();
1013 getGuideDeviationTimer().start();
1017 appendLogText(
i18n(
"Guiding deviation at capture startup %1 below limit value of %2 arcsecs",
1018 deviationText, Options::startGuideDeviation()));
1021 if (m_CaptureState != CAPTURE_SUSPENDED)
1025 if ((Options::enforceGuideDeviation() ==
false)
1027 (m_activeJob && (m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW ||
1028 m_activeJob->getExposeLeft() == 0.0 ||
1029 m_activeJob->getFrameType() != FRAME_LIGHT)))
1034 if (m_activeJob && m_activeJob->getStatus() == JOB_BUSY && m_activeJob->getFrameType() == FRAME_LIGHT)
1036 if (deviation_rms <= Options::guideDeviation())
1037 resetSpikesDetected();
1041 if (increaseSpikesDetected() < Options::guideDeviationReps())
1044 appendLogText(
i18n(
"Guiding deviation %1 exceeded limit value of %2 arcsecs for %4 consecutive samples, "
1045 "suspending exposure and waiting for guider up to %3 seconds.",
1046 deviationText, Options::guideDeviation(),
1047 QString(
"%L1").arg(getGuideDeviationTimer().interval() / 1000.0, 0,
'f', 3),
1048 Options::guideDeviationReps()));
1050 emit suspendCapture();
1052 resetSpikesDetected();
1053 setGuidingDeviationDetected(
true);
1057 if (checkMeridianFlipReady())
1058 emit startCapture();
1060 getGuideDeviationTimer().start();
1068 for(
auto &job : allJobs())
1070 if (job->getStatus() == JOB_ABORTED)
1077 if (abortedJob !=
nullptr && isGuidingDeviationDetected())
1079 if (deviation_rms <= Options::startGuideDeviation())
1081 getGuideDeviationTimer().stop();
1084 if (! getCaptureDelayTimer().isActive())
1087 if (m_CaptureState == CAPTURE_SUSPENDED)
1089 const int seqDelay = abortedJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1091 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1092 "resuming exposure.",
1093 deviationText, Options::startGuideDeviation()));
1095 appendLogText(
i18n(
"Guiding deviation %1 is now lower than limit value of %2 arcsecs, "
1096 "resuming exposure in %3 seconds.",
1097 deviationText, Options::startGuideDeviation(), seqDelay / 1000.0));
1099 emit startCapture();
1107 if (getCaptureDelayTimer().isActive())
1108 getCaptureDelayTimer().stop();
1110 appendLogText(
i18n(
"Guiding deviation %1 is still higher than limit value of %2 arcsecs.",
1111 deviationText, Options::startGuideDeviation()));
1116void CameraState::addDownloadTime(
double time)
1118 totalDownloadTime += time;
1122int CameraState::pendingJobCount()
1124 int completedJobs = 0;
1128 if (job->getStatus() == JOB_DONE)
1132 return (allJobs().count() - completedJobs);
1136QString CameraState::jobState(
int id)
1138 if (
id < allJobs().count())
1141 return job->getStatusString();
1148QString CameraState::jobFilterName(
int id)
1150 if (
id < allJobs().count())
1153 return job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1160CCDFrameType CameraState::jobFrameType(
int id)
1162 if (
id < allJobs().count())
1165 return job->getFrameType();
1171int CameraState::jobImageProgress(
int id)
1173 if (
id < allJobs().count())
1176 return job->getCompleted();
1182int CameraState::jobImageCount(
int id)
1184 if (
id < allJobs().count())
1187 return job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1193double CameraState::jobExposureProgress(
int id)
1195 if (
id < allJobs().count())
1198 return job->getExposeLeft();
1204double CameraState::jobExposureDuration(
int id)
1206 if (
id < allJobs().count())
1209 return job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1215double CameraState::progressPercentage()
1217 int totalImageCount = 0;
1218 int totalImageCompleted = 0;
1222 totalImageCount += job->getCoreProperty(SequenceJob::SJ_Count).toInt();
1223 totalImageCompleted += job->getCompleted();
1226 if (totalImageCount != 0)
1227 return ((
static_cast<double>(totalImageCompleted) / totalImageCount) * 100.0);
1232bool CameraState::isActiveJobPreview()
1234 return m_activeJob && m_activeJob->jobType() == SequenceJob::JOBTYPE_PREVIEW;
1237int CameraState::activeJobRemainingTime()
1239 if (m_activeJob ==
nullptr)
1242 return m_activeJob->getJobRemainingTime(averageDownloadTime());
1245int CameraState::overallRemainingTime()
1248 double estimatedDownloadTime = averageDownloadTime();
1251 remaining += job->getJobRemainingTime(estimatedDownloadTime);
1256QString CameraState::sequenceQueueStatus()
1258 if (allJobs().count() == 0)
1264 int idle = 0,
error = 0, complete = 0, aborted = 0, running = 0;
1268 switch (job->getStatus())
1293 if (m_CaptureState == CAPTURE_SUSPENDED)
1302 if (idle == allJobs().count())
1305 if (complete == allJobs().count())
1315 {
"preAction",
static_cast<int>(calibrationPreAction())},
1316 {
"duration", flatFieldDuration()},
1317 {
"az", wallCoord().az().Degrees()},
1318 {
"al", wallCoord().alt().Degrees()},
1319 {
"adu", targetADU()},
1320 {
"tolerance", targetADUTolerance()},
1321 {
"skyflat", skyFlat()},
1327void CameraState::setCalibrationSettings(
const QJsonObject &settings)
1329 const int preAction = settings[
"preAction"].toInt(calibrationPreAction());
1330 const int duration = settings[
"duration"].toInt(flatFieldDuration());
1331 const double az = settings[
"az"].toDouble(wallCoord().az().Degrees());
1332 const double al = settings[
"al"].toDouble(wallCoord().alt().Degrees());
1333 const int adu = settings[
"adu"].toInt(
static_cast<int>(std::round(targetADU())));
1334 const int tolerance = settings[
"tolerance"].toInt(
static_cast<int>(std::round(targetADUTolerance())));
1335 const int skyflat = settings[
"skyflat"].toBool();
1337 setCalibrationPreAction(
static_cast<CalibrationPreActions
>(preAction));
1338 setFlatFieldDuration(
static_cast<FlatFieldDuration
>(duration));
1339 wallCoord().setAz(az);
1340 wallCoord().setAlt(al);
1342 setTargetADUTolerance(tolerance);
1343 setSkyFlat(skyflat);
1346bool CameraState::setDarkFlatExposure(
SequenceJob *job)
1348 const auto darkFlatFilter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
1349 const auto darkFlatBinning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1350 const auto darkFlatADU = job->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1352 for (
auto &oneJob : allJobs())
1354 if (oneJob->getFrameType() != FRAME_FLAT)
1357 const auto filter = oneJob->getCoreProperty(SequenceJob::SJ_Filter).toString();
1360 if (!darkFlatFilter.isEmpty() && darkFlatFilter != filter)
1364 const auto binning = oneJob->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
1365 if (darkFlatBinning != binning)
1369 const auto adu = oneJob->getCoreProperty(SequenceJob::SJ_TargetADU).toInt();
1370 if (job->getFlatFieldDuration() == DURATION_ADU)
1372 if (darkFlatADU != adu)
1377 job->setCoreProperty(SequenceJob::SJ_Exposure, oneJob->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
1384void CameraState::checkSeqBoundary()
1387 if (getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1390 setNextSequenceID(placeholderPath().checkSeqBoundary(*getActiveJob()));
1393bool CameraState::isModelinDSLRInfo(
const QString &model)
1397 return (oneDSLRInfo[
"Model"] == model);
1400 return (pos != m_DSLRInfos.end());
1403void CameraState::setCapturedFramesCount(
const QString &signature, uint16_t count)
1405 m_capturedFramesMap[signature] = count;
1406 qCDebug(KSTARS_EKOS_CAPTURE) <<
1407 QString(
"Client module indicates that storage for '%1' has already %2 captures processed.").
arg(signature).
arg(count);
1409 setIgnoreJobProgress(
false);
1412void CameraState::changeSequenceValue(
int index,
QString key,
QString value)
1415 QJsonObject oneSequence = seqArray[index].toObject();
1416 oneSequence[key] = value;
1417 seqArray.
replace(index, oneSequence);
1418 setSequence(seqArray);
1419 emit sequenceChanged(seqArray);
1422void CameraState::addCapturedFrame(
const QString &signature)
1424 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1425 if (m_capturedFramesMap.end() != frame_item)
1426 frame_item.value()++;
1427 else m_capturedFramesMap[signature] = 1;
1430void CameraState::removeCapturedFrameCount(
const QString &signature, uint16_t count)
1432 CapturedFramesMap::iterator frame_item = m_capturedFramesMap.find(signature);
1433 if (m_capturedFramesMap.end() != frame_item)
1435 if (frame_item.value() <= count)
1437 m_capturedFramesMap.remove(signature);
1440 frame_item.value() = frame_item.value() - count;
1444void CameraState::appendLogText(
const QString &message)
1446 qCInfo(KSTARS_EKOS_CAPTURE()) << message;
1447 emit newLog(message);
1450bool CameraState::isGuidingOn()
1453 if (Options::ditherNoGuiding())
1456 return (m_GuideState == GUIDE_GUIDING ||
1457 m_GuideState == GUIDE_CALIBRATING ||
1458 m_GuideState == GUIDE_CALIBRATION_SUCCESS ||
1459 m_GuideState == GUIDE_DARK ||
1460 m_GuideState == GUIDE_SUBFRAME ||
1461 m_GuideState == GUIDE_STAR_SELECT ||
1462 m_GuideState == GUIDE_REACQUIRE ||
1463 m_GuideState == GUIDE_DITHERING ||
1464 m_GuideState == GUIDE_DITHERING_SUCCESS ||
1465 m_GuideState == GUIDE_DITHERING_ERROR ||
1466 m_GuideState == GUIDE_DITHERING_SETTLE ||
1467 m_GuideState == GUIDE_SUSPENDED
1471bool CameraState::isActivelyGuiding()
1473 return isGuidingOn() && (m_GuideState == GUIDE_GUIDING);
1476void CameraState::setAlignState(AlignState value)
1478 if (value != m_AlignState)
1479 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Align State changed from" << Ekos::getAlignStatusString(
1480 m_AlignState) <<
"to" << Ekos::getAlignStatusString(value);
1481 m_AlignState = value;
1483 getMeridianFlipState()->setResumeAlignmentAfterFlip(
true);
1488 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1490 appendLogText(
i18n(
"Post flip re-alignment completed successfully."));
1491 resetAlignmentRetries();
1493 if (checkGuidingAfterFlip() ==
false)
1496 updateMeridianFlipStage(MeridianFlipState::MF_NONE);
1497 setCaptureState(CAPTURE_WAITING);
1505 if (getMeridianFlipState()->getMeridianFlipStage() == MeridianFlipState::MF_ALIGNING)
1507 if (increaseAlignmentRetries() >= 3)
1509 appendLogText(
i18n(
"Post-flip alignment failed."));
1510 emit abortCapture();
1514 appendLogText(
i18n(
"Post-flip alignment failed. Retrying..."));
1516 updateMeridianFlipStage(MeridianFlipState::MF_COMPLETED);
1526void CameraState::setPrepareComplete(
bool success)
1531 setCaptureState(CAPTURE_PROGRESS);
1532 emit executeActiveJob();
1536 qWarning(KSTARS_EKOS_CAPTURE) <<
"Capture preparation failed, aborting.";
1537 setCaptureState(CAPTURE_ABORTED);
1538 emit abortCapture();
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.
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 QString &fileName)
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)
QString toLocalFile() const const