6#include "cameraprocess.h"
7#include "QtWidgets/qstatusbar.h"
8#include "capturedeviceadaptor.h"
9#include "refocusstate.h"
10#include "sequencejob.h"
11#include "sequencequeue.h"
12#include "ekos/manager.h"
13#include "ekos/auxiliary/darklibrary.h"
14#include "ekos/auxiliary/darkprocessor.h"
15#include "ekos/auxiliary/opticaltrainmanager.h"
16#include "ekos/auxiliary/profilesettings.h"
17#include "ekos/guide/guide.h"
18#include "indi/indilistener.h"
19#include "indi/indirotator.h"
20#include "indi/blobmanager.h"
21#include "indi/indilightbox.h"
22#include "indi/streamwg.h"
23#include "ksmessagebox.h"
27#include "fitsviewer/fitsdata.h"
28#include "fitsviewer/fitstab.h"
30#include "fitsviewer/fitsviewer.h"
32#include "ksnotification.h"
33#include <ekos_capture_debug.h>
35#ifdef HAVE_STELLARSOLVER
36#include "ekos/auxiliary/stellarsolverprofileeditor.h"
41CameraProcess::CameraProcess(QSharedPointer<CameraState> newModuleState,
42 QSharedPointer<CaptureDeviceAdaptor> newDeviceAdaptor) : QObject(KStars::Instance())
44 setObjectName(
"CameraProcess");
45 m_State = newModuleState;
46 m_DeviceAdaptor = newDeviceAdaptor;
49 connect(devices().data(), &CaptureDeviceAdaptor::newCamera,
this, &CameraProcess::selectCamera);
54 state()->downloadProgressTimer().setInterval(100);
58 m_DarkProcessor =
new DarkProcessor(
this);
59 connect(m_DarkProcessor, &DarkProcessor::newLog,
this, &CameraProcess::newLog);
60 connect(m_DarkProcessor, &DarkProcessor::darkFrameCompleted,
this, &CameraProcess::darkFrameCompleted);
65 this, &CameraProcess::scriptFinished);
69 emit newLog(m_CaptureScript.errorString());
75 emit newLog(m_CaptureScript.readAllStandardError());
80 emit newLog(m_CaptureScript.readAllStandardOutput());
87 if (devices()->mount())
88 devices()->mount()->disconnect(state().data());
90 devices()->setMount(device);
92 if (!devices()->mount())
95 devices()->mount()->disconnect(
this);
104 if ((devices()->rotator() == device) && (device !=
nullptr))
108 if (devices()->mount())
110 if (devices()->rotator())
111 devices()->rotator()->disconnect(
this);
114 state()->isInitialized[CAPTURE_ACTION_ROTATOR] =
false;
118 Manager::Instance()->createRotatorController(device);
119 connect(devices().data(), &CaptureDeviceAdaptor::rotatorReverseToggled,
this, &CameraProcess::rotatorReverseToggled,
122 devices()->setRotator(device);
130 if (devices()->dustCap() && devices()->dustCap() == device)
133 devices()->setDustCap(device);
134 state()->setDustCapState(CAP_UNKNOWN);
142 if (devices()->lightBox() == device)
145 devices()->setLightBox(device);
146 state()->setLightBoxLightState(CAP_LIGHT_UNKNOWN);
153 if (devices()->dome() == device)
156 devices()->setDome(device);
163 if (devices()->getActiveCamera() == device)
170 devices()->setActiveCamera(device);
173 if (state()->getCaptureTimeout().isActive() && state()->getCaptureState() ==
CAPTURE_CAPTURING)
181 connect(device, &ISD::Camera::updateVideoWindow,
this, &CameraProcess::updateVideoWindow);
190 if (devices() ==
nullptr || devices()->getActiveCamera() ==
nullptr)
194 enabled = devices()->getActiveCamera()->isStreamingEnabled();
200 if (devices()->getActiveCamera()->isBLOBEnabled() ==
false)
202 if (Options::guiderType() != Guide::GUIDE_INTERNAL)
203 devices()->getActiveCamera()->setBLOBEnabled(
true);
208 KSMessageBox::Instance()->disconnect(
this);
209 devices()->getActiveCamera()->setBLOBEnabled(
true);
210 devices()->getActiveCamera()->setVideoStreamEnabled(
true);
213 KSMessageBox::Instance()->questionYesNo(
i18n(
"Image transfer is disabled for this camera. Would you like to enable it?"),
214 i18n(
"Image Transfer"), 15);
221 devices()->getActiveCamera()->setVideoStreamEnabled(
true);
228 const CaptureState capturestate = state()->getCaptureState();
235 emit newLog(
i18n(
"Sequence resumed."));
238 switch (state()->getContinueAction())
240 case CAPTURE_CONTINUE_ACTION_CAPTURE_COMPLETE:
243 case CAPTURE_CONTINUE_ACTION_NEXT_EXPOSURE:
262 if (state()->allJobs().count() > 0)
265 if (nextJob !=
nullptr)
271 emit newLog(
i18n(
"No pending jobs found. Please add a job to the sequence queue."));
285 emit newLog(
i18n(
"No new job created."));
289 switch (newJob->jobType())
291 case SequenceJob::JOBTYPE_BATCH:
294 case SequenceJob::JOBTYPE_PREVIEW:
295 state()->setActiveJob(newJob);
306 if (state()->getFocusState() >= FOCUS_PROGRESS)
308 emit newLog(
i18n(
"Cannot capture while focus module is busy."));
310 else if (activeJob() ==
nullptr)
312 if (loop && !state()->isLooping())
314 state()->setLooping(
true);
315 emit newLog(
i18n(
"Starting framing..."));
318 emit createJob(SequenceJob::JOBTYPE_PREVIEW);
331 m_CaptureOperationsTimer.invalidate();
333 state()->resetAlignmentRetries();
337 state()->getCaptureTimeout().stop();
338 state()->getCaptureDelayTimer().stop();
339 if (activeJob() !=
nullptr)
341 if (activeJob()->getStatus() == JOB_BUSY)
347 stopText =
i18n(
"CCD capture suspended");
348 resetJobStatus(JOB_BUSY);
352 stopText =
i18n(
"CCD capture complete");
353 resetJobStatus(JOB_DONE);
357 stopText = state()->isLooping() ?
i18n(
"Framing stopped") :
i18n(
"CCD capture stopped");
358 resetJobStatus(JOB_ABORTED);
362 stopText =
i18n(
"CCD capture stopped");
363 resetJobStatus(JOB_IDLE);
366 emit captureAborted(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
367 KSNotification::event(
QLatin1String(
"CaptureFailed"), stopText, KSNotification::Capture, KSNotification::Alert);
368 emit newLog(stopText);
375 activeJob()->abort();
376 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
378 int index = state()->allJobs().indexOf(activeJob());
379 state()->changeSequenceValue(index,
"Status",
"Aborted");
380 emit updateJobTable(activeJob());
385 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
389 else if (activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
395 state()->allJobs().removeOne(activeJob());
397 state()->setActiveJob(
nullptr);
405 state()->setCaptureState(targetState);
407 state()->setLooping(
false);
408 state()->setBusy(
false);
410 state()->getCaptureDelayTimer().stop();
412 state()->setActiveJob(
nullptr);
415 if (devices()->lightBox() && state()->lightBoxLightEnabled())
417 state()->setLightBoxLightEnabled(
false);
418 devices()->lightBox()->setLightEnabled(
false);
425 if (devices()->getActiveCamera() && devices()->getActiveChip()
426 && devices()->getActiveCamera()->isFastExposureEnabled())
427 devices()->getActiveChip()->abortExposure();
430 emit captureStopped();
435 if (state()->isCaptureRunning() ==
false)
440 emit newLog(
i18n(
"Pausing only possible while frame capture is running."));
441 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Pause button pressed while not capturing.";
445 state()->setContinueAction(CAPTURE_CONTINUE_ACTION_NONE);
447 emit newLog(
i18n(
"Sequence shall be paused after current exposure is complete."));
452 state()->initCapturePreparation();
458 if (activeCamera() ==
nullptr || activeCamera()->isConnected() ==
false)
460 emit newLog(
i18n(
"No camera detected. Check train configuration and connection settings."));
461 activeJob()->abort();
465 state()->setActiveJob(job);
469 if (job->jobType() == SequenceJob::JOBTYPE_PREVIEW && Options::useFITSViewer() ==
false
470 && Options::useSummaryPreview() ==
false)
475 KSMessageBox::Instance()->disconnect(
this);
476 Options::setUseFITSViewer(
true);
482 KSMessageBox::Instance()->disconnect(
this);
483 activeJob()->abort();
485 KSMessageBox::Instance()->questionYesNo(
i18n(
"No view available for previews. Enable FITS viewer?"),
486 i18n(
"Display preview"), 15);
491 if (state()->isLooping() ==
false)
492 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Preparing capture job" << job->getSignature() <<
"for execution.";
494 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
498 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
499 state()->setNextSequenceID(1);
505 QString signature = activeJob()->getSignature();
511 state()->checkSeqBoundary();
531 int count = state()->capturedFramesCount(signature);
536 for (
auto &a_job : state()->allJobs())
537 if (a_job == activeJob())
539 else if (a_job->getSignature() == activeJob()->getSignature())
540 count -= a_job->getCompleted();
543 updatedCaptureCompleted(count);
547 else if (state()->hasCapturedFramesMap())
550 updatedCaptureCompleted(0);
556 else if (state()->ignoreJobProgress()
557 && activeJob()->getJobProgressIgnored() ==
false)
559 activeJob()->setJobProgressIgnored(
true);
560 updatedCaptureCompleted(0);
565 if (activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
566 activeJob()->getCompleted())
568 updatedCaptureCompleted(activeJob()->getCoreProperty(
569 SequenceJob::SJ_Count).toInt());
570 emit newLog(
i18n(
"Job requires %1-second %2 images, has already %3/%4 captures and does not need to run.",
571 QString(
"%L1").arg(job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(), 0,
'f', 3),
572 job->getCoreProperty(SequenceJob::SJ_Filter).toString(),
573 activeJob()->getCompleted(),
574 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
582 if (activeJob()->getFrameType() != FRAME_VIDEO)
585 emit newLog(
i18n(
"Job requires %1-second %2 images, has %3/%4 frames captured and will be processed.",
586 QString(
"%L1").arg(job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(), 0,
'f', 3),
587 job->getCoreProperty(SequenceJob::SJ_Filter).toString(),
588 activeJob()->getCompleted(),
589 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
594 activeCamera()->setNextSequenceID(state()->nextSequenceID());
598 emit newLog(
i18n(
"Job requires %1 x %2-second %3 video and will be processed.",
599 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt(),
600 QString(
"%L1").arg(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(), 0,
'f', 3),
601 activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString()));
606 if (activeCamera()->isBLOBEnabled() ==
false)
611 if (Options::guiderType() != Guide::GUIDE_INTERNAL)
613 activeCamera()->setBLOBEnabled(
true);
619 KSMessageBox::Instance()->disconnect(
this);
620 activeCamera()->setBLOBEnabled(
true);
626 KSMessageBox::Instance()->disconnect(
this);
627 activeCamera()->setBLOBEnabled(
true);
628 state()->setBusy(
false);
631 KSMessageBox::Instance()->questionYesNo(
i18n(
"Image transfer is disabled for this camera. Would you like to enable it?"),
632 i18n(
"Image Transfer"), 15);
638 emit jobPrepared(job);
646 if (activeJob() ==
nullptr)
648 qWarning(KSTARS_EKOS_CAPTURE) <<
"prepareActiveJobStage1 with null activeJob().";
663 if (activeJob() ==
nullptr)
665 qWarning(KSTARS_EKOS_CAPTURE) <<
"prepareActiveJobStage2 with null activeJob().";
668 emit newImage(activeJob(), state()->imageData());
689 if (activeJob() ==
nullptr)
691 qWarning(KSTARS_EKOS_CAPTURE) <<
"executeJob with null activeJob().";
696 if (!activeCamera() || !devices()->getActiveChip())
700 qWarning(KSTARS_EKOS_CAPTURE) <<
"Job execution failed, no active" << (activeCamera() ?
"chip" :
"camera");
704 qDebug(KSTARS_EKOS_CAPTURE) <<
"Executing the sequence job.";
706 if (Options::defaultObserver().isEmpty() ==
false)
708 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetName) !=
"")
709 FITSHeaders.
append(
FITSData::Record(
"Object", activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString(),
714 activeCamera()->setFITSHeaders(FITSHeaders);
717 state()->setBusy(
true);
718 state()->setUseGuideHead((devices()->getActiveChip()->getType() == ISD::CameraChip::PRIMARY_CCD) ?
721 emit syncGUIToJob(activeJob());
725 if (activeJob()->jobType() == SequenceJob::JOBTYPE_DARKFLAT)
728 if (state()->setDarkFlatExposure(activeJob())
729 && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
731 auto placeholderPath = PlaceholderPath();
733 placeholderPath.processJobInfo(activeJob().get());
734 state()->setNextSequenceID(1);
739 m_CaptureOperationsTimer.invalidate();
746 if (activeJob() ==
nullptr)
748 qWarning(KSTARS_EKOS_CAPTURE) <<
"preparePreCaptureActions with null activeJob().";
753 state()->setBusy(
true);
756 activeJob()->setCoreProperty(SequenceJob::SJ_GuiderActive,
757 state()->isActivelyGuiding());
760 activeJob()->prepareCapture();
763 emit jobExecutionPreparationStarted();
768 state()->setOpticalTrain(name);
770 auto mount = OpticalTrainManager::Instance()->getMount(name);
773 auto scope = OpticalTrainManager::Instance()->getScope(name);
776 auto camera = OpticalTrainManager::Instance()->getCamera(name);
779 auto filterWheel = OpticalTrainManager::Instance()->getFilterWheel(name);
782 auto rotator = OpticalTrainManager::Instance()->getRotator(name);
785 auto dustcap = OpticalTrainManager::Instance()->getDustCap(name);
788 auto lightbox = OpticalTrainManager::Instance()->getLightBox(name);
799 if (
checkPausing(CAPTURE_CONTINUE_ACTION_NEXT_EXPOSURE) ==
true)
803 if (state()->checkMeridianFlipActive())
809 state()->getGuideState() == GUIDE_GUIDING &&
810 Options::enforceStartGuiderDrift())
814 if ((state()->getCaptureState() ==
CAPTURE_DITHERING && state()->getDitheringState() != IPS_OK)
815 || state()->checkDithering())
823 if (state()->checkFocusRunning() || state()->startFocusIfRequired())
828 if (state()->getGuideState() == GUIDE_SUSPENDED && activeJob()->getFrameType() == FRAME_LIGHT)
830 emit newLog(
i18n(
"Autoguiding resumed."));
831 emit resumeGuiding();
849 state()->getCaptureTimeout().start(
static_cast<int>(activeJob()->getCoreProperty(
850 SequenceJob::SJ_Exposure).toDouble()) * 1000 +
851 CAPTURE_TIMEOUT_THRESHOLD);
853 state()->imageCountDown().setHMS(0, 0, 0);
854 double ms_left = std::ceil(activeJob()->getExposeLeft() * 1000.0);
855 state()->imageCountDownAddMSecs(
int(ms_left));
856 state()->setLastRemainingFrameTimeMS(ms_left);
857 state()->sequenceCountDown().setHMS(0, 0, 0);
858 state()->sequenceCountDownAddMSecs(activeJob()->getJobRemainingTime(state()->averageDownloadTime()) * 1000);
861 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
863 auto index = state()->allJobs().indexOf(activeJob());
864 if (index >= 0 && index < state()->getSequence().count())
865 state()->changeSequenceValue(index,
"Status",
"In Progress");
867 emit updateJobTable(activeJob());
869 emit captureRunning();
873 case CAPTURE_FRAME_ERROR:
874 emit newLog(
i18n(
"Failed to set sub frame."));
878 case CAPTURE_BIN_ERROR:
879 emit newLog((
i18n(
"Failed to set binning.")));
883 case CAPTURE_FOCUS_ERROR:
884 emit newLog((
i18n(
"Cannot capture while focus module is busy.")));
895 if (started == IPS_BUSY)
901IPState CameraProcess::captureImageWithDelay()
903 auto theJob = activeJob();
905 if (theJob ==
nullptr)
908 const int seqDelay = theJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
914 state()->getCaptureDelayTimer().start(seqDelay);
923 auto theJob = activeJob();
925 if (theJob ==
nullptr)
929 if (activeJob()->getFrameType() == FRAME_LIGHT)
932 if (pending != IPS_OK)
937 return captureImageWithDelay();
945 if (
checkPausing(CAPTURE_CONTINUE_ACTION_CAPTURE_COMPLETE) ==
true)
956 else if (activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
957 activeJob()->getCompleted())
967 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload() &&
968 state()->getMeridianFlipState()->checkMeridianFlipActive() ==
false)
970 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Resuming guiding...";
971 emit resumeGuiding();
975 if (activeCamera()->isFastExposureEnabled())
977 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
979 state()->checkSeqBoundary();
980 activeCamera()->setNextSequenceID(state()->nextSequenceID());
990 if (activeCamera()->isFastExposureEnabled())
992 state()->setRememberFastExposure(
true);
993 activeCamera()->setFastExposureEnabled(
false);
1001 if (activeCamera()->isFastExposureEnabled())
1004 activeJob()->getFrameType() == FRAME_LIGHT &&
1013 state()->setRememberFastExposure(
true);
1014 activeCamera()->setFastExposureEnabled(
false);
1017 m_CaptureOperationsTimer.invalidate();
1031 if (data && activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1033 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW
1034 && activeJob()->getCalibrationStage() != SequenceJobState::CAL_CALIBRATION)
1036 if (state()->generateFilename(extension, &filename) && activeCamera()->saveCurrentImage(filename))
1038 data->setFilename(filename);
1044 qCWarning(KSTARS_EKOS_CAPTURE) <<
"Saving current image failed!";
1047 KSMessageBox::Instance()->disconnect(
this);
1049 KSMessageBox::Instance()->error(
i18n(
"Failed writing image to %1\nPlease check folder, filename & permissions.",
1051 i18n(
"Image Write Failed"), 30);
1066 state()->setImageData(data);
1067 blobInfo =
QString(
"{Device: %1 Property: %2 Element: %3 Chip: %4}").
arg(data->property(
"device").toString())
1068 .
arg(data->property(
"blobVector").toString())
1069 .
arg(data->property(
"blobElement").toString())
1070 .
arg(data->property(
"chip").toInt());
1073 state()->imageData().reset();
1080 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring received FITS as active job is null.";
1082 emit processingFITSfinished(
false);
1086 if (state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1089 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as meridian flip stage is" <<
1090 state()->getMeridianFlipState()->getMeridianFlipStage();
1091 emit processingFITSfinished(
false);
1095 const SequenceJob::SequenceJobType currentJobType = activeJob()->jobType();
1097 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1102 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as current capture state is not active" <<
1103 state()->getCaptureState();
1105 emit processingFITSfinished(
false);
1111 tChip = activeCamera()->getChip(
static_cast<ISD::CameraChip::ChipType
>(data->property(
"chip").toInt()));
1112 if (tChip != devices()->getActiveChip())
1114 if (state()->getGuideState() == GUIDE_IDLE)
1115 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as it does not correspond to the target chip"
1116 << devices()->getActiveChip()->getType();
1118 emit processingFITSfinished(
false);
1123 if (devices()->getActiveChip()->getCaptureMode() == FITS_FOCUS ||
1124 devices()->getActiveChip()->getCaptureMode() == FITS_GUIDE)
1126 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as it has the wrong capture mode" <<
1127 devices()->getActiveChip()->getCaptureMode();
1129 emit processingFITSfinished(
false);
1134 if (data && data->property(
"device").toString() != activeCamera()->getDeviceName())
1136 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as the blob device name does not equal active camera"
1137 << activeCamera()->getDeviceName();
1139 emit processingFITSfinished(
false);
1143 if (currentJobType == SequenceJob::JOBTYPE_PREVIEW)
1146 if (checkSavingReceivedImage(data, extension, filename))
1148 FITSMode captureMode = tChip->getCaptureMode();
1149 FITSScale captureFilter = tChip->getCaptureFilter();
1150 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
1155 if (data && Options::autoDark() && job->jobType() == SequenceJob::JOBTYPE_PREVIEW && state()->useGuideHead() ==
false)
1157 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1160 m_DarkProcessor.data()->denoise(trainID.
toUInt(),
1161 devices()->getActiveChip(),
1162 state()->imageData(),
1163 job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(),
1164 job->getCoreProperty(SequenceJob::SJ_ROI).toRect().x(),
1165 job->getCoreProperty(SequenceJob::SJ_ROI).toRect().y());
1168 qWarning(KSTARS_EKOS_CAPTURE) <<
"Invalid train ID for darks substraction:" << trainID.
toUInt();
1171 if (currentJobType == SequenceJob::JOBTYPE_PREVIEW)
1189 if (activeCamera()->isFastExposureEnabled() ==
false && state()->isLooping() ==
false)
1191 disconnect(activeCamera(), &ISD::Camera::newExposureValue,
this,
1193 DarkLibrary::Instance()->disconnect(
this);
1197 bool alreadySaved =
false;
1198 switch (thejob->getFrameType())
1202 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1206 if (thejob->getFlatFieldDuration() == DURATION_ADU
1207 && thejob->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > 0)
1209 if (
checkFlatCalibration(state()->imageData(), state()->exposureRange().min, state()->exposureRange().max) ==
false)
1214 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1216 if (checkSavingReceivedImage(data, extension, filename))
1217 alreadySaved =
true;
1221 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1230 qWarning(KSTARS_EKOS_CAPTURE) <<
"Job completed with frametype NONE!";
1237 if (thejob->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION_COMPLETE)
1238 thejob->setCalibrationStage(SequenceJobState::CAL_CAPTURING);
1240 if (activeJob() && currentJobType != SequenceJob::JOBTYPE_PREVIEW &&
1241 activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1244 if (alreadySaved || checkSavingReceivedImage(data, extension, filename))
1253 emit newImage(thejob, state()->imageData());
1260 if (currentJobType != SequenceJob::JOBTYPE_PREVIEW)
1264 emit processingFITSfinished(
true);
1269 ISD::CameraChip * tChip = activeCamera()->getChip(
static_cast<ISD::CameraChip::ChipType
>(data->property(
"chip").toInt()));
1271 updateFITSViewer(data, tChip->getCaptureMode(), tChip->getCaptureFilter(),
"", data->property(
"device").toString());
1276 emit newLog(
i18n(
"Remote image saved to %1", file));
1279 if (activeCamera() && activeCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1289 if (activeJob() ==
nullptr)
1291 qCWarning(KSTARS_EKOS_CAPTURE) <<
"Processing pre capture calibration without active job, state = " <<
1292 getCaptureStatusString(state()->getCaptureState());
1299 if (activeJob()->getFrameType() != FRAME_LIGHT
1300 && state()->getGuideState() == GUIDE_GUIDING)
1302 emit newLog(
i18n(
"Autoguiding suspended."));
1303 emit suspendGuiding();
1307 switch (activeJob()->getFrameType())
1330 if (state()->isBusy() ==
false)
1332 emit newLog(
i18n(
"Warning: Calibration process was prematurely terminated."));
1338 if (rc == IPS_ALERT)
1340 else if (rc == IPS_BUSY)
1346 captureImageWithDelay();
1351 if (activeJob() ==
nullptr)
1353 qWarning(KSTARS_EKOS_CAPTURE) <<
"procesJobCompletionStage1 with null activeJob().";
1367 if (activeJob() ==
nullptr)
1369 qWarning(KSTARS_EKOS_CAPTURE) <<
"procesJobCompletionStage2 with null activeJob().";
1373 activeJob()->done();
1375 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1377 int index = state()->allJobs().indexOf(activeJob());
1378 QJsonArray seqArray = state()->getSequence();
1379 QJsonObject oneSequence = seqArray[index].toObject();
1380 oneSequence[
"Status"] =
"Complete";
1381 seqArray.
replace(index, oneSequence);
1382 state()->setSequence(seqArray);
1383 emit sequenceChanged(seqArray);
1384 emit updateJobTable(activeJob());
1398 KSNotification::event(
QLatin1String(
"CaptureSuccessful"),
i18n(
"CCD capture sequence completed"),
1399 KSNotification::Capture);
1405 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1406 emit resumeGuiding();
1415 for (
auto &oneJob : state()->allJobs())
1417 if (oneJob->getStatus() == JOB_IDLE || oneJob->getStatus() == JOB_ABORTED)
1431 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload() &&
1432 state()->getMeridianFlipState()->checkMeridianFlipActive() ==
false)
1434 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Resuming guiding...";
1435 emit resumeGuiding();
1442 qCDebug(KSTARS_EKOS_CAPTURE) <<
"All capture jobs complete.";
1449 if (activeJob() ==
nullptr)
1453 if (!activeCamera() || !activeCamera()->isConnected())
1455 emit newLog(
i18n(
"Error: Lost connection to CCD."));
1460 state()->getCaptureTimeout().stop();
1461 state()->getCaptureDelayTimer().stop();
1462 if (activeCamera()->isFastExposureEnabled())
1464 int remaining = state()->isLooping() ? 100000 : (activeJob()->getCoreProperty(
1465 SequenceJob::SJ_Count).toInt() -
1466 activeJob()->getCompleted());
1468 activeCamera()->setFastCount(
static_cast<uint
>(remaining));
1473 if (activeJob()->getFrameType() == FRAME_FLAT)
1476 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW
1477 && activeJob()->getFlatFieldDuration() == DURATION_ADU &&
1478 activeJob()->getCalibrationStage() == SequenceJobState::CAL_NONE)
1480 if (activeCamera()->getEncodingFormat() !=
"FITS" &&
1481 activeCamera()->getEncodingFormat() !=
"XISF")
1483 emit newLog(
i18n(
"Cannot calculate ADU levels in non-FITS images."));
1488 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
1493 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1495 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
1496 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
1501 if (activeCamera()->getUploadMode() != activeJob()->getUploadMode())
1502 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1505 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1507 state()->checkSeqBoundary();
1508 activeCamera()->setNextSequenceID(state()->nextSequenceID());
1512 if (state()->isRememberFastExposure())
1514 state()->setRememberFastExposure(
false);
1515 activeCamera()->setFastExposureEnabled(
true);
1518 if (state()->frameSettings().contains(devices()->getActiveChip()))
1520 const auto roi = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect();
1521 QVariantMap settings;
1522 settings[
"x"] = roi.x();
1523 settings[
"y"] = roi.y();
1524 settings[
"w"] = roi.width();
1525 settings[
"h"] = roi.height();
1526 settings[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1527 settings[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1529 state()->frameSettings()[devices()->getActiveChip()] = settings;
1533 activeCamera()->setEncodingFormat(activeJob()->getCoreProperty(
1534 SequenceJob::SJ_Encoding).toString());
1536 state()->setStartingCapture(
true);
1537 state()->placeholderPath().setGenerateFilenameSettings(*activeJob());
1540 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
1542 auto remoteUpload = state()->placeholderPath().generateSequenceFilename(*activeJob(),
false,
true, 1,
"",
"",
false,
1548#if defined(Q_OS_WIN)
1551 int lastForwardSlash = remoteUpload.lastIndexOf(
'/');
1552 if (lastForwardSlash > lastSeparator)
1553 lastSeparator = lastForwardSlash;
1556 auto remoteDirectory = remoteUpload.mid(0, lastSeparator);
1557 auto remoteFilename = remoteUpload.mid(lastSeparator + 1);
1558 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatDirectory, remoteDirectory);
1559 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatFilename, remoteFilename);
1565 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(),
1566 activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION ? FITS_CALIBRATE :
1570 if (state()->isRememberFastExposure())
1572 state()->setRememberFastExposure(
false);
1573 activeCamera()->setFastExposureEnabled(
true);
1576 emit captureTarget(activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString());
1577 emit captureImageStarted();
1582 devices()->setActiveChip(state()->useGuideHead() ?
1583 devices()->getActiveCamera()->getChip(
1584 ISD::CameraChip::GUIDE_CCD) :
1585 devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1586 devices()->getActiveChip()->resetFrame();
1587 emit updateFrameProperties(1);
1593 if (state()->checkCapturing() ==
false)
1596 if (devices()->getActiveChip() != tChip ||
1597 devices()->getActiveChip()->getCaptureMode() != FITS_NORMAL
1598 || state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1601 double deltaMS = std::ceil(1000.0 * value - state()->lastRemainingFrameTimeMS());
1602 emit updateCaptureCountDown(
int(deltaMS));
1603 state()->setLastRemainingFrameTimeMS(state()->lastRemainingFrameTimeMS() + deltaMS);
1607 activeJob()->setExposeLeft(value);
1609 emit newExposureProgress(activeJob());
1612 if (activeJob() && ipstate == IPS_ALERT)
1614 int retries = activeJob()->getCaptureRetires() + 1;
1616 activeJob()->setCaptureRetires(retries);
1618 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
1622 activeJob()->abort();
1626 emit newLog((
i18n(
"Restarting capture attempt #%1", retries)));
1628 state()->setNextSequenceID(1);
1634 if (activeJob() !=
nullptr && ipstate == IPS_OK)
1636 activeJob()->setCaptureRetires(0);
1637 activeJob()->setExposeLeft(0);
1639 if (devices()->getActiveCamera()
1640 && devices()->getActiveCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1642 if (activeJob()->getStatus() == JOB_BUSY)
1644 emit processingFITSfinished(
false);
1649 if (state()->getGuideState() == GUIDE_GUIDING && Options::guiderType() == 0
1650 && state()->suspendGuidingOnDownload())
1652 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Autoguiding suspended until primary CCD chip completes downloading...";
1653 emit suspendGuiding();
1656 emit downloadingFrame();
1659 state()->downloadTimer().start();
1660 state()->downloadProgressTimer().start();
1668 double downloadTimeLeft = state()->averageDownloadTime() - state()->downloadTimer().elapsed() /
1670 if(downloadTimeLeft >= 0)
1672 state()->imageCountDown().setHMS(0, 0, 0);
1673 state()->imageCountDownAddMSecs(
int(std::ceil(downloadTimeLeft * 1000)));
1674 emit newDownloadProgress(downloadTimeLeft);
1682 emit newImage(activeJob(), imageData);
1684 if (activeCamera()->isFastExposureEnabled() ==
false)
1686 const int seqDelay = activeJob()->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1692 if (activeJob() !=
nullptr)
1693 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1696 else if (activeJob() !=
nullptr)
1697 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1707 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE
1708 && state()->downloadTimer().isValid())
1711 double currentDownloadTime = state()->downloadTimer().elapsed() / 1000.0;
1712 state()->addDownloadTime(currentDownloadTime);
1714 state()->downloadTimer().invalidate();
1718 emit newLog(
i18n(
"Download Time: %1 s, New Download Time Estimate: %2 s.", dLTimeString, estimatedTimeString));
1725 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1728 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1730 state()->setActiveJob(
nullptr);
1732 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1733 emit resumeGuiding();
1744 state()->getCaptureTimeout().stop();
1745 state()->setCaptureTimeoutCounter(0);
1747 state()->downloadProgressTimer().stop();
1750 if (state()->isLooping())
1764 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW
1765 || activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
1769 updatedCaptureCompleted(activeJob()->getCompleted() + 1);
1771 state()->getRefocusState()->decreaseInSequenceFocusCounter();
1773 state()->getRefocusState()->setAdaptiveFocusDone(
false);
1777 if (state()->getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_FLIPPING)
1778 state()->decreaseDitherCounter();
1781 state()->addCapturedFrame(activeJob()->getSignature());
1784 emit newLog(
i18n(
"Received image %1 out of %2.", activeJob()->getCompleted(),
1785 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
1788 m_CaptureOperationsTimer.invalidate();
1793 double hfr = -1, eccentricity = -1;
1794 int numStars = -1, median = -1;
1799 if (Options::autoHFR() && imageData && !imageData->areStarsSearched() && imageData->getRecordValue(
"FRAME", frameType)
1800 && frameType.
toString() ==
"Light")
1802#ifdef HAVE_STELLARSOLVER
1805 QVariantMap extractionSettings;
1806 extractionSettings[
"optionsProfileIndex"] = Options::hFROptionsProfile();
1807 extractionSettings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::HFRProfiles);
1808 imageData->setSourceExtractorSettings(extractionSettings);
1813 hfr = imageData->getHFR(HFR_AVERAGE);
1814 numStars = imageData->getSkyBackground().starsDetected;
1815 median = imageData->getMedian();
1816 eccentricity = imageData->getEccentricity();
1817 filename = imageData->filename();
1820 if (state()->isLooping() ==
false && activeJob() !=
nullptr && activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1821 emit newLog(
i18n(
"Captured %1", filename));
1823 auto remainingPlaceholders = PlaceholderPath::remainingPlaceholders(filename);
1824 if (remainingPlaceholders.size() > 0)
1827 i18n(
"WARNING: remaining and potentially unknown placeholders %1 in %2",
1828 remainingPlaceholders.join(
", "), filename));
1834 QVariantMap metadata;
1835 metadata[
"filename"] = filename;
1836 metadata[
"type"] = activeJob()->getFrameType();
1837 metadata[
"exposure"] = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1838 metadata[
"filter"] = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
1839 metadata[
"width"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().width();
1840 metadata[
"height"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().height();
1841 metadata[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1842 metadata[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1843 metadata[
"hfr"] = hfr;
1844 metadata[
"starCount"] = numStars;
1845 metadata[
"median"] = median;
1846 metadata[
"eccentricity"] = eccentricity;
1847 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Captured frame metadata: filename =" << filename <<
", type =" << metadata[
"type"].toInt()
1848 <<
"exposure =" << metadata[
"exposure"].toDouble() <<
"filter =" << metadata[
"filter"].toString() <<
"width =" <<
1849 metadata[
"width"].toInt() <<
"height =" << metadata[
"height"].toInt() <<
"hfr =" << metadata[
"hfr"].toDouble() <<
1850 "starCount =" << metadata[
"starCount"].toInt() <<
"median =" << metadata[
"median"].toInt() <<
"eccentricity =" <<
1851 metadata[
"eccentricity"].toDouble();
1853 emit captureComplete(metadata);
1862 const QString captureScript = activeJob()->getScript(scriptType);
1863 if (captureScript.isEmpty() ==
false && precond)
1865 state()->setCaptureScriptType(scriptType);
1868 emit newLog(
i18n(
"Executing capture script %1", captureScript));
1880 switch (state()->captureScriptType())
1883 emit newLog(
i18n(
"Pre capture script finished with code %1.", exitCode));
1884 if (activeJob() && activeJob()->getStatus() == JOB_IDLE)
1888 m_CaptureOperationsTimer.invalidate();
1894 emit newLog(
i18n(
"Post capture script finished with code %1.", exitCode));
1897 if (activeJob() ==
nullptr
1898 || activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
1899 activeJob()->getCompleted())
1904 else if (state()->checkMeridianFlipReady())
1906 emit newLog(
i18n(
"Processing meridian flip..."));
1916 emit newLog(
i18n(
"Pre job script finished with code %1.", exitCode));
1921 emit newLog(
i18n(
"Post job script finished with code %1.", exitCode));
1935 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1936 if (activeCamera() && trainID.
isValid())
1938 if (activeCamera() && activeCamera()->getDeviceName() == name)
1941 emit refreshCamera(
true);
1944 emit refreshCamera(
false);
1955 if (!activeCamera())
1961 devices()->setActiveChip(
nullptr);
1964 if (activeCamera()->getDeviceName().contains(
"Guider"))
1966 state()->setUseGuideHead(
true);
1967 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::GUIDE_CCD));
1970 if (devices()->getActiveChip() ==
nullptr)
1972 state()->setUseGuideHead(
false);
1973 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1976 emit refreshCameraSettings();
1981 auto pos = std::find_if(state()->DSLRInfos().begin(),
1984 return (oneDSLRInfo[
"Model"] == model);
1988 if (pos != state()->DSLRInfos().end())
1991 devices()->getActiveChip()->setImageInfo(camera[
"Width"].toInt(),
1992 camera[
"Height"].toInt(),
1993 camera[
"PixelW"].toDouble(),
1994 camera[
"PixelH"].toDouble(),
2001 if (activeCamera() && activeCamera()->getDeviceName() == camera)
2004 auto rememberState = state()->getCaptureState();
2007 state()->setCaptureState(rememberState);
2010 state()->setCaptureTimeoutCounter(0);
2014 devices()->setActiveChip(devices()->getActiveChip());
2028 auto name = device->getDeviceName();
2029 device->disconnect(
this);
2032 if (devices()->mount() && devices()->mount()->getDeviceName() == device->getDeviceName())
2034 devices()->mount()->disconnect(
this);
2035 devices()->setMount(
nullptr);
2036 if (activeJob() !=
nullptr)
2037 activeJob()->addMount(
nullptr);
2041 if (devices()->dome() && devices()->dome()->getDeviceName() == device->getDeviceName())
2043 devices()->dome()->disconnect(
this);
2044 devices()->setDome(
nullptr);
2048 if (devices()->rotator() && devices()->rotator()->getDeviceName() == device->getDeviceName())
2050 devices()->rotator()->disconnect(
this);
2051 devices()->setRotator(
nullptr);
2055 if (devices()->dustCap() && devices()->dustCap()->getDeviceName() == device->getDeviceName())
2057 devices()->dustCap()->disconnect(
this);
2058 devices()->setDustCap(
nullptr);
2059 state()->hasDustCap =
false;
2060 state()->setDustCapState(CAP_UNKNOWN);
2064 if (devices()->lightBox() && devices()->lightBox()->getDeviceName() == device->getDeviceName())
2066 devices()->lightBox()->disconnect(
this);
2067 devices()->setLightBox(
nullptr);
2068 state()->hasLightBox =
false;
2069 state()->setLightBoxLightState(CAP_LIGHT_UNKNOWN);
2073 if (activeCamera() && activeCamera()->getDeviceName() == name)
2075 activeCamera()->disconnect(
this);
2076 devices()->setActiveCamera(
nullptr);
2077 devices()->setActiveChip(
nullptr);
2080 if (INDIListener::findDevice(name, generic))
2081 DarkLibrary::Instance()->removeDevice(generic);
2087 if (devices()->filterWheel() && devices()->filterWheel()->getDeviceName() == name)
2089 devices()->filterWheel()->disconnect(
this);
2090 devices()->setFilterWheel(
nullptr);
2094 emit refreshFilterSettings();
2101 state()->setCaptureTimeoutCounter(state()->captureTimeoutCounter() + 1);
2103 if (state()->deviceRestartCounter() >= 3)
2105 state()->setCaptureTimeoutCounter(0);
2106 state()->setDeviceRestartCounter(0);
2107 emit newLog(
i18n(
"Exposure timeout. Aborting..."));
2112 if (state()->captureTimeoutCounter() > 3 && activeCamera())
2114 emit newLog(
i18n(
"Exposure timeout. More than 3 have been detected, will restart driver."));
2115 QString camera = activeCamera()->getDeviceName();
2116 QString fw = (devices()->filterWheel() !=
nullptr) ?
2117 devices()->filterWheel()->getDeviceName() :
"";
2118 emit driverTimedout(camera);
2121 state()->setDeviceRestartCounter(state()->deviceRestartCounter() + 1);
2129 if (activeCamera() && activeJob())
2132 emit newLog(
i18n(
"Exposure timeout. Restarting exposure..."));
2133 activeCamera()->setEncodingFormat(
"FITS");
2134 auto rememberState = state()->getCaptureState();
2137 state()->setCaptureState(rememberState);
2139 auto targetChip = activeCamera()->getChip(state()->useGuideHead() ?
2140 ISD::CameraChip::GUIDE_CCD :
2141 ISD::CameraChip::PRIMARY_CCD);
2142 targetChip->abortExposure();
2143 const double exptime = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
2144 targetChip->capture(exptime);
2145 state()->getCaptureTimeout().start(
static_cast<int>((exptime) * 1000 + CAPTURE_TIMEOUT_THRESHOLD));
2149 else if (state()->captureTimeoutCounter() < 40)
2151 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Unable to restart exposure as camera is missing, trying again in 5 seconds...";
2156 state()->setCaptureTimeoutCounter(0);
2157 state()->setDeviceRestartCounter(0);
2158 emit newLog(
i18n(
"Exposure timeout. Too many. Aborting..."));
2171 if (type == ISD::Camera::ERROR_CAPTURE)
2173 int retries = activeJob()->getCaptureRetires() + 1;
2175 activeJob()->setCaptureRetires(retries);
2177 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
2185 emit newLog(
i18n(
"Restarting capture attempt #%1", retries));
2187 state()->setNextSequenceID(1);
2204 double currentADU = imageData->getADU();
2205 bool outOfRange =
false, saturated =
false;
2207 switch (imageData->bpp())
2210 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT8_MAX)
2212 else if (currentADU / UINT8_MAX > 0.95)
2217 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT16_MAX)
2219 else if (currentADU / UINT16_MAX > 0.95)
2224 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT32_MAX)
2226 else if (currentADU / UINT32_MAX > 0.95)
2236 emit newLog(
i18n(
"Flat calibration failed. Captured image is only %1-bit while requested ADU is %2.",
2238 ,
QString::number(activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble(),
'f', 2)));
2244 double nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.1;
2245 nextExposure = qBound(exp_min, nextExposure, exp_max);
2247 emit newLog(
i18n(
"Current image is saturated (%1). Next exposure is %2 seconds.",
2250 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2251 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2252 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2254 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2260 double ADUDiff = fabs(currentADU - activeJob()->getCoreProperty(
2261 SequenceJob::SJ_TargetADU).toDouble());
2264 if (ADUDiff <= state()->targetADUTolerance())
2266 if (activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
2269 i18n(
"Current ADU %1 within target ADU tolerance range.",
QString::number(currentADU,
'f', 0)));
2270 activeCamera()->setUploadMode(activeJob()->getUploadMode());
2271 auto placeholderPath = PlaceholderPath();
2273 placeholderPath.processJobInfo(activeJob().get());
2275 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
2280 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
2281 state()->checkSeqBoundary();
2287 double nextExposure = -1;
2290 if (std::fabs(imageData->getMax(0) - imageData->getMin(0)) < 10)
2291 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.5;
2295 if (nextExposure <= 0 || std::isnan(nextExposure))
2298 i18n(
"Unable to calculate optimal exposure settings, please capture the flats manually."));
2304 nextExposure = qBound(exp_min, nextExposure, exp_max);
2306 emit newLog(
i18n(
"Current ADU is %1 Next exposure is %2 seconds.",
QString::number(currentADU,
'f', 0),
2307 QString(
"%L1").arg(nextExposure, 0,
'f', 6)));
2309 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2310 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2311 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2313 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2324 if (activeJob() ==
nullptr)
2326 qWarning(KSTARS_EKOS_CAPTURE) <<
"setCurrentADU with null activeJob().";
2331 double nextExposure = 0;
2332 double targetADU = activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble();
2333 std::vector<double> coeff;
2337 if(activeJob()->getCoreProperty(SequenceJob::SJ_SkyFlat).toBool() && ExpRaw.size() > 2)
2339 int remove = ExpRaw.size() - 2;
2340 ExpRaw.remove(0, remove);
2341 ADURaw.remove(0, remove);
2345 ExpRaw.append(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
2346 ADURaw.append(currentADU);
2348 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Capture: Current ADU = " << currentADU <<
" targetADU = " << targetADU
2349 <<
" Exposure Count: " << ExpRaw.count();
2353 if (ExpRaw.count() >= 2)
2355 if (ExpRaw.count() >= 5)
2359 coeff = gsl_polynomial_fit(ADURaw.data(), ExpRaw.data(), ExpRaw.count(), 2, chisq);
2360 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Running polynomial fitting. Found " << coeff.size() <<
" coefficients.";
2361 if (std::isnan(coeff[0]) || std::isinf(coeff[0]))
2363 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coefficients are invalid.";
2364 targetADUAlgorithm = ADU_LEAST_SQUARES;
2368 nextExposure = coeff[0] + (coeff[1] * targetADU) + (coeff[2] * pow(targetADU, 2));
2370 if (nextExposure < 0 || (nextExposure > ExpRaw.last() || targetADU < ADURaw.last())
2371 || (nextExposure < ExpRaw.last() || targetADU > ADURaw.last()))
2374 targetADUAlgorithm = ADU_LEAST_SQUARES;
2378 targetADUAlgorithm = ADU_POLYNOMIAL;
2379 for (
size_t i = 0; i < coeff.size(); i++)
2380 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coeff #" << i <<
"=" << coeff[i];
2385 bool looping =
false;
2386 if (ExpRaw.count() >= 10)
2388 int size = ExpRaw.count();
2389 looping = (std::fabs(ExpRaw[size - 1] - ExpRaw[size - 2] < 0.01)) &&
2390 (std::fabs(ExpRaw[size - 2] - ExpRaw[size - 3] < 0.01));
2391 if (looping && targetADUAlgorithm == ADU_POLYNOMIAL)
2393 qWarning(KSTARS_EKOS_CAPTURE) <<
"Detected looping in polynomial results. Falling back to llsqr.";
2394 targetADUAlgorithm = ADU_LEAST_SQUARES;
2401 if (targetADUAlgorithm == ADU_LEAST_SQUARES)
2403 double a = 0, b = 0;
2404 llsq(ExpRaw, ADURaw, a, b);
2409 nextExposure = (targetADU - b) / a;
2411 if (nextExposure < 0)
2419 if (nextExposure == 0.0 || nextExposure > 180)
2421 if (currentADU < targetADU)
2422 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 1.25;
2424 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * .75;
2427 qCDebug(KSTARS_EKOS_CAPTURE) <<
"next flat exposure is" << nextExposure;
2429 return nextExposure;
2439QString Ekos::CameraProcess::createTabTitle(
const FITSMode &captureMode,
const QString &deviceName)
2441 const bool isPreview = (activeJob() ==
nullptr || (activeJob() && activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW));
2442 if (isPreview && Options::singlePreviewFITS())
2446 if (Options::singleWindowCapturedFITS())
2447 return (
i18n(
"%1 Preview", deviceName));
2450 return(
i18n(
"Preview"));
2452 else if (captureMode == FITS_CALIBRATE)
2456 const QString filtername = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
2457 if (filtername ==
"")
2460 return(
QString(
"%1 %2").arg(filtername).arg(
i18n(
"Flat Calibration")));
2463 return(
i18n(
"Calibration"));
2469 const FITSScale &captureFilter,
const QString &filename,
const QString &deviceName)
2475 switch (captureMode)
2478 case FITS_CALIBRATE:
2480 if (Options::useFITSViewer())
2483 bool success =
false;
2487 QString tabTitle = createTabTitle(captureMode, deviceName);
2490 int *tabID = &m_fitsvViewerTabIDs.normalTabID;
2491 if (*tabID == -1 || Options::singlePreviewFITS() ==
false)
2494 success = getFITSViewer()->loadData(data, fileURL, &tabIndex, captureMode, captureFilter, tabTitle);
2497 auto tabs = getFITSViewer()->tabs();
2498 if (tabIndex < tabs.size() && captureMode == FITS_NORMAL)
2500 emit newView(tabs[tabIndex]->getView());
2501 tabs[tabIndex]->disconnect(
this);
2502 connect(tabs[tabIndex].get(), &FITSTab::updated,
this, [
this]
2505 emit newView(tab->getView());
2511 success = getFITSViewer()->updateData(data, fileURL, *tabID, &tabIndex, captureMode, captureFilter, tabTitle);
2518 qCCritical(KSTARS_EKOS_CAPTURE()) <<
"error adding/updating FITS";
2522 if (Options::focusFITSOnNewImage())
2523 getFITSViewer()->raise();
2536 FITSMode captureMode = tChip ==
nullptr ? FITS_UNKNOWN : tChip->getCaptureMode();
2537 FITSScale captureFilter = tChip ==
nullptr ? FITS_NONE : tChip->getCaptureFilter();
2538 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
2544 if (m_VideoWindow.isNull() && activeCamera() !=
nullptr)
2546 m_VideoWindow.reset(
new StreamWG(activeCamera()));
2551 connect(activeCamera(), &ISD::Camera::videoRecordToggled, m_VideoWindow.get(), &StreamWG::enableStream,
2557 return m_VideoWindow;
2560void CameraProcess::updateVideoWindow(
int width,
int height,
bool streamEnabled)
2564 if (width > 0 && height > 0)
2569void CameraProcess::closeVideoWindow()
2571 if (m_VideoWindow.
isNull())
2574 m_VideoWindow->close();
2577void CameraProcess::showVideoFrame(INDI::Property prop,
int width,
int height)
2588 const QString &targetName,
bool setOptions)
2590 state()->clearCapturedFramesMap();
2591 auto queue = state()->getSequenceQueue();
2592 if (!queue->load(fileURL, targetName, devices(), state()))
2594 QString message =
i18n(
"Unable to open file %1", fileURL);
2595 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2601 queue->setOptions();
2603 state()->updateHFRThreshold();
2606 for (
auto j : state()->allJobs())
2615 state()->getSequenceQueue()->loadOptions();
2616 return state()->getSequenceQueue()->save(path, state()->observerName());
2628 connect(activeCamera(), &ISD::Camera::videoRecordToggled,
this, &CameraProcess::updateVideoRecordStatus,
2642 disconnect(activeCamera(), &ISD::Camera::ready,
this, &CameraProcess::cameraReady);
2649 if (devices()->filterWheel() && devices()->filterWheel() == device)
2652 if (devices()->filterWheel())
2653 devices()->filterWheel()->disconnect(
this);
2655 devices()->setFilterWheel(device);
2657 return (device !=
nullptr);
2664 emit newLog(
i18n(
"Sequence paused."));
2669 state()->setContinueAction(continueAction);
2682 for (
auto &job : state()->allJobs())
2684 if (job->getStatus() == JOB_IDLE || job->getStatus() == JOB_ABORTED)
2696 for (
auto &job : state()->allJobs())
2698 if (job->getStatus() != JOB_DONE)
2701 if (state()->getCaptureDelayTimer().isActive())
2703 if (state()->getCaptureDelayTimer().interval() <= 0)
2704 state()->getCaptureDelayTimer().setInterval(1000);
2711 if (!state()->ignoreJobProgress())
2714 i18n(
"All jobs are complete. Do you want to reset the status of all jobs and restart capturing?"),
2722 first_job = state()->allJobs().first();
2726 else if (state()->ignoreJobProgress())
2728 emit newLog(
i18n(
"Warning: option \"Always Reset Sequence When Starting\" is enabled and resets the sequence counts."));
2735void CameraProcess::resetJobStatus(JOBStatus newStatus)
2737 if (activeJob() !=
nullptr)
2739 activeJob()->resetStatus(newStatus);
2740 emit updateJobTable(activeJob());
2744void CameraProcess::resetAllJobs()
2746 for (
auto &job : state()->allJobs())
2751 m_State->clearCapturedFramesMap();
2753 emit updateJobTable(
nullptr);
2756void CameraProcess::updatedCaptureCompleted(
int count)
2758 activeJob()->setCompleted(count);
2759 emit updateJobTable(activeJob());
2762void CameraProcess::updateVideoRecordStatus(
bool enabled)
2765 if (activeJob() ==
nullptr)
2768 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Video recording" << (enabled ?
"started." :
"stopped.");
2770 if (enabled ==
false)
2772 updatedCaptureCompleted(activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt());
2777void CameraProcess::llsq(QVector<double> x, QVector<double> y,
double &a,
double &b)
2799 for (i = 0; i < n; i++)
2804 xbar = xbar /
static_cast<double>(n);
2805 ybar = ybar /
static_cast<double>(n);
2811 for (i = 0; i < n; i++)
2813 top = top + (x[i] - xbar) * (y[i] - ybar);
2814 bot = bot + (x[i] - xbar) * (x[i] - xbar);
2819 b = ybar - a * xbar;
2831 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2839 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2840 return devices()->getActiveCamera()->setCoolerControl(enable);
2849 KSMessageBox::Instance()->disconnect(
this);
2851 emit driverTimedout(name);
2855 KSMessageBox::Instance()->disconnect(
this);
2858 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to restart %1 camera driver?", name),
2859 i18n(
"Driver Restart"), 5);
2864 if (!activeCamera())
2867 ISD::CameraChip *tChip = devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD);
2870 if (devices()->getActiveCamera()->hasVideoStream())
2871 types.
append(CAPTURE_TYPE_VIDEO);
2878 if (devices()->getFilterManager().isNull())
2881 return devices()->getFilterManager()->getFilterLabels();
2886 if (devices()->getActiveCamera()->getProperty(
"CCD_GAIN"))
2891 ccdGain[
"GAIN"] = value;
2892 propertyMap[
"CCD_GAIN"] = ccdGain;
2896 propertyMap[
"CCD_GAIN"].remove(
"GAIN");
2897 if (propertyMap[
"CCD_GAIN"].size() == 0)
2898 propertyMap.remove(
"CCD_GAIN");
2901 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2906 ccdGain[
"Gain"] = value;
2907 propertyMap[
"CCD_CONTROLS"] = ccdGain;
2911 propertyMap[
"CCD_CONTROLS"].remove(
"Gain");
2912 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2913 propertyMap.remove(
"CCD_CONTROLS");
2920 if (devices()->getActiveCamera()->getProperty(
"CCD_OFFSET"))
2925 ccdOffset[
"OFFSET"] = value;
2926 propertyMap[
"CCD_OFFSET"] = ccdOffset;
2930 propertyMap[
"CCD_OFFSET"].remove(
"OFFSET");
2931 if (propertyMap[
"CCD_OFFSET"].size() == 0)
2932 propertyMap.remove(
"CCD_OFFSET");
2935 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2940 ccdOffset[
"Offset"] = value;
2941 propertyMap[
"CCD_CONTROLS"] = ccdOffset;
2945 propertyMap[
"CCD_CONTROLS"].remove(
"Offset");
2946 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2947 propertyMap.remove(
"CCD_CONTROLS");
2955 if (!m_FITSViewerWindow.
isNull())
2956 return m_FITSViewerWindow;
2959 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2964 connect(m_FITSViewerWindow.
get(), &FITSViewer::closed,
this, [
this](
int tabIndex)
2966 if (tabIndex == m_fitsvViewerTabIDs.normalTabID)
2967 m_fitsvViewerTabIDs.normalTabID = -1;
2968 else if (tabIndex == m_fitsvViewerTabIDs.calibrationTabID)
2969 m_fitsvViewerTabIDs.calibrationTabID = -1;
2970 else if (tabIndex == m_fitsvViewerTabIDs.focusTabID)
2971 m_fitsvViewerTabIDs.focusTabID = -1;
2972 else if (tabIndex == m_fitsvViewerTabIDs.guideTabID)
2973 m_fitsvViewerTabIDs.guideTabID = -1;
2974 else if (tabIndex == m_fitsvViewerTabIDs.alignTabID)
2975 m_fitsvViewerTabIDs.alignTabID = -1;
2979 connect(m_FITSViewerWindow.
get(), &FITSViewer::terminated,
this, [
this]()
2981 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2982 m_FITSViewerWindow.
clear();
2985 return m_FITSViewerWindow;
2990 return devices()->getActiveCamera();
2993void CameraProcess::checkCaptureOperationsTimeout(
const std::function<
void()> &slot)
2996 if (m_CaptureOperationsTimer.isValid() ==
false)
2997 m_CaptureOperationsTimer.start();
3000 if (state()->getCaptureState() == CAPTURE_PAUSED)
3001 m_CaptureOperationsTimer.restart();
3004 if (m_CaptureOperationsTimer.elapsed() >= Options::captureOperationsTimeout() * 1000)
3006 emit newLog(
i18n(
"Capture operations timed out after %1 seconds.", Options::captureOperationsTimeout()));
3007 stopCapturing(CAPTURE_ABORTED);
const QSharedPointer< SequenceJob > findNextPendingJob()
findExecutableJob find next job to be executed
IPState runCaptureScript(ScriptTypes scriptType, bool precond=true)
runCaptureScript Run the pre-/post capture/job script
void processCaptureTimeout()
processCaptureTimeout If exposure timed out, let's handle it.
bool setMount(ISD::Mount *device)
setMount Connect to the given mount device (and deconnect the old one if existing)
void setExposureProgress(ISD::CameraChip *tChip, double value, IPState state)
setExposureProgress Manage exposure progress reported by the camera device.
IPState startNextExposure()
startNextExposure Ensure that all pending preparation tasks are be completed (focusing,...
void updatePreCaptureCalibrationStatus()
updatePreCaptureCalibrationStatus This is a wrapping loop for processPreCaptureCalibrationStage(),...
void reconnectCameraDriver(const QString &camera, const QString &filterWheel)
reconnectDriver Reconnect the camera driver
IPState checkLightFramePendingTasks()
Check all tasks that might be pending before capturing may start.
void checkNextExposure()
checkNextExposure Try to start capturing the next exposure (
void clearFlatCache()
clearFlatCache Clear the measured values for flat calibrations
bool loadSequenceQueue(const QString &fileURL, const QString &targetName="", bool setOptions=true)
Loads the Ekos Sequence Queue file in the Sequence Queue.
bool setFilterWheel(ISD::FilterWheel *device)
setFilterWheel Connect to the given filter wheel device (and deconnect the old one if existing)
void startNextPendingJob()
startNextPendingJob Start the next pending job.
Q_SCRIPTABLE void resetFrame()
resetFrame Reset frame settings of the camera
bool saveSequenceQueue(const QString &path, bool loadOptions=true)
Saves the Sequence Queue to the Ekos Sequence Queue file.
QStringList generateScriptArguments() const
generateScriptArguments Generate argument list to pass to capture script
IPState previewImageCompletedAction()
previewImageCompletedAction Activities required when a preview image has been captured.
bool setDome(ISD::Dome *device)
setDome Connect to the given dome device
bool setCoolerControl(bool enable)
Set the CCD cooler ON/OFF.
void setScope(const QString &name)
setScope Set active train telescope name
void prepareActiveJobStage1()
prepareActiveJobStage1 Check for pre job script to execute.
void updateCompletedCaptureCountersAction()
updateCompletedCaptureCounters Update counters if an image has been captured
void scriptFinished(int exitCode, QProcess::ExitStatus status)
scriptFinished Slot managing the return status of pre/post capture/job scripts
bool setRotator(ISD::Rotator *device)
setRotator Connect to the given rotator device (and deconnect the old one if existing)
void selectCamera(QString name)
setCamera select camera device
void startJob(const QSharedPointer< SequenceJob > &job)
startJob Start the execution of a selected sequence job:
Q_SCRIPTABLE void executeJob()
executeJob Start the execution of activeJob by initiating updatePreCaptureCalibrationStatus().
void stopCapturing(CaptureState targetState)
stopCapturing Stopping the entire capturing state (envelope for aborting, suspending,...
IPState processPreCaptureCalibrationStage()
processPreCaptureCalibrationStage Execute the tasks that need to be completed before capturing may st...
bool setCamera(ISD::Camera *device)
setCamera Connect to the given camera device (and deconnect the old one if existing)
QSharedPointer< StreamWG > getVideoWindow()
getVideoWindow Return the current video window and initialize it if required.
void checkCamera()
configureCamera Refreshes the CCD information in the capture module.
void updateGain(double value, QMap< QString, QMap< QString, QVariant > > &propertyMap)
getGain Update the gain value from the custom property value.
QStringList filterLabels()
filterLabels list of currently available filter labels
bool setLightBox(ISD::LightBox *device)
setLightBox Connect to the given dust cap device (and deconnect the old one if existing)
Q_SCRIPTABLE void toggleSequence()
toggleSequence Toggle sequence state depending on its current state.
IPState startNextJob()
startNextJob Select the next job that is either idle or aborted and call prepareJob(*SequenceJob) to ...
bool checkPausing(CaptureContinueAction continueAction)
checkPausing check if a pause has been planned and pause subsequently
void jobCreated(QSharedPointer< SequenceJob > newJob)
Counterpart to the event {.
void prepareActiveJobStage2()
prepareActiveJobStage2 Reset #calibrationStage and continue with preparePreCaptureActions().
void prepareJob(const QSharedPointer< SequenceJob > &job)
prepareJob Update the counters of existing frames and continue with prepareActiveJob(),...
void showFITSPreview(const QSharedPointer< FITSData > &data)
showFITSPreview Directly show the FITS data as preview
void removeDevice(const QSharedPointer< ISD::GenericDevice > &device)
Generic method for removing any connected device.
IPState resumeSequence()
resumeSequence Try to continue capturing.
void refreshOpticalTrain(QString name)
refreshOpticalTrain Refresh the devices from the optical train configuration
QStringList frameTypes()
frameTypes Retrieve the frame types from the active camera's primary chip.
void updateOffset(double value, QMap< QString, QMap< QString, QVariant > > &propertyMap)
getOffset Update the offset value from the custom property value.
void capturePreview(bool loop=false)
capturePreview Capture a preview (single or looping ones)
void processJobCompletion2()
processJobCompletionStage2 Stop execution of the current sequence and check whether there exists a ne...
bool checkFlatCalibration(QSharedPointer< FITSData > imageData, double exp_min, double exp_max)
checkFlatCalibration check the flat calibration
IPState updateImageMetadataAction(QSharedPointer< FITSData > imageData)
updateImageMetadataAction Update meta data of a captured image
void captureStarted(CaptureResult rc)
captureStarted Manage the result when capturing has been started
void processFITSData(const QSharedPointer< FITSData > &data, const QString &extension)
newFITS process new FITS data received from camera.
void processNewRemoteFile(QString file)
setNewRemoteFile A new image has been stored as remote file
Q_SCRIPTABLE void pauseCapturing()
pauseCapturing Pauses capturing as soon as the current capture is complete.
IPState updateDownloadTimesAction()
updateDownloadTimesAction Add the current download time to the list of already measured ones
double calculateFlatExpTime(double currentADU)
calculateFlatExpTime calculate the next flat exposure time from the measured ADU value
void processCaptureError(ISD::Camera::ErrorType type)
processCaptureError Handle when image capture fails
IPState continueFramingAction(const QSharedPointer< FITSData > &imageData)
continueFramingAction If framing is running, start the next capture sequence
void syncDSLRToTargetChip(const QString &model)
syncDSLRToTargetChip Syncs INDI driver CCD_INFO property to the DSLR values.
void setDownloadProgress()
setDownloadProgress update the Capture Module and Summary Screen's estimate of how much time is left ...
void prepareJobExecution()
preparePreCaptureActions Trigger setting the filter, temperature, (if existing) the rotator angle and...
void updateFITSViewer(const QSharedPointer< FITSData > data, const FITSMode &captureMode, const FITSScale &captureFilter, const QString &filename, const QString &deviceName)
updateFITSViewer display new image in the configured FITSViewer tab.
void processJobCompletion1()
processJobCompletionStage1 Process job completion.
void captureImage()
captureImage Initiates image capture in the active job.
bool setDustCap(ISD::DustCap *device)
setDustCap Connect to the given dust cap device (and deconnect the old one if existing)
void restartCamera(const QString &name)
restartCamera Restarts the INDI driver associated with a camera.
bool hasCoolerControl()
Does the CCD has a cooler control (On/Off) ?
void toggleVideo(bool enabled)
Toggle video streaming if supported by the device.
CameraChip class controls a particular chip in camera.
Camera class controls an INDI Camera device.
Class handles control of INDI dome devices.
Handles operation of a remotely controlled dust cover cap.
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
Rotator class handles control of INDI Rotator devices.
static KStars * Instance()
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
CaptureState
Capture states.
@ SCRIPT_POST_CAPTURE
Script to run after a sequence capture is completed.
@ SCRIPT_POST_JOB
Script to run after a sequence job is completed.
@ SCRIPT_PRE_CAPTURE
Script to run before a sequence capture is started.
@ SCRIPT_PRE_JOB
Script to run before a sequence job is started.
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void replace(qsizetype i, const QJsonValue &value)
void append(QList< T > &&value)
qsizetype count() const const
bool isEmpty() const const
QStatusBar * statusBar() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T qobject_cast(QObject *object)
QObject * sender() const const
void errorOccurred(QProcess::ProcessError error)
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardError()
void readyReadStandardOutput()
bool isNull() const const
void showMessage(const QString &message, int timeout)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)
bool isValid() const const
QString toString() const const
uint toUInt(bool *ok) const const
Object to hold FITS Header records.