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())
704 if (Options::defaultObserver().isEmpty() ==
false)
706 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetName) !=
"")
707 FITSHeaders.
append(
FITSData::Record(
"Object", activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString(),
712 activeCamera()->setFITSHeaders(FITSHeaders);
715 state()->setBusy(
true);
716 state()->setUseGuideHead((devices()->getActiveChip()->getType() == ISD::CameraChip::PRIMARY_CCD) ?
719 emit syncGUIToJob(activeJob());
723 if (activeJob()->jobType() == SequenceJob::JOBTYPE_DARKFLAT)
726 if (state()->setDarkFlatExposure(activeJob())
727 && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
729 auto placeholderPath = PlaceholderPath();
731 placeholderPath.processJobInfo(activeJob().get());
732 state()->setNextSequenceID(1);
737 m_CaptureOperationsTimer.invalidate();
744 if (activeJob() ==
nullptr)
746 qWarning(KSTARS_EKOS_CAPTURE) <<
"preparePreCaptureActions with null activeJob().";
751 state()->setBusy(
true);
754 activeJob()->setCoreProperty(SequenceJob::SJ_GuiderActive,
755 state()->isActivelyGuiding());
758 activeJob()->prepareCapture();
761 emit jobExecutionPreparationStarted();
766 state()->setOpticalTrain(name);
768 auto mount = OpticalTrainManager::Instance()->getMount(name);
771 auto scope = OpticalTrainManager::Instance()->getScope(name);
774 auto camera = OpticalTrainManager::Instance()->getCamera(name);
777 auto filterWheel = OpticalTrainManager::Instance()->getFilterWheel(name);
780 auto rotator = OpticalTrainManager::Instance()->getRotator(name);
783 auto dustcap = OpticalTrainManager::Instance()->getDustCap(name);
786 auto lightbox = OpticalTrainManager::Instance()->getLightBox(name);
797 if (
checkPausing(CAPTURE_CONTINUE_ACTION_NEXT_EXPOSURE) ==
true)
801 if (state()->checkMeridianFlipActive())
807 state()->getGuideState() == GUIDE_GUIDING &&
808 Options::enforceStartGuiderDrift())
812 if ((state()->getCaptureState() ==
CAPTURE_DITHERING && state()->getDitheringState() != IPS_OK)
813 || state()->checkDithering())
821 if (state()->checkFocusRunning() || state()->startFocusIfRequired())
826 if (state()->getGuideState() == GUIDE_SUSPENDED && activeJob()->getFrameType() == FRAME_LIGHT)
828 emit newLog(
i18n(
"Autoguiding resumed."));
829 emit resumeGuiding();
847 state()->getCaptureTimeout().start(
static_cast<int>(activeJob()->getCoreProperty(
848 SequenceJob::SJ_Exposure).toDouble()) * 1000 +
849 CAPTURE_TIMEOUT_THRESHOLD);
851 state()->imageCountDown().setHMS(0, 0, 0);
852 double ms_left = std::ceil(activeJob()->getExposeLeft() * 1000.0);
853 state()->imageCountDownAddMSecs(
int(ms_left));
854 state()->setLastRemainingFrameTimeMS(ms_left);
855 state()->sequenceCountDown().setHMS(0, 0, 0);
856 state()->sequenceCountDownAddMSecs(activeJob()->getJobRemainingTime(state()->averageDownloadTime()) * 1000);
859 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
861 auto index = state()->allJobs().indexOf(activeJob());
862 if (index >= 0 && index < state()->getSequence().count())
863 state()->changeSequenceValue(index,
"Status",
"In Progress");
865 emit updateJobTable(activeJob());
867 emit captureRunning();
871 case CAPTURE_FRAME_ERROR:
872 emit newLog(
i18n(
"Failed to set sub frame."));
876 case CAPTURE_BIN_ERROR:
877 emit newLog((
i18n(
"Failed to set binning.")));
881 case CAPTURE_FOCUS_ERROR:
882 emit newLog((
i18n(
"Cannot capture while focus module is busy.")));
893 if (started == IPS_BUSY)
899IPState CameraProcess::captureImageWithDelay()
901 auto theJob = activeJob();
903 if (theJob ==
nullptr)
906 const int seqDelay = theJob->getCoreProperty(SequenceJob::SJ_Delay).toInt();
912 state()->getCaptureDelayTimer().start(seqDelay);
921 auto theJob = activeJob();
923 if (theJob ==
nullptr)
927 if (activeJob()->getFrameType() == FRAME_LIGHT)
930 if (pending != IPS_OK)
935 return captureImageWithDelay();
943 if (
checkPausing(CAPTURE_CONTINUE_ACTION_CAPTURE_COMPLETE) ==
true)
954 else if (activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
955 activeJob()->getCompleted())
965 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload() &&
966 state()->getMeridianFlipState()->checkMeridianFlipActive() ==
false)
968 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Resuming guiding...";
969 emit resumeGuiding();
973 if (activeCamera()->isFastExposureEnabled())
975 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
977 state()->checkSeqBoundary();
978 activeCamera()->setNextSequenceID(state()->nextSequenceID());
988 if (activeCamera()->isFastExposureEnabled())
990 state()->setRememberFastExposure(
true);
991 activeCamera()->setFastExposureEnabled(
false);
999 if (activeCamera()->isFastExposureEnabled())
1002 activeJob()->getFrameType() == FRAME_LIGHT &&
1011 state()->setRememberFastExposure(
true);
1012 activeCamera()->setFastExposureEnabled(
false);
1015 m_CaptureOperationsTimer.invalidate();
1029 if (data && activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1031 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW
1032 && activeJob()->getCalibrationStage() != SequenceJobState::CAL_CALIBRATION)
1034 if (state()->generateFilename(extension, &filename) && activeCamera()->saveCurrentImage(filename))
1036 data->setFilename(filename);
1042 qCWarning(KSTARS_EKOS_CAPTURE) <<
"Saving current image failed!";
1045 KSMessageBox::Instance()->disconnect(
this);
1047 KSMessageBox::Instance()->error(
i18n(
"Failed writing image to %1\nPlease check folder, filename & permissions.",
1049 i18n(
"Image Write Failed"), 30);
1064 state()->setImageData(data);
1065 blobInfo =
QString(
"{Device: %1 Property: %2 Element: %3 Chip: %4}").
arg(data->property(
"device").toString())
1066 .
arg(data->property(
"blobVector").toString())
1067 .
arg(data->property(
"blobElement").toString())
1068 .
arg(data->property(
"chip").toInt());
1071 state()->imageData().reset();
1078 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring received FITS as active job is null.";
1080 emit processingFITSfinished(
false);
1084 if (state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1087 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as meridian flip stage is" <<
1088 state()->getMeridianFlipState()->getMeridianFlipStage();
1089 emit processingFITSfinished(
false);
1093 const SequenceJob::SequenceJobType currentJobType = activeJob()->jobType();
1095 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1100 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as current capture state is not active" <<
1101 state()->getCaptureState();
1103 emit processingFITSfinished(
false);
1109 tChip = activeCamera()->getChip(
static_cast<ISD::CameraChip::ChipType
>(data->property(
"chip").toInt()));
1110 if (tChip != devices()->getActiveChip())
1112 if (state()->getGuideState() == GUIDE_IDLE)
1113 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as it does not correspond to the target chip"
1114 << devices()->getActiveChip()->getType();
1116 emit processingFITSfinished(
false);
1121 if (devices()->getActiveChip()->getCaptureMode() == FITS_FOCUS ||
1122 devices()->getActiveChip()->getCaptureMode() == FITS_GUIDE)
1124 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as it has the wrong capture mode" <<
1125 devices()->getActiveChip()->getCaptureMode();
1127 emit processingFITSfinished(
false);
1132 if (data && data->property(
"device").toString() != activeCamera()->getDeviceName())
1134 qCWarning(KSTARS_EKOS_CAPTURE) << blobInfo <<
"Ignoring Received FITS as the blob device name does not equal active camera"
1135 << activeCamera()->getDeviceName();
1137 emit processingFITSfinished(
false);
1141 if (currentJobType == SequenceJob::JOBTYPE_PREVIEW)
1144 if (checkSavingReceivedImage(data, extension, filename))
1146 FITSMode captureMode = tChip->getCaptureMode();
1147 FITSScale captureFilter = tChip->getCaptureFilter();
1148 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
1153 if (data && Options::autoDark() && job->jobType() == SequenceJob::JOBTYPE_PREVIEW && state()->useGuideHead() ==
false)
1155 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1158 m_DarkProcessor.data()->denoise(trainID.
toUInt(),
1159 devices()->getActiveChip(),
1160 state()->imageData(),
1161 job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(),
1162 job->getCoreProperty(SequenceJob::SJ_ROI).toRect().x(),
1163 job->getCoreProperty(SequenceJob::SJ_ROI).toRect().y());
1166 qWarning(KSTARS_EKOS_CAPTURE) <<
"Invalid train ID for darks substraction:" << trainID.
toUInt();
1169 if (currentJobType == SequenceJob::JOBTYPE_PREVIEW)
1187 if (activeCamera()->isFastExposureEnabled() ==
false && state()->isLooping() ==
false)
1189 disconnect(activeCamera(), &ISD::Camera::newExposureValue,
this,
1191 DarkLibrary::Instance()->disconnect(
this);
1195 bool alreadySaved =
false;
1196 switch (thejob->getFrameType())
1200 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1204 if (thejob->getFlatFieldDuration() == DURATION_ADU
1205 && thejob->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > 0)
1207 if (
checkFlatCalibration(state()->imageData(), state()->exposureRange().min, state()->exposureRange().max) ==
false)
1212 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1214 if (checkSavingReceivedImage(data, extension, filename))
1215 alreadySaved =
true;
1219 thejob->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
1228 qWarning(KSTARS_EKOS_CAPTURE) <<
"Job completed with frametype NONE!";
1235 if (thejob->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION_COMPLETE)
1236 thejob->setCalibrationStage(SequenceJobState::CAL_CAPTURING);
1238 if (activeJob() && currentJobType != SequenceJob::JOBTYPE_PREVIEW &&
1239 activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1242 if (alreadySaved || checkSavingReceivedImage(data, extension, filename))
1251 emit newImage(thejob, state()->imageData());
1258 if (currentJobType != SequenceJob::JOBTYPE_PREVIEW)
1262 emit processingFITSfinished(
true);
1267 ISD::CameraChip * tChip = activeCamera()->getChip(
static_cast<ISD::CameraChip::ChipType
>(data->property(
"chip").toInt()));
1269 updateFITSViewer(data, tChip->getCaptureMode(), tChip->getCaptureFilter(),
"", data->property(
"device").toString());
1274 emit newLog(
i18n(
"Remote image saved to %1", file));
1277 if (activeCamera() && activeCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1287 if (activeJob() ==
nullptr)
1289 qCWarning(KSTARS_EKOS_CAPTURE) <<
"Processing pre capture calibration without active job, state = " <<
1290 getCaptureStatusString(state()->getCaptureState());
1297 if (activeJob()->getFrameType() != FRAME_LIGHT
1298 && state()->getGuideState() == GUIDE_GUIDING)
1300 emit newLog(
i18n(
"Autoguiding suspended."));
1301 emit suspendGuiding();
1305 switch (activeJob()->getFrameType())
1328 if (state()->isBusy() ==
false)
1330 emit newLog(
i18n(
"Warning: Calibration process was prematurely terminated."));
1336 if (rc == IPS_ALERT)
1338 else if (rc == IPS_BUSY)
1344 captureImageWithDelay();
1349 if (activeJob() ==
nullptr)
1351 qWarning(KSTARS_EKOS_CAPTURE) <<
"procesJobCompletionStage1 with null activeJob().";
1365 if (activeJob() ==
nullptr)
1367 qWarning(KSTARS_EKOS_CAPTURE) <<
"procesJobCompletionStage2 with null activeJob().";
1371 activeJob()->done();
1373 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1375 int index = state()->allJobs().indexOf(activeJob());
1376 QJsonArray seqArray = state()->getSequence();
1377 QJsonObject oneSequence = seqArray[index].toObject();
1378 oneSequence[
"Status"] =
"Complete";
1379 seqArray.
replace(index, oneSequence);
1380 state()->setSequence(seqArray);
1381 emit sequenceChanged(seqArray);
1382 emit updateJobTable(activeJob());
1396 KSNotification::event(
QLatin1String(
"CaptureSuccessful"),
i18n(
"CCD capture sequence completed"),
1397 KSNotification::Capture);
1403 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1404 emit resumeGuiding();
1413 for (
auto &oneJob : state()->allJobs())
1415 if (oneJob->getStatus() == JOB_IDLE || oneJob->getStatus() == JOB_ABORTED)
1429 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload() &&
1430 state()->getMeridianFlipState()->checkMeridianFlipActive() ==
false)
1432 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Resuming guiding...";
1433 emit resumeGuiding();
1440 qCDebug(KSTARS_EKOS_CAPTURE) <<
"All capture jobs complete.";
1447 if (activeJob() ==
nullptr)
1451 if (!activeCamera() || !activeCamera()->isConnected())
1453 emit newLog(
i18n(
"Error: Lost connection to CCD."));
1458 state()->getCaptureTimeout().stop();
1459 state()->getCaptureDelayTimer().stop();
1460 if (activeCamera()->isFastExposureEnabled())
1462 int remaining = state()->isLooping() ? 100000 : (activeJob()->getCoreProperty(
1463 SequenceJob::SJ_Count).toInt() -
1464 activeJob()->getCompleted());
1466 activeCamera()->setFastCount(
static_cast<uint
>(remaining));
1471 if (activeJob()->getFrameType() == FRAME_FLAT)
1474 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW
1475 && activeJob()->getFlatFieldDuration() == DURATION_ADU &&
1476 activeJob()->getCalibrationStage() == SequenceJobState::CAL_NONE)
1478 if (activeCamera()->getEncodingFormat() !=
"FITS" &&
1479 activeCamera()->getEncodingFormat() !=
"XISF")
1481 emit newLog(
i18n(
"Cannot calculate ADU levels in non-FITS images."));
1486 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
1491 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1493 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
1494 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
1499 if (activeCamera()->getUploadMode() != activeJob()->getUploadMode())
1500 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1503 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
1505 state()->checkSeqBoundary();
1506 activeCamera()->setNextSequenceID(state()->nextSequenceID());
1510 if (state()->isRememberFastExposure())
1512 state()->setRememberFastExposure(
false);
1513 activeCamera()->setFastExposureEnabled(
true);
1516 if (state()->frameSettings().contains(devices()->getActiveChip()))
1518 const auto roi = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect();
1519 QVariantMap settings;
1520 settings[
"x"] = roi.x();
1521 settings[
"y"] = roi.y();
1522 settings[
"w"] = roi.width();
1523 settings[
"h"] = roi.height();
1524 settings[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1525 settings[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1527 state()->frameSettings()[devices()->getActiveChip()] = settings;
1531 activeCamera()->setEncodingFormat(activeJob()->getCoreProperty(
1532 SequenceJob::SJ_Encoding).toString());
1534 state()->setStartingCapture(
true);
1535 state()->placeholderPath().setGenerateFilenameSettings(*activeJob());
1538 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
1540 auto remoteUpload = state()->placeholderPath().generateSequenceFilename(*activeJob(),
false,
true, 1,
"",
"",
false,
1546#if defined(Q_OS_WIN)
1549 int lastForwardSlash = remoteUpload.lastIndexOf(
'/');
1550 if (lastForwardSlash > lastSeparator)
1551 lastSeparator = lastForwardSlash;
1554 auto remoteDirectory = remoteUpload.mid(0, lastSeparator);
1555 auto remoteFilename = remoteUpload.mid(lastSeparator + 1);
1556 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatDirectory, remoteDirectory);
1557 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatFilename, remoteFilename);
1563 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(),
1564 activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION ? FITS_CALIBRATE :
1568 if (state()->isRememberFastExposure())
1570 state()->setRememberFastExposure(
false);
1571 activeCamera()->setFastExposureEnabled(
true);
1574 emit captureTarget(activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString());
1575 emit captureImageStarted();
1580 devices()->setActiveChip(state()->useGuideHead() ?
1581 devices()->getActiveCamera()->getChip(
1582 ISD::CameraChip::GUIDE_CCD) :
1583 devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1584 devices()->getActiveChip()->resetFrame();
1585 emit updateFrameProperties(1);
1591 if (state()->checkCapturing() ==
false)
1594 if (devices()->getActiveChip() != tChip ||
1595 devices()->getActiveChip()->getCaptureMode() != FITS_NORMAL
1596 || state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1599 double deltaMS = std::ceil(1000.0 * value - state()->lastRemainingFrameTimeMS());
1600 emit updateCaptureCountDown(
int(deltaMS));
1601 state()->setLastRemainingFrameTimeMS(state()->lastRemainingFrameTimeMS() + deltaMS);
1605 activeJob()->setExposeLeft(value);
1607 emit newExposureProgress(activeJob());
1610 if (activeJob() && ipstate == IPS_ALERT)
1612 int retries = activeJob()->getCaptureRetires() + 1;
1614 activeJob()->setCaptureRetires(retries);
1616 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
1620 activeJob()->abort();
1624 emit newLog((
i18n(
"Restarting capture attempt #%1", retries)));
1626 state()->setNextSequenceID(1);
1632 if (activeJob() !=
nullptr && ipstate == IPS_OK)
1634 activeJob()->setCaptureRetires(0);
1635 activeJob()->setExposeLeft(0);
1637 if (devices()->getActiveCamera()
1638 && devices()->getActiveCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1640 if (activeJob()->getStatus() == JOB_BUSY)
1642 emit processingFITSfinished(
false);
1647 if (state()->getGuideState() == GUIDE_GUIDING && Options::guiderType() == 0
1648 && state()->suspendGuidingOnDownload())
1650 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Autoguiding suspended until primary CCD chip completes downloading...";
1651 emit suspendGuiding();
1654 emit downloadingFrame();
1657 state()->downloadTimer().start();
1658 state()->downloadProgressTimer().start();
1666 double downloadTimeLeft = state()->averageDownloadTime() - state()->downloadTimer().elapsed() /
1668 if(downloadTimeLeft >= 0)
1670 state()->imageCountDown().setHMS(0, 0, 0);
1671 state()->imageCountDownAddMSecs(
int(std::ceil(downloadTimeLeft * 1000)));
1672 emit newDownloadProgress(downloadTimeLeft);
1680 emit newImage(activeJob(), imageData);
1682 if (activeCamera()->isFastExposureEnabled() ==
false)
1684 const int seqDelay = activeJob()->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1690 if (activeJob() !=
nullptr)
1691 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1694 else if (activeJob() !=
nullptr)
1695 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1705 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE
1706 && state()->downloadTimer().isValid())
1709 double currentDownloadTime = state()->downloadTimer().elapsed() / 1000.0;
1710 state()->addDownloadTime(currentDownloadTime);
1712 state()->downloadTimer().invalidate();
1716 emit newLog(
i18n(
"Download Time: %1 s, New Download Time Estimate: %2 s.", dLTimeString, estimatedTimeString));
1723 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1726 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1728 state()->setActiveJob(
nullptr);
1730 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1731 emit resumeGuiding();
1742 state()->getCaptureTimeout().stop();
1743 state()->setCaptureTimeoutCounter(0);
1745 state()->downloadProgressTimer().stop();
1748 if (state()->isLooping())
1762 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW
1763 || activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
1767 updatedCaptureCompleted(activeJob()->getCompleted() + 1);
1769 state()->getRefocusState()->decreaseInSequenceFocusCounter();
1771 state()->getRefocusState()->setAdaptiveFocusDone(
false);
1775 if (state()->getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_FLIPPING)
1776 state()->decreaseDitherCounter();
1779 state()->addCapturedFrame(activeJob()->getSignature());
1782 emit newLog(
i18n(
"Received image %1 out of %2.", activeJob()->getCompleted(),
1783 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
1786 m_CaptureOperationsTimer.invalidate();
1791 double hfr = -1, eccentricity = -1;
1792 int numStars = -1, median = -1;
1797 if (Options::autoHFR() && imageData && !imageData->areStarsSearched() && imageData->getRecordValue(
"FRAME", frameType)
1798 && frameType.
toString() ==
"Light")
1800#ifdef HAVE_STELLARSOLVER
1803 QVariantMap extractionSettings;
1804 extractionSettings[
"optionsProfileIndex"] = Options::hFROptionsProfile();
1805 extractionSettings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::HFRProfiles);
1806 imageData->setSourceExtractorSettings(extractionSettings);
1811 hfr = imageData->getHFR(HFR_AVERAGE);
1812 numStars = imageData->getSkyBackground().starsDetected;
1813 median = imageData->getMedian();
1814 eccentricity = imageData->getEccentricity();
1815 filename = imageData->filename();
1818 if (state()->isLooping() ==
false && activeJob() !=
nullptr && activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1819 emit newLog(
i18n(
"Captured %1", filename));
1821 auto remainingPlaceholders = PlaceholderPath::remainingPlaceholders(filename);
1822 if (remainingPlaceholders.size() > 0)
1825 i18n(
"WARNING: remaining and potentially unknown placeholders %1 in %2",
1826 remainingPlaceholders.join(
", "), filename));
1832 QVariantMap metadata;
1833 metadata[
"filename"] = filename;
1834 metadata[
"type"] = activeJob()->getFrameType();
1835 metadata[
"exposure"] = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1836 metadata[
"filter"] = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
1837 metadata[
"width"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().width();
1838 metadata[
"height"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().height();
1839 metadata[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1840 metadata[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1841 metadata[
"hfr"] = hfr;
1842 metadata[
"starCount"] = numStars;
1843 metadata[
"median"] = median;
1844 metadata[
"eccentricity"] = eccentricity;
1845 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Captured frame metadata: filename =" << filename <<
", type =" << metadata[
"type"].toInt()
1846 <<
"exposure =" << metadata[
"exposure"].toDouble() <<
"filter =" << metadata[
"filter"].toString() <<
"width =" <<
1847 metadata[
"width"].toInt() <<
"height =" << metadata[
"height"].toInt() <<
"hfr =" << metadata[
"hfr"].toDouble() <<
1848 "starCount =" << metadata[
"starCount"].toInt() <<
"median =" << metadata[
"median"].toInt() <<
"eccentricity =" <<
1849 metadata[
"eccentricity"].toDouble();
1851 emit captureComplete(metadata);
1860 const QString captureScript = activeJob()->getScript(scriptType);
1861 if (captureScript.isEmpty() ==
false && precond)
1863 state()->setCaptureScriptType(scriptType);
1866 emit newLog(
i18n(
"Executing capture script %1", captureScript));
1878 switch (state()->captureScriptType())
1881 emit newLog(
i18n(
"Pre capture script finished with code %1.", exitCode));
1882 if (activeJob() && activeJob()->getStatus() == JOB_IDLE)
1886 m_CaptureOperationsTimer.invalidate();
1892 emit newLog(
i18n(
"Post capture script finished with code %1.", exitCode));
1895 if (activeJob() ==
nullptr
1896 || activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
1897 activeJob()->getCompleted())
1902 else if (state()->checkMeridianFlipReady())
1904 emit newLog(
i18n(
"Processing meridian flip..."));
1914 emit newLog(
i18n(
"Pre job script finished with code %1.", exitCode));
1919 emit newLog(
i18n(
"Post job script finished with code %1.", exitCode));
1933 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1934 if (activeCamera() && trainID.
isValid())
1936 if (activeCamera() && activeCamera()->getDeviceName() == name)
1939 emit refreshCamera(
true);
1942 emit refreshCamera(
false);
1953 if (!activeCamera())
1959 devices()->setActiveChip(
nullptr);
1962 if (activeCamera()->getDeviceName().contains(
"Guider"))
1964 state()->setUseGuideHead(
true);
1965 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::GUIDE_CCD));
1968 if (devices()->getActiveChip() ==
nullptr)
1970 state()->setUseGuideHead(
false);
1971 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1974 emit refreshCameraSettings();
1979 auto pos = std::find_if(state()->DSLRInfos().begin(),
1982 return (oneDSLRInfo[
"Model"] == model);
1986 if (pos != state()->DSLRInfos().end())
1989 devices()->getActiveChip()->setImageInfo(camera[
"Width"].toInt(),
1990 camera[
"Height"].toInt(),
1991 camera[
"PixelW"].toDouble(),
1992 camera[
"PixelH"].toDouble(),
1999 if (activeCamera() && activeCamera()->getDeviceName() == camera)
2002 auto rememberState = state()->getCaptureState();
2005 state()->setCaptureState(rememberState);
2008 state()->setCaptureTimeoutCounter(0);
2012 devices()->setActiveChip(devices()->getActiveChip());
2026 auto name = device->getDeviceName();
2027 device->disconnect(
this);
2030 if (devices()->mount() && devices()->mount()->getDeviceName() == device->getDeviceName())
2032 devices()->mount()->disconnect(
this);
2033 devices()->setMount(
nullptr);
2034 if (activeJob() !=
nullptr)
2035 activeJob()->addMount(
nullptr);
2039 if (devices()->dome() && devices()->dome()->getDeviceName() == device->getDeviceName())
2041 devices()->dome()->disconnect(
this);
2042 devices()->setDome(
nullptr);
2046 if (devices()->rotator() && devices()->rotator()->getDeviceName() == device->getDeviceName())
2048 devices()->rotator()->disconnect(
this);
2049 devices()->setRotator(
nullptr);
2053 if (devices()->dustCap() && devices()->dustCap()->getDeviceName() == device->getDeviceName())
2055 devices()->dustCap()->disconnect(
this);
2056 devices()->setDustCap(
nullptr);
2057 state()->hasDustCap =
false;
2058 state()->setDustCapState(CAP_UNKNOWN);
2062 if (devices()->lightBox() && devices()->lightBox()->getDeviceName() == device->getDeviceName())
2064 devices()->lightBox()->disconnect(
this);
2065 devices()->setLightBox(
nullptr);
2066 state()->hasLightBox =
false;
2067 state()->setLightBoxLightState(CAP_LIGHT_UNKNOWN);
2071 if (activeCamera() && activeCamera()->getDeviceName() == name)
2073 activeCamera()->disconnect(
this);
2074 devices()->setActiveCamera(
nullptr);
2075 devices()->setActiveChip(
nullptr);
2078 if (INDIListener::findDevice(name, generic))
2079 DarkLibrary::Instance()->removeDevice(generic);
2085 if (devices()->filterWheel() && devices()->filterWheel()->getDeviceName() == name)
2087 devices()->filterWheel()->disconnect(
this);
2088 devices()->setFilterWheel(
nullptr);
2092 emit refreshFilterSettings();
2099 state()->setCaptureTimeoutCounter(state()->captureTimeoutCounter() + 1);
2101 if (state()->deviceRestartCounter() >= 3)
2103 state()->setCaptureTimeoutCounter(0);
2104 state()->setDeviceRestartCounter(0);
2105 emit newLog(
i18n(
"Exposure timeout. Aborting..."));
2110 if (state()->captureTimeoutCounter() > 3 && activeCamera())
2112 emit newLog(
i18n(
"Exposure timeout. More than 3 have been detected, will restart driver."));
2113 QString camera = activeCamera()->getDeviceName();
2114 QString fw = (devices()->filterWheel() !=
nullptr) ?
2115 devices()->filterWheel()->getDeviceName() :
"";
2116 emit driverTimedout(camera);
2119 state()->setDeviceRestartCounter(state()->deviceRestartCounter() + 1);
2127 if (activeCamera() && activeJob())
2130 emit newLog(
i18n(
"Exposure timeout. Restarting exposure..."));
2131 activeCamera()->setEncodingFormat(
"FITS");
2132 auto rememberState = state()->getCaptureState();
2135 state()->setCaptureState(rememberState);
2137 auto targetChip = activeCamera()->getChip(state()->useGuideHead() ?
2138 ISD::CameraChip::GUIDE_CCD :
2139 ISD::CameraChip::PRIMARY_CCD);
2140 targetChip->abortExposure();
2141 const double exptime = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
2142 targetChip->capture(exptime);
2143 state()->getCaptureTimeout().start(
static_cast<int>((exptime) * 1000 + CAPTURE_TIMEOUT_THRESHOLD));
2147 else if (state()->captureTimeoutCounter() < 40)
2149 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Unable to restart exposure as camera is missing, trying again in 5 seconds...";
2154 state()->setCaptureTimeoutCounter(0);
2155 state()->setDeviceRestartCounter(0);
2156 emit newLog(
i18n(
"Exposure timeout. Too many. Aborting..."));
2169 if (type == ISD::Camera::ERROR_CAPTURE)
2171 int retries = activeJob()->getCaptureRetires() + 1;
2173 activeJob()->setCaptureRetires(retries);
2175 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
2183 emit newLog(
i18n(
"Restarting capture attempt #%1", retries));
2185 state()->setNextSequenceID(1);
2202 double currentADU = imageData->getADU();
2203 bool outOfRange =
false, saturated =
false;
2205 switch (imageData->bpp())
2208 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT8_MAX)
2210 else if (currentADU / UINT8_MAX > 0.95)
2215 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT16_MAX)
2217 else if (currentADU / UINT16_MAX > 0.95)
2222 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT32_MAX)
2224 else if (currentADU / UINT32_MAX > 0.95)
2234 emit newLog(
i18n(
"Flat calibration failed. Captured image is only %1-bit while requested ADU is %2.",
2236 ,
QString::number(activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble(),
'f', 2)));
2242 double nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.1;
2243 nextExposure = qBound(exp_min, nextExposure, exp_max);
2245 emit newLog(
i18n(
"Current image is saturated (%1). Next exposure is %2 seconds.",
2248 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2249 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2250 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2252 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2258 double ADUDiff = fabs(currentADU - activeJob()->getCoreProperty(
2259 SequenceJob::SJ_TargetADU).toDouble());
2262 if (ADUDiff <= state()->targetADUTolerance())
2264 if (activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
2267 i18n(
"Current ADU %1 within target ADU tolerance range.",
QString::number(currentADU,
'f', 0)));
2268 activeCamera()->setUploadMode(activeJob()->getUploadMode());
2269 auto placeholderPath = PlaceholderPath();
2271 placeholderPath.processJobInfo(activeJob().get());
2273 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
2278 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
2279 state()->checkSeqBoundary();
2285 double nextExposure = -1;
2288 if (std::fabs(imageData->getMax(0) - imageData->getMin(0)) < 10)
2289 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.5;
2293 if (nextExposure <= 0 || std::isnan(nextExposure))
2296 i18n(
"Unable to calculate optimal exposure settings, please capture the flats manually."));
2302 nextExposure = qBound(exp_min, nextExposure, exp_max);
2304 emit newLog(
i18n(
"Current ADU is %1 Next exposure is %2 seconds.",
QString::number(currentADU,
'f', 0),
2305 QString(
"%L1").arg(nextExposure, 0,
'f', 6)));
2307 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2308 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2309 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2311 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2322 if (activeJob() ==
nullptr)
2324 qWarning(KSTARS_EKOS_CAPTURE) <<
"setCurrentADU with null activeJob().";
2329 double nextExposure = 0;
2330 double targetADU = activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble();
2331 std::vector<double> coeff;
2335 if(activeJob()->getCoreProperty(SequenceJob::SJ_SkyFlat).toBool() && ExpRaw.size() > 2)
2337 int remove = ExpRaw.size() - 2;
2338 ExpRaw.remove(0, remove);
2339 ADURaw.remove(0, remove);
2343 ExpRaw.append(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
2344 ADURaw.append(currentADU);
2346 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Capture: Current ADU = " << currentADU <<
" targetADU = " << targetADU
2347 <<
" Exposure Count: " << ExpRaw.count();
2351 if (ExpRaw.count() >= 2)
2353 if (ExpRaw.count() >= 5)
2357 coeff = gsl_polynomial_fit(ADURaw.data(), ExpRaw.data(), ExpRaw.count(), 2, chisq);
2358 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Running polynomial fitting. Found " << coeff.size() <<
" coefficients.";
2359 if (std::isnan(coeff[0]) || std::isinf(coeff[0]))
2361 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coefficients are invalid.";
2362 targetADUAlgorithm = ADU_LEAST_SQUARES;
2366 nextExposure = coeff[0] + (coeff[1] * targetADU) + (coeff[2] * pow(targetADU, 2));
2368 if (nextExposure < 0 || (nextExposure > ExpRaw.last() || targetADU < ADURaw.last())
2369 || (nextExposure < ExpRaw.last() || targetADU > ADURaw.last()))
2372 targetADUAlgorithm = ADU_LEAST_SQUARES;
2376 targetADUAlgorithm = ADU_POLYNOMIAL;
2377 for (
size_t i = 0; i < coeff.size(); i++)
2378 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coeff #" << i <<
"=" << coeff[i];
2383 bool looping =
false;
2384 if (ExpRaw.count() >= 10)
2386 int size = ExpRaw.count();
2387 looping = (std::fabs(ExpRaw[size - 1] - ExpRaw[size - 2] < 0.01)) &&
2388 (std::fabs(ExpRaw[size - 2] - ExpRaw[size - 3] < 0.01));
2389 if (looping && targetADUAlgorithm == ADU_POLYNOMIAL)
2391 qWarning(KSTARS_EKOS_CAPTURE) <<
"Detected looping in polynomial results. Falling back to llsqr.";
2392 targetADUAlgorithm = ADU_LEAST_SQUARES;
2399 if (targetADUAlgorithm == ADU_LEAST_SQUARES)
2401 double a = 0, b = 0;
2402 llsq(ExpRaw, ADURaw, a, b);
2407 nextExposure = (targetADU - b) / a;
2409 if (nextExposure < 0)
2417 if (nextExposure == 0.0 || nextExposure > 180)
2419 if (currentADU < targetADU)
2420 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 1.25;
2422 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * .75;
2425 qCDebug(KSTARS_EKOS_CAPTURE) <<
"next flat exposure is" << nextExposure;
2427 return nextExposure;
2437QString Ekos::CameraProcess::createTabTitle(
const FITSMode &captureMode,
const QString &deviceName)
2439 const bool isPreview = (activeJob() ==
nullptr || (activeJob() && activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW));
2440 if (isPreview && Options::singlePreviewFITS())
2444 if (Options::singleWindowCapturedFITS())
2445 return (
i18n(
"%1 Preview", deviceName));
2448 return(
i18n(
"Preview"));
2450 else if (captureMode == FITS_CALIBRATE)
2454 const QString filtername = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
2455 if (filtername ==
"")
2458 return(
QString(
"%1 %2").arg(filtername).arg(
i18n(
"Flat Calibration")));
2461 return(
i18n(
"Calibration"));
2467 const FITSScale &captureFilter,
const QString &filename,
const QString &deviceName)
2473 switch (captureMode)
2476 case FITS_CALIBRATE:
2478 if (Options::useFITSViewer())
2481 bool success =
false;
2485 QString tabTitle = createTabTitle(captureMode, deviceName);
2488 int *tabID = &m_fitsvViewerTabIDs.normalTabID;
2489 if (*tabID == -1 || Options::singlePreviewFITS() ==
false)
2492 success = getFITSViewer()->loadData(data, fileURL, &tabIndex, captureMode, captureFilter, tabTitle);
2495 auto tabs = getFITSViewer()->tabs();
2496 if (tabIndex < tabs.size() && captureMode == FITS_NORMAL)
2498 emit newView(tabs[tabIndex]->getView());
2499 tabs[tabIndex]->disconnect(
this);
2500 connect(tabs[tabIndex].get(), &FITSTab::updated,
this, [
this]
2503 emit newView(tab->getView());
2509 success = getFITSViewer()->updateData(data, fileURL, *tabID, &tabIndex, captureMode, captureFilter, tabTitle);
2516 qCCritical(KSTARS_EKOS_CAPTURE()) <<
"error adding/updating FITS";
2520 if (Options::focusFITSOnNewImage())
2521 getFITSViewer()->raise();
2534 FITSMode captureMode = tChip ==
nullptr ? FITS_UNKNOWN : tChip->getCaptureMode();
2535 FITSScale captureFilter = tChip ==
nullptr ? FITS_NONE : tChip->getCaptureFilter();
2536 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
2542 if (m_VideoWindow.isNull() && activeCamera() !=
nullptr)
2544 m_VideoWindow.reset(
new StreamWG(activeCamera()));
2549 connect(activeCamera(), &ISD::Camera::videoRecordToggled, m_VideoWindow.get(), &StreamWG::enableStream,
2555 return m_VideoWindow;
2558void CameraProcess::updateVideoWindow(
int width,
int height,
bool streamEnabled)
2562 if (width > 0 && height > 0)
2567void CameraProcess::closeVideoWindow()
2569 if (m_VideoWindow.
isNull())
2572 m_VideoWindow->close();
2575void CameraProcess::showVideoFrame(INDI::Property prop,
int width,
int height)
2586 const QString &targetName,
bool setOptions)
2588 state()->clearCapturedFramesMap();
2589 auto queue = state()->getSequenceQueue();
2590 if (!queue->load(fileURL, targetName, devices(), state()))
2592 QString message =
i18n(
"Unable to open file %1", fileURL);
2593 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2599 queue->setOptions();
2601 state()->updateHFRThreshold();
2604 for (
auto j : state()->allJobs())
2613 state()->getSequenceQueue()->loadOptions();
2614 return state()->getSequenceQueue()->save(path, state()->observerName());
2626 connect(activeCamera(), &ISD::Camera::videoRecordToggled,
this, &CameraProcess::updateVideoRecordStatus,
2640 disconnect(activeCamera(), &ISD::Camera::ready,
this, &CameraProcess::cameraReady);
2647 if (devices()->filterWheel() && devices()->filterWheel() == device)
2650 if (devices()->filterWheel())
2651 devices()->filterWheel()->disconnect(
this);
2653 devices()->setFilterWheel(device);
2655 return (device !=
nullptr);
2662 emit newLog(
i18n(
"Sequence paused."));
2667 state()->setContinueAction(continueAction);
2680 for (
auto &job : state()->allJobs())
2682 if (job->getStatus() == JOB_IDLE || job->getStatus() == JOB_ABORTED)
2694 for (
auto &job : state()->allJobs())
2696 if (job->getStatus() != JOB_DONE)
2699 if (state()->getCaptureDelayTimer().isActive())
2701 if (state()->getCaptureDelayTimer().interval() <= 0)
2702 state()->getCaptureDelayTimer().setInterval(1000);
2709 if (!state()->ignoreJobProgress())
2712 i18n(
"All jobs are complete. Do you want to reset the status of all jobs and restart capturing?"),
2720 first_job = state()->allJobs().first();
2724 else if (state()->ignoreJobProgress())
2726 emit newLog(
i18n(
"Warning: option \"Always Reset Sequence When Starting\" is enabled and resets the sequence counts."));
2733void CameraProcess::resetJobStatus(JOBStatus newStatus)
2735 if (activeJob() !=
nullptr)
2737 activeJob()->resetStatus(newStatus);
2738 emit updateJobTable(activeJob());
2742void CameraProcess::resetAllJobs()
2744 for (
auto &job : state()->allJobs())
2749 m_State->clearCapturedFramesMap();
2751 emit updateJobTable(
nullptr);
2754void CameraProcess::updatedCaptureCompleted(
int count)
2756 activeJob()->setCompleted(count);
2757 emit updateJobTable(activeJob());
2760void CameraProcess::updateVideoRecordStatus(
bool enabled)
2763 if (activeJob() ==
nullptr)
2766 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Video recording" << (enabled ?
"started." :
"stopped.");
2768 if (enabled ==
false)
2770 updatedCaptureCompleted(activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt());
2775void CameraProcess::llsq(QVector<double> x, QVector<double> y,
double &a,
double &b)
2797 for (i = 0; i < n; i++)
2802 xbar = xbar /
static_cast<double>(n);
2803 ybar = ybar /
static_cast<double>(n);
2809 for (i = 0; i < n; i++)
2811 top = top + (x[i] - xbar) * (y[i] - ybar);
2812 bot = bot + (x[i] - xbar) * (x[i] - xbar);
2817 b = ybar - a * xbar;
2829 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2837 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2838 return devices()->getActiveCamera()->setCoolerControl(enable);
2847 KSMessageBox::Instance()->disconnect(
this);
2849 emit driverTimedout(name);
2853 KSMessageBox::Instance()->disconnect(
this);
2856 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to restart %1 camera driver?", name),
2857 i18n(
"Driver Restart"), 5);
2862 if (!activeCamera())
2865 ISD::CameraChip *tChip = devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD);
2868 if (devices()->getActiveCamera()->hasVideoStream())
2869 types.
append(CAPTURE_TYPE_VIDEO);
2876 if (devices()->getFilterManager().isNull())
2879 return devices()->getFilterManager()->getFilterLabels();
2884 if (devices()->getActiveCamera()->getProperty(
"CCD_GAIN"))
2889 ccdGain[
"GAIN"] = value;
2890 propertyMap[
"CCD_GAIN"] = ccdGain;
2894 propertyMap[
"CCD_GAIN"].remove(
"GAIN");
2895 if (propertyMap[
"CCD_GAIN"].size() == 0)
2896 propertyMap.remove(
"CCD_GAIN");
2899 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2904 ccdGain[
"Gain"] = value;
2905 propertyMap[
"CCD_CONTROLS"] = ccdGain;
2909 propertyMap[
"CCD_CONTROLS"].remove(
"Gain");
2910 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2911 propertyMap.remove(
"CCD_CONTROLS");
2918 if (devices()->getActiveCamera()->getProperty(
"CCD_OFFSET"))
2923 ccdOffset[
"OFFSET"] = value;
2924 propertyMap[
"CCD_OFFSET"] = ccdOffset;
2928 propertyMap[
"CCD_OFFSET"].remove(
"OFFSET");
2929 if (propertyMap[
"CCD_OFFSET"].size() == 0)
2930 propertyMap.remove(
"CCD_OFFSET");
2933 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2938 ccdOffset[
"Offset"] = value;
2939 propertyMap[
"CCD_CONTROLS"] = ccdOffset;
2943 propertyMap[
"CCD_CONTROLS"].remove(
"Offset");
2944 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2945 propertyMap.remove(
"CCD_CONTROLS");
2953 if (!m_FITSViewerWindow.
isNull())
2954 return m_FITSViewerWindow;
2957 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2962 connect(m_FITSViewerWindow.
get(), &FITSViewer::closed,
this, [
this](
int tabIndex)
2964 if (tabIndex == m_fitsvViewerTabIDs.normalTabID)
2965 m_fitsvViewerTabIDs.normalTabID = -1;
2966 else if (tabIndex == m_fitsvViewerTabIDs.calibrationTabID)
2967 m_fitsvViewerTabIDs.calibrationTabID = -1;
2968 else if (tabIndex == m_fitsvViewerTabIDs.focusTabID)
2969 m_fitsvViewerTabIDs.focusTabID = -1;
2970 else if (tabIndex == m_fitsvViewerTabIDs.guideTabID)
2971 m_fitsvViewerTabIDs.guideTabID = -1;
2972 else if (tabIndex == m_fitsvViewerTabIDs.alignTabID)
2973 m_fitsvViewerTabIDs.alignTabID = -1;
2977 connect(m_FITSViewerWindow.
get(), &FITSViewer::terminated,
this, [
this]()
2979 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2980 m_FITSViewerWindow.
clear();
2983 return m_FITSViewerWindow;
2988 return devices()->getActiveCamera();
2991void CameraProcess::checkCaptureOperationsTimeout(
const std::function<
void()> &slot)
2994 if (m_CaptureOperationsTimer.isValid() ==
false)
2995 m_CaptureOperationsTimer.start();
2998 if (state()->getCaptureState() == CAPTURE_PAUSED)
2999 m_CaptureOperationsTimer.restart();
3002 if (m_CaptureOperationsTimer.elapsed() >= Options::captureOperationsTimeout() * 1000)
3004 emit newLog(
i18n(
"Capture operations timed out after %1 seconds.", Options::captureOperationsTimeout()));
3005 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.