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."));
283 if (newJob ==
nullptr)
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 activeJob()->deleteLater();
399 state()->setActiveJob(
nullptr);
407 state()->setCaptureState(targetState);
409 state()->setLooping(
false);
410 state()->setBusy(
false);
412 state()->getCaptureDelayTimer().stop();
414 state()->setActiveJob(
nullptr);
417 if (devices()->lightBox() && state()->lightBoxLightEnabled())
419 state()->setLightBoxLightEnabled(
false);
420 devices()->lightBox()->setLightEnabled(
false);
427 if (devices()->getActiveCamera() && devices()->getActiveChip()
428 && devices()->getActiveCamera()->isFastExposureEnabled())
429 devices()->getActiveChip()->abortExposure();
432 emit captureStopped();
437 if (state()->isCaptureRunning() ==
false)
442 emit newLog(
i18n(
"Pausing only possible while frame capture is running."));
443 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Pause button pressed while not capturing.";
447 state()->setContinueAction(CAPTURE_CONTINUE_ACTION_NONE);
449 emit newLog(
i18n(
"Sequence shall be paused after current exposure is complete."));
454 state()->initCapturePreparation();
460 if (activeCamera() ==
nullptr || activeCamera()->isConnected() ==
false)
462 emit newLog(
i18n(
"No camera detected. Check train configuration and connection settings."));
463 activeJob()->abort();
467 state()->setActiveJob(job);
471 if (job->jobType() == SequenceJob::JOBTYPE_PREVIEW && Options::useFITSViewer() ==
false
472 && Options::useSummaryPreview() ==
false)
477 KSMessageBox::Instance()->disconnect(
this);
478 Options::setUseFITSViewer(
true);
484 KSMessageBox::Instance()->disconnect(
this);
485 activeJob()->abort();
487 KSMessageBox::Instance()->questionYesNo(
i18n(
"No view available for previews. Enable FITS viewer?"),
488 i18n(
"Display preview"), 15);
493 if (state()->isLooping() ==
false)
494 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Preparing capture job" << job->getSignature() <<
"for execution.";
496 if (activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
500 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
501 state()->setNextSequenceID(1);
507 QString signature = activeJob()->getSignature();
513 state()->checkSeqBoundary();
533 int count = state()->capturedFramesCount(signature);
538 for (
auto &a_job : state()->allJobs())
539 if (a_job == activeJob())
541 else if (a_job->getSignature() == activeJob()->getSignature())
542 count -= a_job->getCompleted();
545 updatedCaptureCompleted(count);
549 else if (state()->hasCapturedFramesMap())
552 updatedCaptureCompleted(0);
558 else if (state()->ignoreJobProgress()
559 && activeJob()->getJobProgressIgnored() ==
false)
561 activeJob()->setJobProgressIgnored(
true);
562 updatedCaptureCompleted(0);
567 if (activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
568 activeJob()->getCompleted())
570 updatedCaptureCompleted(activeJob()->getCoreProperty(
571 SequenceJob::SJ_Count).toInt());
572 emit newLog(
i18n(
"Job requires %1-second %2 images, has already %3/%4 captures and does not need to run.",
573 QString(
"%L1").arg(job->getCoreProperty(SequenceJob::SJ_Exposure).
toDouble(), 0,
'f', 3),
574 job->getCoreProperty(SequenceJob::SJ_Filter).
toString(),
575 activeJob()->getCompleted(),
576 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
584 if (activeJob()->getFrameType() != FRAME_VIDEO)
587 emit newLog(
i18n(
"Job requires %1-second %2 images, has %3/%4 frames captured and will be processed.",
588 QString(
"%L1").arg(job->getCoreProperty(SequenceJob::SJ_Exposure).
toDouble(), 0,
'f', 3),
589 job->getCoreProperty(SequenceJob::SJ_Filter).
toString(),
590 activeJob()->getCompleted(),
591 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
596 activeCamera()->setNextSequenceID(state()->nextSequenceID());
600 emit newLog(
i18n(
"Job requires %1 x %2-second %3 video and will be processed.",
601 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt(),
602 QString(
"%L1").arg(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble(), 0,
'f', 3),
603 activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString()));
608 if (activeCamera()->isBLOBEnabled() ==
false)
613 if (Options::guiderType() != Guide::GUIDE_INTERNAL)
615 activeCamera()->setBLOBEnabled(
true);
621 KSMessageBox::Instance()->disconnect(
this);
622 activeCamera()->setBLOBEnabled(
true);
628 KSMessageBox::Instance()->disconnect(
this);
629 activeCamera()->setBLOBEnabled(
true);
630 state()->setBusy(
false);
633 KSMessageBox::Instance()->questionYesNo(
i18n(
"Image transfer is disabled for this camera. Would you like to enable it?"),
634 i18n(
"Image Transfer"), 15);
640 emit jobPrepared(job);
648 if (activeJob() ==
nullptr)
650 qWarning(KSTARS_EKOS_CAPTURE) <<
"prepareActiveJobStage1 with null activeJob().";
665 if (activeJob() ==
nullptr)
667 qWarning(KSTARS_EKOS_CAPTURE) <<
"prepareActiveJobStage2 with null activeJob().";
670 emit newImage(activeJob(), state()->imageData());
691 if (activeJob() ==
nullptr)
693 qWarning(KSTARS_EKOS_CAPTURE) <<
"executeJob with null activeJob().";
698 if (!activeCamera() || !devices()->getActiveChip())
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());
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();
1075 const SequenceJob *job = activeJob();
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)
1182 SequenceJob *thejob = activeJob();
1184 if (thejob ==
nullptr)
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();
1413 SequenceJob * next_job =
nullptr;
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,
1546 auto remoteDirectory = remoteUpload.mid(0, lastSeparator);
1547 auto remoteFilename = remoteUpload.mid(lastSeparator + 1);
1548 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatDirectory, remoteDirectory);
1549 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatFilename, remoteFilename);
1555 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(),
1556 activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION ? FITS_CALIBRATE :
1560 if (state()->isRememberFastExposure())
1562 state()->setRememberFastExposure(
false);
1563 activeCamera()->setFastExposureEnabled(
true);
1566 emit captureTarget(activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString());
1567 emit captureImageStarted();
1572 devices()->setActiveChip(state()->useGuideHead() ?
1573 devices()->getActiveCamera()->getChip(
1574 ISD::CameraChip::GUIDE_CCD) :
1575 devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1576 devices()->getActiveChip()->resetFrame();
1577 emit updateFrameProperties(1);
1583 if (state()->checkCapturing() ==
false)
1586 if (devices()->getActiveChip() != tChip ||
1587 devices()->getActiveChip()->getCaptureMode() != FITS_NORMAL
1588 || state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1591 double deltaMS = std::ceil(1000.0 * value - state()->lastRemainingFrameTimeMS());
1592 emit updateCaptureCountDown(
int(deltaMS));
1593 state()->setLastRemainingFrameTimeMS(state()->lastRemainingFrameTimeMS() + deltaMS);
1597 activeJob()->setExposeLeft(value);
1599 emit newExposureProgress(activeJob());
1602 if (activeJob() && ipstate == IPS_ALERT)
1604 int retries = activeJob()->getCaptureRetires() + 1;
1606 activeJob()->setCaptureRetires(retries);
1608 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
1612 activeJob()->abort();
1616 emit newLog((
i18n(
"Restarting capture attempt #%1", retries)));
1618 state()->setNextSequenceID(1);
1624 if (activeJob() !=
nullptr && ipstate == IPS_OK)
1626 activeJob()->setCaptureRetires(0);
1627 activeJob()->setExposeLeft(0);
1629 if (devices()->getActiveCamera()
1630 && devices()->getActiveCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1632 if (activeJob()->getStatus() == JOB_BUSY)
1634 emit processingFITSfinished(
false);
1639 if (state()->getGuideState() == GUIDE_GUIDING && Options::guiderType() == 0
1640 && state()->suspendGuidingOnDownload())
1642 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Autoguiding suspended until primary CCD chip completes downloading...";
1643 emit suspendGuiding();
1646 emit downloadingFrame();
1649 state()->downloadTimer().start();
1650 state()->downloadProgressTimer().start();
1658 double downloadTimeLeft = state()->averageDownloadTime() - state()->downloadTimer().elapsed() /
1660 if(downloadTimeLeft >= 0)
1662 state()->imageCountDown().setHMS(0, 0, 0);
1663 state()->imageCountDownAddMSecs(
int(std::ceil(downloadTimeLeft * 1000)));
1664 emit newDownloadProgress(downloadTimeLeft);
1672 emit newImage(activeJob(), imageData);
1674 if (activeCamera()->isFastExposureEnabled() ==
false)
1676 const int seqDelay = activeJob()->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1682 if (activeJob() !=
nullptr)
1683 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1686 else if (activeJob() !=
nullptr)
1687 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1697 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE
1698 && state()->downloadTimer().isValid())
1701 double currentDownloadTime = state()->downloadTimer().elapsed() / 1000.0;
1702 state()->addDownloadTime(currentDownloadTime);
1704 state()->downloadTimer().invalidate();
1708 emit newLog(
i18n(
"Download Time: %1 s, New Download Time Estimate: %2 s.", dLTimeString, estimatedTimeString));
1715 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1718 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1720 state()->setActiveJob(
nullptr);
1722 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1723 emit resumeGuiding();
1734 state()->getCaptureTimeout().stop();
1735 state()->setCaptureTimeoutCounter(0);
1737 state()->downloadProgressTimer().stop();
1740 if (state()->isLooping())
1754 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW
1755 || activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
1759 updatedCaptureCompleted(activeJob()->getCompleted() + 1);
1761 state()->getRefocusState()->decreaseInSequenceFocusCounter();
1763 state()->getRefocusState()->setAdaptiveFocusDone(
false);
1767 if (state()->getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_FLIPPING)
1768 state()->decreaseDitherCounter();
1771 state()->addCapturedFrame(activeJob()->getSignature());
1774 emit newLog(
i18n(
"Received image %1 out of %2.", activeJob()->getCompleted(),
1775 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
1778 m_CaptureOperationsTimer.invalidate();
1783 double hfr = -1, eccentricity = -1;
1784 int numStars = -1, median = -1;
1789 if (Options::autoHFR() && imageData && !imageData->areStarsSearched() && imageData->getRecordValue(
"FRAME", frameType)
1790 && frameType.
toString() ==
"Light")
1792#ifdef HAVE_STELLARSOLVER
1795 QVariantMap extractionSettings;
1796 extractionSettings[
"optionsProfileIndex"] = Options::hFROptionsProfile();
1797 extractionSettings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::HFRProfiles);
1798 imageData->setSourceExtractorSettings(extractionSettings);
1803 hfr = imageData->getHFR(HFR_AVERAGE);
1804 numStars = imageData->getSkyBackground().starsDetected;
1805 median = imageData->getMedian();
1806 eccentricity = imageData->getEccentricity();
1807 filename = imageData->filename();
1810 if (state()->isLooping() ==
false && activeJob() !=
nullptr && activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1811 emit newLog(
i18n(
"Captured %1", filename));
1813 auto remainingPlaceholders = PlaceholderPath::remainingPlaceholders(filename);
1814 if (remainingPlaceholders.size() > 0)
1817 i18n(
"WARNING: remaining and potentially unknown placeholders %1 in %2",
1818 remainingPlaceholders.join(
", "), filename));
1824 QVariantMap metadata;
1825 metadata[
"filename"] = filename;
1826 metadata[
"type"] = activeJob()->getFrameType();
1827 metadata[
"exposure"] = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1828 metadata[
"filter"] = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
1829 metadata[
"width"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().width();
1830 metadata[
"height"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().height();
1831 metadata[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1832 metadata[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1833 metadata[
"hfr"] = hfr;
1834 metadata[
"starCount"] = numStars;
1835 metadata[
"median"] = median;
1836 metadata[
"eccentricity"] = eccentricity;
1837 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Captured frame metadata: filename =" << filename <<
", type =" << metadata[
"type"].toInt()
1838 <<
"exposure =" << metadata[
"exposure"].toDouble() <<
"filter =" << metadata[
"filter"].toString() <<
"width =" <<
1839 metadata[
"width"].toInt() <<
"height =" << metadata[
"height"].toInt() <<
"hfr =" << metadata[
"hfr"].toDouble() <<
1840 "starCount =" << metadata[
"starCount"].toInt() <<
"median =" << metadata[
"median"].toInt() <<
"eccentricity =" <<
1841 metadata[
"eccentricity"].toDouble();
1843 emit captureComplete(metadata);
1852 const QString captureScript = activeJob()->getScript(scriptType);
1853 if (captureScript.isEmpty() ==
false && precond)
1855 state()->setCaptureScriptType(scriptType);
1858 emit newLog(
i18n(
"Executing capture script %1", captureScript));
1870 switch (state()->captureScriptType())
1873 emit newLog(
i18n(
"Pre capture script finished with code %1.", exitCode));
1874 if (activeJob() && activeJob()->getStatus() == JOB_IDLE)
1878 m_CaptureOperationsTimer.invalidate();
1884 emit newLog(
i18n(
"Post capture script finished with code %1.", exitCode));
1887 if (activeJob() ==
nullptr
1888 || activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
1889 activeJob()->getCompleted())
1894 else if (state()->checkMeridianFlipReady())
1896 emit newLog(
i18n(
"Processing meridian flip..."));
1906 emit newLog(
i18n(
"Pre job script finished with code %1.", exitCode));
1911 emit newLog(
i18n(
"Post job script finished with code %1.", exitCode));
1925 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1926 if (activeCamera() && trainID.
isValid())
1928 if (activeCamera() && activeCamera()->getDeviceName() == name)
1931 emit refreshCamera(
true);
1934 emit refreshCamera(
false);
1945 if (!activeCamera())
1951 devices()->setActiveChip(
nullptr);
1954 if (activeCamera()->getDeviceName().contains(
"Guider"))
1956 state()->setUseGuideHead(
true);
1957 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::GUIDE_CCD));
1960 if (devices()->getActiveChip() ==
nullptr)
1962 state()->setUseGuideHead(
false);
1963 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1966 emit refreshCameraSettings();
1971 auto pos = std::find_if(state()->DSLRInfos().begin(),
1974 return (oneDSLRInfo[
"Model"] == model);
1978 if (pos != state()->DSLRInfos().end())
1981 devices()->getActiveChip()->setImageInfo(camera[
"Width"].toInt(),
1982 camera[
"Height"].toInt(),
1983 camera[
"PixelW"].toDouble(),
1984 camera[
"PixelH"].toDouble(),
1991 if (activeCamera() && activeCamera()->getDeviceName() == camera)
1994 auto rememberState = state()->getCaptureState();
1997 state()->setCaptureState(rememberState);
2000 state()->setCaptureTimeoutCounter(0);
2004 devices()->setActiveChip(devices()->getActiveChip());
2018 auto name = device->getDeviceName();
2019 device->disconnect(
this);
2022 if (devices()->mount() && devices()->mount()->getDeviceName() == device->getDeviceName())
2024 devices()->mount()->disconnect(
this);
2025 devices()->setMount(
nullptr);
2026 if (activeJob() !=
nullptr)
2027 activeJob()->addMount(
nullptr);
2031 if (devices()->dome() && devices()->dome()->getDeviceName() == device->getDeviceName())
2033 devices()->dome()->disconnect(
this);
2034 devices()->setDome(
nullptr);
2038 if (devices()->rotator() && devices()->rotator()->getDeviceName() == device->getDeviceName())
2040 devices()->rotator()->disconnect(
this);
2041 devices()->setRotator(
nullptr);
2045 if (devices()->dustCap() && devices()->dustCap()->getDeviceName() == device->getDeviceName())
2047 devices()->dustCap()->disconnect(
this);
2048 devices()->setDustCap(
nullptr);
2049 state()->hasDustCap =
false;
2050 state()->setDustCapState(CAP_UNKNOWN);
2054 if (devices()->lightBox() && devices()->lightBox()->getDeviceName() == device->getDeviceName())
2056 devices()->lightBox()->disconnect(
this);
2057 devices()->setLightBox(
nullptr);
2058 state()->hasLightBox =
false;
2059 state()->setLightBoxLightState(CAP_LIGHT_UNKNOWN);
2063 if (activeCamera() && activeCamera()->getDeviceName() == name)
2065 activeCamera()->disconnect(
this);
2066 devices()->setActiveCamera(
nullptr);
2067 devices()->setActiveChip(
nullptr);
2070 if (INDIListener::findDevice(name, generic))
2071 DarkLibrary::Instance()->removeDevice(generic);
2077 if (devices()->filterWheel() && devices()->filterWheel()->getDeviceName() == name)
2079 devices()->filterWheel()->disconnect(
this);
2080 devices()->setFilterWheel(
nullptr);
2084 emit refreshFilterSettings();
2091 state()->setCaptureTimeoutCounter(state()->captureTimeoutCounter() + 1);
2093 if (state()->deviceRestartCounter() >= 3)
2095 state()->setCaptureTimeoutCounter(0);
2096 state()->setDeviceRestartCounter(0);
2097 emit newLog(
i18n(
"Exposure timeout. Aborting..."));
2102 if (state()->captureTimeoutCounter() > 3 && activeCamera())
2104 emit newLog(
i18n(
"Exposure timeout. More than 3 have been detected, will restart driver."));
2105 QString camera = activeCamera()->getDeviceName();
2106 QString fw = (devices()->filterWheel() !=
nullptr) ?
2107 devices()->filterWheel()->getDeviceName() :
"";
2108 emit driverTimedout(camera);
2111 state()->setDeviceRestartCounter(state()->deviceRestartCounter() + 1);
2119 if (activeCamera() && activeJob())
2122 emit newLog(
i18n(
"Exposure timeout. Restarting exposure..."));
2123 activeCamera()->setEncodingFormat(
"FITS");
2124 auto rememberState = state()->getCaptureState();
2127 state()->setCaptureState(rememberState);
2129 auto targetChip = activeCamera()->getChip(state()->useGuideHead() ?
2130 ISD::CameraChip::GUIDE_CCD :
2131 ISD::CameraChip::PRIMARY_CCD);
2132 targetChip->abortExposure();
2133 const double exptime = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
2134 targetChip->capture(exptime);
2135 state()->getCaptureTimeout().start(
static_cast<int>((exptime) * 1000 + CAPTURE_TIMEOUT_THRESHOLD));
2139 else if (state()->captureTimeoutCounter() < 40)
2141 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Unable to restart exposure as camera is missing, trying again in 5 seconds...";
2146 state()->setCaptureTimeoutCounter(0);
2147 state()->setDeviceRestartCounter(0);
2148 emit newLog(
i18n(
"Exposure timeout. Too many. Aborting..."));
2161 if (type == ISD::Camera::ERROR_CAPTURE)
2163 int retries = activeJob()->getCaptureRetires() + 1;
2165 activeJob()->setCaptureRetires(retries);
2167 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
2175 emit newLog(
i18n(
"Restarting capture attempt #%1", retries));
2177 state()->setNextSequenceID(1);
2194 double currentADU = imageData->getADU();
2195 bool outOfRange =
false, saturated =
false;
2197 switch (imageData->bpp())
2200 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT8_MAX)
2202 else if (currentADU / UINT8_MAX > 0.95)
2207 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT16_MAX)
2209 else if (currentADU / UINT16_MAX > 0.95)
2214 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT32_MAX)
2216 else if (currentADU / UINT32_MAX > 0.95)
2226 emit newLog(
i18n(
"Flat calibration failed. Captured image is only %1-bit while requested ADU is %2.",
2228 ,
QString::number(activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble(),
'f', 2)));
2234 double nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.1;
2235 nextExposure = qBound(exp_min, nextExposure, exp_max);
2237 emit newLog(
i18n(
"Current image is saturated (%1). Next exposure is %2 seconds.",
2240 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2241 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2242 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2244 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2250 double ADUDiff = fabs(currentADU - activeJob()->getCoreProperty(
2251 SequenceJob::SJ_TargetADU).toDouble());
2254 if (ADUDiff <= state()->targetADUTolerance())
2256 if (activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
2259 i18n(
"Current ADU %1 within target ADU tolerance range.",
QString::number(currentADU,
'f', 0)));
2260 activeCamera()->setUploadMode(activeJob()->getUploadMode());
2261 auto placeholderPath = PlaceholderPath();
2263 placeholderPath.processJobInfo(activeJob());
2265 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
2270 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
2271 state()->checkSeqBoundary();
2277 double nextExposure = -1;
2280 if (std::fabs(imageData->getMax(0) - imageData->getMin(0)) < 10)
2281 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.5;
2285 if (nextExposure <= 0 || std::isnan(nextExposure))
2288 i18n(
"Unable to calculate optimal exposure settings, please capture the flats manually."));
2294 nextExposure = qBound(exp_min, nextExposure, exp_max);
2296 emit newLog(
i18n(
"Current ADU is %1 Next exposure is %2 seconds.",
QString::number(currentADU,
'f', 0),
2297 QString(
"%L1").arg(nextExposure, 0,
'f', 6)));
2299 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2300 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2301 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2303 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2314 if (activeJob() ==
nullptr)
2316 qWarning(KSTARS_EKOS_CAPTURE) <<
"setCurrentADU with null activeJob().";
2321 double nextExposure = 0;
2322 double targetADU = activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble();
2323 std::vector<double> coeff;
2327 if(activeJob()->getCoreProperty(SequenceJob::SJ_SkyFlat).toBool() && ExpRaw.size() > 2)
2329 int remove = ExpRaw.size() - 2;
2330 ExpRaw.remove(0, remove);
2331 ADURaw.remove(0, remove);
2335 ExpRaw.append(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
2336 ADURaw.append(currentADU);
2338 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Capture: Current ADU = " << currentADU <<
" targetADU = " << targetADU
2339 <<
" Exposure Count: " << ExpRaw.count();
2343 if (ExpRaw.count() >= 2)
2345 if (ExpRaw.count() >= 5)
2349 coeff = gsl_polynomial_fit(ADURaw.data(), ExpRaw.data(), ExpRaw.count(), 2, chisq);
2350 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Running polynomial fitting. Found " << coeff.size() <<
" coefficients.";
2351 if (std::isnan(coeff[0]) || std::isinf(coeff[0]))
2353 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coefficients are invalid.";
2354 targetADUAlgorithm = ADU_LEAST_SQUARES;
2358 nextExposure = coeff[0] + (coeff[1] * targetADU) + (coeff[2] * pow(targetADU, 2));
2360 if (nextExposure < 0 || (nextExposure > ExpRaw.last() || targetADU < ADURaw.last())
2361 || (nextExposure < ExpRaw.last() || targetADU > ADURaw.last()))
2364 targetADUAlgorithm = ADU_LEAST_SQUARES;
2368 targetADUAlgorithm = ADU_POLYNOMIAL;
2369 for (
size_t i = 0; i < coeff.size(); i++)
2370 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coeff #" << i <<
"=" << coeff[i];
2375 bool looping =
false;
2376 if (ExpRaw.count() >= 10)
2378 int size = ExpRaw.count();
2379 looping = (std::fabs(ExpRaw[size - 1] - ExpRaw[size - 2] < 0.01)) &&
2380 (std::fabs(ExpRaw[size - 2] - ExpRaw[size - 3] < 0.01));
2381 if (looping && targetADUAlgorithm == ADU_POLYNOMIAL)
2383 qWarning(KSTARS_EKOS_CAPTURE) <<
"Detected looping in polynomial results. Falling back to llsqr.";
2384 targetADUAlgorithm = ADU_LEAST_SQUARES;
2391 if (targetADUAlgorithm == ADU_LEAST_SQUARES)
2393 double a = 0, b = 0;
2394 llsq(ExpRaw, ADURaw, a, b);
2399 nextExposure = (targetADU - b) / a;
2401 if (nextExposure < 0)
2409 if (nextExposure == 0.0 || nextExposure > 180)
2411 if (currentADU < targetADU)
2412 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 1.25;
2414 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * .75;
2417 qCDebug(KSTARS_EKOS_CAPTURE) <<
"next flat exposure is" << nextExposure;
2419 return nextExposure;
2429QString Ekos::CameraProcess::createTabTitle(
const FITSMode &captureMode,
const QString &deviceName)
2431 const bool isPreview = (activeJob() ==
nullptr || (activeJob() && activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW));
2432 if (isPreview && Options::singlePreviewFITS())
2436 if (Options::singleWindowCapturedFITS())
2437 return (
i18n(
"%1 Preview", deviceName));
2440 return(
i18n(
"Preview"));
2442 else if (captureMode == FITS_CALIBRATE)
2446 const QString filtername = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
2447 if (filtername ==
"")
2450 return(
QString(
"%1 %2").arg(filtername).arg(
i18n(
"Flat Calibration")));
2453 return(
i18n(
"Calibration"));
2459 const FITSScale &captureFilter,
const QString &filename,
const QString &deviceName)
2465 switch (captureMode)
2468 case FITS_CALIBRATE:
2470 if (Options::useFITSViewer())
2473 bool success =
false;
2477 QString tabTitle = createTabTitle(captureMode, deviceName);
2480 int *tabID = &m_fitsvViewerTabIDs.normalTabID;
2481 if (*tabID == -1 || Options::singlePreviewFITS() ==
false)
2484 success = getFITSViewer()->loadData(data, fileURL, &tabIndex, captureMode, captureFilter, tabTitle);
2487 auto tabs = getFITSViewer()->tabs();
2488 if (tabIndex < tabs.size() && captureMode == FITS_NORMAL)
2490 emit newView(tabs[tabIndex]->getView());
2491 tabs[tabIndex]->disconnect(
this);
2492 connect(tabs[tabIndex].get(), &FITSTab::updated,
this, [
this]
2495 emit newView(tab->getView());
2501 success = getFITSViewer()->updateData(data, fileURL, *tabID, &tabIndex, captureMode, captureFilter, tabTitle);
2508 qCCritical(KSTARS_EKOS_CAPTURE()) <<
"error adding/updating FITS";
2512 if (Options::focusFITSOnNewImage())
2513 getFITSViewer()->raise();
2526 FITSMode captureMode = tChip ==
nullptr ? FITS_UNKNOWN : tChip->getCaptureMode();
2527 FITSScale captureFilter = tChip ==
nullptr ? FITS_NONE : tChip->getCaptureFilter();
2528 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
2534 if (m_VideoWindow.isNull() && activeCamera() !=
nullptr)
2536 m_VideoWindow.reset(
new StreamWG(activeCamera()));
2541 connect(activeCamera(), &ISD::Camera::videoRecordToggled, m_VideoWindow.get(), &StreamWG::enableStream,
2547 return m_VideoWindow;
2550void CameraProcess::updateVideoWindow(
int width,
int height,
bool streamEnabled)
2554 if (width > 0 && height > 0)
2559void CameraProcess::closeVideoWindow()
2561 if (m_VideoWindow.
isNull())
2564 m_VideoWindow->close();
2567void CameraProcess::showVideoFrame(INDI::Property prop,
int width,
int height)
2578 const QString &targetName,
bool setOptions)
2580 state()->clearCapturedFramesMap();
2581 auto queue = state()->getSequenceQueue();
2582 if (!queue->load(fileURL, targetName, devices(), state()))
2584 QString message =
i18n(
"Unable to open file %1", fileURL);
2585 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2591 queue->setOptions();
2593 state()->updateHFRThreshold();
2596 for (
auto j : state()->allJobs())
2605 state()->getSequenceQueue()->loadOptions();
2606 return state()->getSequenceQueue()->save(path, state()->observerName());
2618 connect(activeCamera(), &ISD::Camera::videoRecordToggled,
this, &CameraProcess::updateVideoRecordStatus,
2632 disconnect(activeCamera(), &ISD::Camera::ready,
this, &CameraProcess::cameraReady);
2639 if (devices()->filterWheel() && devices()->filterWheel() == device)
2642 if (devices()->filterWheel())
2643 devices()->filterWheel()->disconnect(
this);
2645 devices()->setFilterWheel(device);
2647 return (device !=
nullptr);
2654 emit newLog(
i18n(
"Sequence paused."));
2659 state()->setContinueAction(continueAction);
2669 SequenceJob * first_job =
nullptr;
2672 for (
auto &job : state()->allJobs())
2674 if (job->getStatus() == JOB_IDLE || job->getStatus() == JOB_ABORTED)
2683 if (first_job ==
nullptr)
2686 for (
auto &job : state()->allJobs())
2688 if (job->getStatus() != JOB_DONE)
2691 if (state()->getCaptureDelayTimer().isActive())
2693 if (state()->getCaptureDelayTimer().interval() <= 0)
2694 state()->getCaptureDelayTimer().setInterval(1000);
2701 if (!state()->ignoreJobProgress())
2704 i18n(
"All jobs are complete. Do you want to reset the status of all jobs and restart capturing?"),
2712 first_job = state()->allJobs().first();
2716 else if (state()->ignoreJobProgress())
2718 emit newLog(
i18n(
"Warning: option \"Always Reset Sequence When Starting\" is enabled and resets the sequence counts."));
2725void CameraProcess::resetJobStatus(JOBStatus newStatus)
2727 if (activeJob() !=
nullptr)
2729 activeJob()->resetStatus(newStatus);
2730 emit updateJobTable(activeJob());
2734void CameraProcess::resetAllJobs()
2736 for (
auto &job : state()->allJobs())
2741 m_State->clearCapturedFramesMap();
2743 emit updateJobTable(
nullptr);
2746void CameraProcess::updatedCaptureCompleted(
int count)
2748 activeJob()->setCompleted(count);
2749 emit updateJobTable(activeJob());
2752void CameraProcess::updateVideoRecordStatus(
bool enabled)
2755 if (activeJob() ==
nullptr)
2758 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Video recording" << (enabled ?
"started." :
"stopped.");
2760 if (enabled ==
false)
2762 updatedCaptureCompleted(activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt());
2767void CameraProcess::llsq(QVector<double> x, QVector<double> y,
double &a,
double &b)
2789 for (i = 0; i < n; i++)
2794 xbar = xbar /
static_cast<double>(n);
2795 ybar = ybar /
static_cast<double>(n);
2801 for (i = 0; i < n; i++)
2803 top = top + (x[i] - xbar) * (y[i] - ybar);
2804 bot = bot + (x[i] - xbar) * (x[i] - xbar);
2809 b = ybar - a * xbar;
2821 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2829 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2830 return devices()->getActiveCamera()->setCoolerControl(enable);
2839 KSMessageBox::Instance()->disconnect(
this);
2841 emit driverTimedout(name);
2845 KSMessageBox::Instance()->disconnect(
this);
2848 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to restart %1 camera driver?", name),
2849 i18n(
"Driver Restart"), 5);
2854 if (!activeCamera())
2857 ISD::CameraChip *tChip = devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD);
2860 if (devices()->getActiveCamera()->hasVideoStream())
2861 types.
append(CAPTURE_TYPE_VIDEO);
2868 if (devices()->getFilterManager().isNull())
2871 return devices()->getFilterManager()->getFilterLabels();
2876 if (devices()->getActiveCamera()->getProperty(
"CCD_GAIN"))
2881 ccdGain[
"GAIN"] = value;
2882 propertyMap[
"CCD_GAIN"] = ccdGain;
2886 propertyMap[
"CCD_GAIN"].remove(
"GAIN");
2887 if (propertyMap[
"CCD_GAIN"].size() == 0)
2888 propertyMap.remove(
"CCD_GAIN");
2891 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2896 ccdGain[
"Gain"] = value;
2897 propertyMap[
"CCD_CONTROLS"] = ccdGain;
2901 propertyMap[
"CCD_CONTROLS"].remove(
"Gain");
2902 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2903 propertyMap.remove(
"CCD_CONTROLS");
2910 if (devices()->getActiveCamera()->getProperty(
"CCD_OFFSET"))
2915 ccdOffset[
"OFFSET"] = value;
2916 propertyMap[
"CCD_OFFSET"] = ccdOffset;
2920 propertyMap[
"CCD_OFFSET"].remove(
"OFFSET");
2921 if (propertyMap[
"CCD_OFFSET"].size() == 0)
2922 propertyMap.remove(
"CCD_OFFSET");
2925 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2930 ccdOffset[
"Offset"] = value;
2931 propertyMap[
"CCD_CONTROLS"] = ccdOffset;
2935 propertyMap[
"CCD_CONTROLS"].remove(
"Offset");
2936 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2937 propertyMap.remove(
"CCD_CONTROLS");
2945 if (!m_FITSViewerWindow.
isNull())
2946 return m_FITSViewerWindow;
2949 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2954 connect(m_FITSViewerWindow.
get(), &FITSViewer::closed,
this, [
this](
int tabIndex)
2956 if (tabIndex == m_fitsvViewerTabIDs.normalTabID)
2957 m_fitsvViewerTabIDs.normalTabID = -1;
2958 else if (tabIndex == m_fitsvViewerTabIDs.calibrationTabID)
2959 m_fitsvViewerTabIDs.calibrationTabID = -1;
2960 else if (tabIndex == m_fitsvViewerTabIDs.focusTabID)
2961 m_fitsvViewerTabIDs.focusTabID = -1;
2962 else if (tabIndex == m_fitsvViewerTabIDs.guideTabID)
2963 m_fitsvViewerTabIDs.guideTabID = -1;
2964 else if (tabIndex == m_fitsvViewerTabIDs.alignTabID)
2965 m_fitsvViewerTabIDs.alignTabID = -1;
2969 connect(m_FITSViewerWindow.
get(), &FITSViewer::terminated,
this, [
this]()
2971 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2972 m_FITSViewerWindow.
clear();
2975 return m_FITSViewerWindow;
2980 return devices()->getActiveCamera();
2983void CameraProcess::checkCaptureOperationsTimeout(
const std::function<
void()> &slot)
2986 if (m_CaptureOperationsTimer.isValid() ==
false)
2987 m_CaptureOperationsTimer.start();
2990 if (state()->getCaptureState() == CAPTURE_PAUSED)
2991 m_CaptureOperationsTimer.restart();
2994 if (m_CaptureOperationsTimer.elapsed() >= Options::captureOperationsTimeout() * 1000)
2996 emit newLog(
i18n(
"Capture operations timed out after %1 seconds.", Options::captureOperationsTimeout()));
2997 stopCapturing(CAPTURE_ABORTED);
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
Q_SCRIPTABLE void executeJob()
executeJob Start the execution of activeJob by initiating updatePreCaptureCalibrationStatus().
SequenceJob * findNextPendingJob()
findExecutableJob find next job to be executed
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 prepareActiveJobStage2()
prepareActiveJobStage2 Reset #calibrationStage and continue with preparePreCaptureActions().
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 startJob(SequenceJob *job)
startJob Start the execution of a selected sequence job:
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 jobCreated(SequenceJob *newJob)
Counterpart to the event {.
void prepareJob(SequenceJob *job)
prepareJob Update the counters of existing frames and continue with prepareActiveJob(),...
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
double toDouble(bool *ok) const const
QRect toRect() const const
QString toString() const const
uint toUInt(bool *ok) const const
Object to hold FITS Header records.