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,
1544 auto remoteDirectory = remoteUpload.mid(0, lastSeparator);
1545 auto remoteFilename = remoteUpload.mid(lastSeparator + 1);
1546 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatDirectory, remoteDirectory);
1547 activeJob()->setCoreProperty(SequenceJob::SJ_RemoteFormatFilename, remoteFilename);
1553 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(),
1554 activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION ? FITS_CALIBRATE :
1558 if (state()->isRememberFastExposure())
1560 state()->setRememberFastExposure(
false);
1561 activeCamera()->setFastExposureEnabled(
true);
1564 emit captureTarget(activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString());
1565 emit captureImageStarted();
1570 devices()->setActiveChip(state()->useGuideHead() ?
1571 devices()->getActiveCamera()->getChip(
1572 ISD::CameraChip::GUIDE_CCD) :
1573 devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1574 devices()->getActiveChip()->resetFrame();
1575 emit updateFrameProperties(1);
1581 if (state()->checkCapturing() ==
false)
1584 if (devices()->getActiveChip() != tChip ||
1585 devices()->getActiveChip()->getCaptureMode() != FITS_NORMAL
1586 || state()->getMeridianFlipState()->getMeridianFlipStage() >= MeridianFlipState::MF_ALIGNING)
1589 double deltaMS = std::ceil(1000.0 * value - state()->lastRemainingFrameTimeMS());
1590 emit updateCaptureCountDown(
int(deltaMS));
1591 state()->setLastRemainingFrameTimeMS(state()->lastRemainingFrameTimeMS() + deltaMS);
1595 activeJob()->setExposeLeft(value);
1597 emit newExposureProgress(activeJob());
1600 if (activeJob() && ipstate == IPS_ALERT)
1602 int retries = activeJob()->getCaptureRetires() + 1;
1604 activeJob()->setCaptureRetires(retries);
1606 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
1610 activeJob()->abort();
1614 emit newLog((
i18n(
"Restarting capture attempt #%1", retries)));
1616 state()->setNextSequenceID(1);
1622 if (activeJob() !=
nullptr && ipstate == IPS_OK)
1624 activeJob()->setCaptureRetires(0);
1625 activeJob()->setExposeLeft(0);
1627 if (devices()->getActiveCamera()
1628 && devices()->getActiveCamera()->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
1630 if (activeJob()->getStatus() == JOB_BUSY)
1632 emit processingFITSfinished(
false);
1637 if (state()->getGuideState() == GUIDE_GUIDING && Options::guiderType() == 0
1638 && state()->suspendGuidingOnDownload())
1640 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Autoguiding suspended until primary CCD chip completes downloading...";
1641 emit suspendGuiding();
1644 emit downloadingFrame();
1647 state()->downloadTimer().start();
1648 state()->downloadProgressTimer().start();
1656 double downloadTimeLeft = state()->averageDownloadTime() - state()->downloadTimer().elapsed() /
1658 if(downloadTimeLeft >= 0)
1660 state()->imageCountDown().setHMS(0, 0, 0);
1661 state()->imageCountDownAddMSecs(
int(std::ceil(downloadTimeLeft * 1000)));
1662 emit newDownloadProgress(downloadTimeLeft);
1670 emit newImage(activeJob(), imageData);
1672 if (activeCamera()->isFastExposureEnabled() ==
false)
1674 const int seqDelay = activeJob()->getCoreProperty(SequenceJob::SJ_Delay).toInt();
1680 if (activeJob() !=
nullptr)
1681 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1684 else if (activeJob() !=
nullptr)
1685 activeJob()->startCapturing(state()->getRefocusState()->isAutoFocusReady(), FITS_NORMAL);
1695 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE
1696 && state()->downloadTimer().isValid())
1699 double currentDownloadTime = state()->downloadTimer().elapsed() / 1000.0;
1700 state()->addDownloadTime(currentDownloadTime);
1702 state()->downloadTimer().invalidate();
1706 emit newLog(
i18n(
"Download Time: %1 s, New Download Time Estimate: %2 s.", dLTimeString, estimatedTimeString));
1713 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW)
1716 activeCamera()->setUploadMode(activeJob()->getUploadMode());
1718 state()->setActiveJob(
nullptr);
1720 if (state()->getGuideState() == GUIDE_SUSPENDED && state()->suspendGuidingOnDownload())
1721 emit resumeGuiding();
1732 state()->getCaptureTimeout().stop();
1733 state()->setCaptureTimeoutCounter(0);
1735 state()->downloadProgressTimer().stop();
1738 if (state()->isLooping())
1752 if (activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW
1753 || activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
1757 updatedCaptureCompleted(activeJob()->getCompleted() + 1);
1759 state()->getRefocusState()->decreaseInSequenceFocusCounter();
1761 state()->getRefocusState()->setAdaptiveFocusDone(
false);
1765 if (state()->getMeridianFlipState()->getMeridianFlipStage() < MeridianFlipState::MF_FLIPPING)
1766 state()->decreaseDitherCounter();
1769 state()->addCapturedFrame(activeJob()->getSignature());
1772 emit newLog(
i18n(
"Received image %1 out of %2.", activeJob()->getCompleted(),
1773 activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt()));
1776 m_CaptureOperationsTimer.invalidate();
1781 double hfr = -1, eccentricity = -1;
1782 int numStars = -1, median = -1;
1787 if (Options::autoHFR() && imageData && !imageData->areStarsSearched() && imageData->getRecordValue(
"FRAME", frameType)
1788 && frameType.
toString() ==
"Light")
1790#ifdef HAVE_STELLARSOLVER
1793 QVariantMap extractionSettings;
1794 extractionSettings[
"optionsProfileIndex"] = Options::hFROptionsProfile();
1795 extractionSettings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::HFRProfiles);
1796 imageData->setSourceExtractorSettings(extractionSettings);
1801 hfr = imageData->getHFR(HFR_AVERAGE);
1802 numStars = imageData->getSkyBackground().starsDetected;
1803 median = imageData->getMedian();
1804 eccentricity = imageData->getEccentricity();
1805 filename = imageData->filename();
1808 if (state()->isLooping() ==
false && activeJob() !=
nullptr && activeJob()->jobType() != SequenceJob::JOBTYPE_PREVIEW)
1809 emit newLog(
i18n(
"Captured %1", filename));
1811 auto remainingPlaceholders = PlaceholderPath::remainingPlaceholders(filename);
1812 if (remainingPlaceholders.size() > 0)
1815 i18n(
"WARNING: remaining and potentially unknown placeholders %1 in %2",
1816 remainingPlaceholders.join(
", "), filename));
1822 QVariantMap metadata;
1823 metadata[
"filename"] = filename;
1824 metadata[
"type"] = activeJob()->getFrameType();
1825 metadata[
"exposure"] = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
1826 metadata[
"filter"] = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
1827 metadata[
"width"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().width();
1828 metadata[
"height"] = activeJob()->getCoreProperty(SequenceJob::SJ_ROI).toRect().height();
1829 metadata[
"binx"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().x();
1830 metadata[
"biny"] = activeJob()->getCoreProperty(SequenceJob::SJ_Binning).toPoint().y();
1831 metadata[
"hfr"] = hfr;
1832 metadata[
"starCount"] = numStars;
1833 metadata[
"median"] = median;
1834 metadata[
"eccentricity"] = eccentricity;
1835 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Captured frame metadata: filename =" << filename <<
", type =" << metadata[
"type"].toInt()
1836 <<
"exposure =" << metadata[
"exposure"].toDouble() <<
"filter =" << metadata[
"filter"].toString() <<
"width =" <<
1837 metadata[
"width"].toInt() <<
"height =" << metadata[
"height"].toInt() <<
"hfr =" << metadata[
"hfr"].toDouble() <<
1838 "starCount =" << metadata[
"starCount"].toInt() <<
"median =" << metadata[
"median"].toInt() <<
"eccentricity =" <<
1839 metadata[
"eccentricity"].toDouble();
1841 emit captureComplete(metadata);
1850 const QString captureScript = activeJob()->getScript(scriptType);
1851 if (captureScript.isEmpty() ==
false && precond)
1853 state()->setCaptureScriptType(scriptType);
1856 emit newLog(
i18n(
"Executing capture script %1", captureScript));
1868 switch (state()->captureScriptType())
1871 emit newLog(
i18n(
"Pre capture script finished with code %1.", exitCode));
1872 if (activeJob() && activeJob()->getStatus() == JOB_IDLE)
1876 m_CaptureOperationsTimer.invalidate();
1882 emit newLog(
i18n(
"Post capture script finished with code %1.", exitCode));
1885 if (activeJob() ==
nullptr
1886 || activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt() <=
1887 activeJob()->getCompleted())
1892 else if (state()->checkMeridianFlipReady())
1894 emit newLog(
i18n(
"Processing meridian flip..."));
1904 emit newLog(
i18n(
"Pre job script finished with code %1.", exitCode));
1909 emit newLog(
i18n(
"Post job script finished with code %1.", exitCode));
1923 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::CaptureOpticalTrain);
1924 if (activeCamera() && trainID.
isValid())
1926 if (activeCamera() && activeCamera()->getDeviceName() == name)
1929 emit refreshCamera(
true);
1932 emit refreshCamera(
false);
1943 if (!activeCamera())
1949 devices()->setActiveChip(
nullptr);
1952 if (activeCamera()->getDeviceName().contains(
"Guider"))
1954 state()->setUseGuideHead(
true);
1955 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::GUIDE_CCD));
1958 if (devices()->getActiveChip() ==
nullptr)
1960 state()->setUseGuideHead(
false);
1961 devices()->setActiveChip(activeCamera()->getChip(ISD::CameraChip::PRIMARY_CCD));
1964 emit refreshCameraSettings();
1969 auto pos = std::find_if(state()->DSLRInfos().begin(),
1972 return (oneDSLRInfo[
"Model"] == model);
1976 if (pos != state()->DSLRInfos().end())
1979 devices()->getActiveChip()->setImageInfo(camera[
"Width"].toInt(),
1980 camera[
"Height"].toInt(),
1981 camera[
"PixelW"].toDouble(),
1982 camera[
"PixelH"].toDouble(),
1989 if (activeCamera() && activeCamera()->getDeviceName() == camera)
1992 auto rememberState = state()->getCaptureState();
1995 state()->setCaptureState(rememberState);
1998 state()->setCaptureTimeoutCounter(0);
2002 devices()->setActiveChip(devices()->getActiveChip());
2016 auto name = device->getDeviceName();
2017 device->disconnect(
this);
2020 if (devices()->mount() && devices()->mount()->getDeviceName() == device->getDeviceName())
2022 devices()->mount()->disconnect(
this);
2023 devices()->setMount(
nullptr);
2024 if (activeJob() !=
nullptr)
2025 activeJob()->addMount(
nullptr);
2029 if (devices()->dome() && devices()->dome()->getDeviceName() == device->getDeviceName())
2031 devices()->dome()->disconnect(
this);
2032 devices()->setDome(
nullptr);
2036 if (devices()->rotator() && devices()->rotator()->getDeviceName() == device->getDeviceName())
2038 devices()->rotator()->disconnect(
this);
2039 devices()->setRotator(
nullptr);
2043 if (devices()->dustCap() && devices()->dustCap()->getDeviceName() == device->getDeviceName())
2045 devices()->dustCap()->disconnect(
this);
2046 devices()->setDustCap(
nullptr);
2047 state()->hasDustCap =
false;
2048 state()->setDustCapState(CAP_UNKNOWN);
2052 if (devices()->lightBox() && devices()->lightBox()->getDeviceName() == device->getDeviceName())
2054 devices()->lightBox()->disconnect(
this);
2055 devices()->setLightBox(
nullptr);
2056 state()->hasLightBox =
false;
2057 state()->setLightBoxLightState(CAP_LIGHT_UNKNOWN);
2061 if (activeCamera() && activeCamera()->getDeviceName() == name)
2063 activeCamera()->disconnect(
this);
2064 devices()->setActiveCamera(
nullptr);
2065 devices()->setActiveChip(
nullptr);
2068 if (INDIListener::findDevice(name, generic))
2069 DarkLibrary::Instance()->removeDevice(generic);
2075 if (devices()->filterWheel() && devices()->filterWheel()->getDeviceName() == name)
2077 devices()->filterWheel()->disconnect(
this);
2078 devices()->setFilterWheel(
nullptr);
2082 emit refreshFilterSettings();
2089 state()->setCaptureTimeoutCounter(state()->captureTimeoutCounter() + 1);
2091 if (state()->deviceRestartCounter() >= 3)
2093 state()->setCaptureTimeoutCounter(0);
2094 state()->setDeviceRestartCounter(0);
2095 emit newLog(
i18n(
"Exposure timeout. Aborting..."));
2100 if (state()->captureTimeoutCounter() > 3 && activeCamera())
2102 emit newLog(
i18n(
"Exposure timeout. More than 3 have been detected, will restart driver."));
2103 QString camera = activeCamera()->getDeviceName();
2104 QString fw = (devices()->filterWheel() !=
nullptr) ?
2105 devices()->filterWheel()->getDeviceName() :
"";
2106 emit driverTimedout(camera);
2109 state()->setDeviceRestartCounter(state()->deviceRestartCounter() + 1);
2117 if (activeCamera() && activeJob())
2120 emit newLog(
i18n(
"Exposure timeout. Restarting exposure..."));
2121 activeCamera()->setEncodingFormat(
"FITS");
2122 auto rememberState = state()->getCaptureState();
2125 state()->setCaptureState(rememberState);
2127 auto targetChip = activeCamera()->getChip(state()->useGuideHead() ?
2128 ISD::CameraChip::GUIDE_CCD :
2129 ISD::CameraChip::PRIMARY_CCD);
2130 targetChip->abortExposure();
2131 const double exptime = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
2132 targetChip->capture(exptime);
2133 state()->getCaptureTimeout().start(
static_cast<int>((exptime) * 1000 + CAPTURE_TIMEOUT_THRESHOLD));
2137 else if (state()->captureTimeoutCounter() < 40)
2139 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Unable to restart exposure as camera is missing, trying again in 5 seconds...";
2144 state()->setCaptureTimeoutCounter(0);
2145 state()->setDeviceRestartCounter(0);
2146 emit newLog(
i18n(
"Exposure timeout. Too many. Aborting..."));
2159 if (type == ISD::Camera::ERROR_CAPTURE)
2161 int retries = activeJob()->getCaptureRetires() + 1;
2163 activeJob()->setCaptureRetires(retries);
2165 emit newLog(
i18n(
"Capture failed. Check INDI Control Panel for details."));
2173 emit newLog(
i18n(
"Restarting capture attempt #%1", retries));
2175 state()->setNextSequenceID(1);
2192 double currentADU = imageData->getADU();
2193 bool outOfRange =
false, saturated =
false;
2195 switch (imageData->bpp())
2198 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT8_MAX)
2200 else if (currentADU / UINT8_MAX > 0.95)
2205 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT16_MAX)
2207 else if (currentADU / UINT16_MAX > 0.95)
2212 if (activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble() > UINT32_MAX)
2214 else if (currentADU / UINT32_MAX > 0.95)
2224 emit newLog(
i18n(
"Flat calibration failed. Captured image is only %1-bit while requested ADU is %2.",
2226 ,
QString::number(activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble(),
'f', 2)));
2232 double nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.1;
2233 nextExposure = qBound(exp_min, nextExposure, exp_max);
2235 emit newLog(
i18n(
"Current image is saturated (%1). Next exposure is %2 seconds.",
2238 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2239 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2240 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2242 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2248 double ADUDiff = fabs(currentADU - activeJob()->getCoreProperty(
2249 SequenceJob::SJ_TargetADU).toDouble());
2252 if (ADUDiff <= state()->targetADUTolerance())
2254 if (activeJob()->getCalibrationStage() == SequenceJobState::CAL_CALIBRATION)
2257 i18n(
"Current ADU %1 within target ADU tolerance range.",
QString::number(currentADU,
'f', 0)));
2258 activeCamera()->setUploadMode(activeJob()->getUploadMode());
2259 auto placeholderPath = PlaceholderPath();
2261 placeholderPath.processJobInfo(activeJob().get());
2263 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION_COMPLETE);
2268 if (activeCamera() && activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_REMOTE)
2269 state()->checkSeqBoundary();
2275 double nextExposure = -1;
2278 if (std::fabs(imageData->getMax(0) - imageData->getMin(0)) < 10)
2279 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 0.5;
2283 if (nextExposure <= 0 || std::isnan(nextExposure))
2286 i18n(
"Unable to calculate optimal exposure settings, please capture the flats manually."));
2292 nextExposure = qBound(exp_min, nextExposure, exp_max);
2294 emit newLog(
i18n(
"Current ADU is %1 Next exposure is %2 seconds.",
QString::number(currentADU,
'f', 0),
2295 QString(
"%L1").arg(nextExposure, 0,
'f', 6)));
2297 activeJob()->setCalibrationStage(SequenceJobState::CAL_CALIBRATION);
2298 activeJob()->setCoreProperty(SequenceJob::SJ_Exposure, nextExposure);
2299 if (activeCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
2301 activeCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
2312 if (activeJob() ==
nullptr)
2314 qWarning(KSTARS_EKOS_CAPTURE) <<
"setCurrentADU with null activeJob().";
2319 double nextExposure = 0;
2320 double targetADU = activeJob()->getCoreProperty(SequenceJob::SJ_TargetADU).toDouble();
2321 std::vector<double> coeff;
2325 if(activeJob()->getCoreProperty(SequenceJob::SJ_SkyFlat).toBool() && ExpRaw.size() > 2)
2327 int remove = ExpRaw.size() - 2;
2328 ExpRaw.remove(0, remove);
2329 ADURaw.remove(0, remove);
2333 ExpRaw.append(activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble());
2334 ADURaw.append(currentADU);
2336 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Capture: Current ADU = " << currentADU <<
" targetADU = " << targetADU
2337 <<
" Exposure Count: " << ExpRaw.count();
2341 if (ExpRaw.count() >= 2)
2343 if (ExpRaw.count() >= 5)
2347 coeff = gsl_polynomial_fit(ADURaw.data(), ExpRaw.data(), ExpRaw.count(), 2, chisq);
2348 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Running polynomial fitting. Found " << coeff.size() <<
" coefficients.";
2349 if (std::isnan(coeff[0]) || std::isinf(coeff[0]))
2351 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coefficients are invalid.";
2352 targetADUAlgorithm = ADU_LEAST_SQUARES;
2356 nextExposure = coeff[0] + (coeff[1] * targetADU) + (coeff[2] * pow(targetADU, 2));
2358 if (nextExposure < 0 || (nextExposure > ExpRaw.last() || targetADU < ADURaw.last())
2359 || (nextExposure < ExpRaw.last() || targetADU > ADURaw.last()))
2362 targetADUAlgorithm = ADU_LEAST_SQUARES;
2366 targetADUAlgorithm = ADU_POLYNOMIAL;
2367 for (
size_t i = 0; i < coeff.size(); i++)
2368 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Coeff #" << i <<
"=" << coeff[i];
2373 bool looping =
false;
2374 if (ExpRaw.count() >= 10)
2376 int size = ExpRaw.count();
2377 looping = (std::fabs(ExpRaw[size - 1] - ExpRaw[size - 2] < 0.01)) &&
2378 (std::fabs(ExpRaw[size - 2] - ExpRaw[size - 3] < 0.01));
2379 if (looping && targetADUAlgorithm == ADU_POLYNOMIAL)
2381 qWarning(KSTARS_EKOS_CAPTURE) <<
"Detected looping in polynomial results. Falling back to llsqr.";
2382 targetADUAlgorithm = ADU_LEAST_SQUARES;
2389 if (targetADUAlgorithm == ADU_LEAST_SQUARES)
2391 double a = 0, b = 0;
2392 llsq(ExpRaw, ADURaw, a, b);
2397 nextExposure = (targetADU - b) / a;
2399 if (nextExposure < 0)
2407 if (nextExposure == 0.0 || nextExposure > 180)
2409 if (currentADU < targetADU)
2410 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * 1.25;
2412 nextExposure = activeJob()->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() * .75;
2415 qCDebug(KSTARS_EKOS_CAPTURE) <<
"next flat exposure is" << nextExposure;
2417 return nextExposure;
2427QString Ekos::CameraProcess::createTabTitle(
const FITSMode &captureMode,
const QString &deviceName)
2429 const bool isPreview = (activeJob() ==
nullptr || (activeJob() && activeJob()->jobType() == SequenceJob::JOBTYPE_PREVIEW));
2430 if (isPreview && Options::singlePreviewFITS())
2434 if (Options::singleWindowCapturedFITS())
2435 return (
i18n(
"%1 Preview", deviceName));
2438 return(
i18n(
"Preview"));
2440 else if (captureMode == FITS_CALIBRATE)
2444 const QString filtername = activeJob()->getCoreProperty(SequenceJob::SJ_Filter).toString();
2445 if (filtername ==
"")
2448 return(
QString(
"%1 %2").arg(filtername).arg(
i18n(
"Flat Calibration")));
2451 return(
i18n(
"Calibration"));
2457 const FITSScale &captureFilter,
const QString &filename,
const QString &deviceName)
2463 switch (captureMode)
2466 case FITS_CALIBRATE:
2468 if (Options::useFITSViewer())
2471 bool success =
false;
2475 QString tabTitle = createTabTitle(captureMode, deviceName);
2478 int *tabID = &m_fitsvViewerTabIDs.normalTabID;
2479 if (*tabID == -1 || Options::singlePreviewFITS() ==
false)
2482 success = getFITSViewer()->loadData(data, fileURL, &tabIndex, captureMode, captureFilter, tabTitle);
2485 auto tabs = getFITSViewer()->tabs();
2486 if (tabIndex < tabs.size() && captureMode == FITS_NORMAL)
2488 emit newView(tabs[tabIndex]->getView());
2489 tabs[tabIndex]->disconnect(
this);
2490 connect(tabs[tabIndex].get(), &FITSTab::updated,
this, [
this]
2493 emit newView(tab->getView());
2499 success = getFITSViewer()->updateData(data, fileURL, *tabID, &tabIndex, captureMode, captureFilter, tabTitle);
2506 qCCritical(KSTARS_EKOS_CAPTURE()) <<
"error adding/updating FITS";
2510 if (Options::focusFITSOnNewImage())
2511 getFITSViewer()->raise();
2524 FITSMode captureMode = tChip ==
nullptr ? FITS_UNKNOWN : tChip->getCaptureMode();
2525 FITSScale captureFilter = tChip ==
nullptr ? FITS_NONE : tChip->getCaptureFilter();
2526 updateFITSViewer(data, captureMode, captureFilter, filename, data->property(
"device").toString());
2532 if (m_VideoWindow.isNull() && activeCamera() !=
nullptr)
2534 m_VideoWindow.reset(
new StreamWG(activeCamera()));
2539 connect(activeCamera(), &ISD::Camera::videoRecordToggled, m_VideoWindow.get(), &StreamWG::enableStream,
2545 return m_VideoWindow;
2548void CameraProcess::updateVideoWindow(
int width,
int height,
bool streamEnabled)
2552 if (width > 0 && height > 0)
2557void CameraProcess::closeVideoWindow()
2559 if (m_VideoWindow.
isNull())
2562 m_VideoWindow->close();
2565void CameraProcess::showVideoFrame(INDI::Property prop,
int width,
int height)
2576 const QString &targetName,
bool setOptions)
2578 state()->clearCapturedFramesMap();
2579 auto queue = state()->getSequenceQueue();
2580 if (!queue->load(fileURL, targetName, devices(), state()))
2582 QString message =
i18n(
"Unable to open file %1", fileURL);
2583 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2589 queue->setOptions();
2591 state()->updateHFRThreshold();
2594 for (
auto j : state()->allJobs())
2603 state()->getSequenceQueue()->loadOptions();
2604 return state()->getSequenceQueue()->save(path, state()->observerName());
2616 connect(activeCamera(), &ISD::Camera::videoRecordToggled,
this, &CameraProcess::updateVideoRecordStatus,
2630 disconnect(activeCamera(), &ISD::Camera::ready,
this, &CameraProcess::cameraReady);
2637 if (devices()->filterWheel() && devices()->filterWheel() == device)
2640 if (devices()->filterWheel())
2641 devices()->filterWheel()->disconnect(
this);
2643 devices()->setFilterWheel(device);
2645 return (device !=
nullptr);
2652 emit newLog(
i18n(
"Sequence paused."));
2657 state()->setContinueAction(continueAction);
2670 for (
auto &job : state()->allJobs())
2672 if (job->getStatus() == JOB_IDLE || job->getStatus() == JOB_ABORTED)
2684 for (
auto &job : state()->allJobs())
2686 if (job->getStatus() != JOB_DONE)
2689 if (state()->getCaptureDelayTimer().isActive())
2691 if (state()->getCaptureDelayTimer().interval() <= 0)
2692 state()->getCaptureDelayTimer().setInterval(1000);
2699 if (!state()->ignoreJobProgress())
2702 i18n(
"All jobs are complete. Do you want to reset the status of all jobs and restart capturing?"),
2710 first_job = state()->allJobs().first();
2714 else if (state()->ignoreJobProgress())
2716 emit newLog(
i18n(
"Warning: option \"Always Reset Sequence When Starting\" is enabled and resets the sequence counts."));
2723void CameraProcess::resetJobStatus(JOBStatus newStatus)
2725 if (activeJob() !=
nullptr)
2727 activeJob()->resetStatus(newStatus);
2728 emit updateJobTable(activeJob());
2732void CameraProcess::resetAllJobs()
2734 for (
auto &job : state()->allJobs())
2739 m_State->clearCapturedFramesMap();
2741 emit updateJobTable(
nullptr);
2744void CameraProcess::updatedCaptureCompleted(
int count)
2746 activeJob()->setCompleted(count);
2747 emit updateJobTable(activeJob());
2750void CameraProcess::updateVideoRecordStatus(
bool enabled)
2753 if (activeJob() ==
nullptr)
2756 qCInfo(KSTARS_EKOS_CAPTURE) <<
"Video recording" << (enabled ?
"started." :
"stopped.");
2758 if (enabled ==
false)
2760 updatedCaptureCompleted(activeJob()->getCoreProperty(SequenceJob::SJ_Count).toInt());
2765void CameraProcess::llsq(QVector<double> x, QVector<double> y,
double &a,
double &b)
2787 for (i = 0; i < n; i++)
2792 xbar = xbar /
static_cast<double>(n);
2793 ybar = ybar /
static_cast<double>(n);
2799 for (i = 0; i < n; i++)
2801 top = top + (x[i] - xbar) * (y[i] - ybar);
2802 bot = bot + (x[i] - xbar) * (x[i] - xbar);
2807 b = ybar - a * xbar;
2819 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2827 if (devices()->getActiveCamera() && devices()->getActiveCamera()->
hasCoolerControl())
2828 return devices()->getActiveCamera()->setCoolerControl(enable);
2837 KSMessageBox::Instance()->disconnect(
this);
2839 emit driverTimedout(name);
2843 KSMessageBox::Instance()->disconnect(
this);
2846 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to restart %1 camera driver?", name),
2847 i18n(
"Driver Restart"), 5);
2852 if (!activeCamera())
2855 ISD::CameraChip *tChip = devices()->getActiveCamera()->getChip(ISD::CameraChip::PRIMARY_CCD);
2858 if (devices()->getActiveCamera()->hasVideoStream())
2859 types.
append(CAPTURE_TYPE_VIDEO);
2866 if (devices()->getFilterManager().isNull())
2869 return devices()->getFilterManager()->getFilterLabels();
2874 if (devices()->getActiveCamera()->getProperty(
"CCD_GAIN"))
2879 ccdGain[
"GAIN"] = value;
2880 propertyMap[
"CCD_GAIN"] = ccdGain;
2884 propertyMap[
"CCD_GAIN"].remove(
"GAIN");
2885 if (propertyMap[
"CCD_GAIN"].size() == 0)
2886 propertyMap.remove(
"CCD_GAIN");
2889 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2894 ccdGain[
"Gain"] = value;
2895 propertyMap[
"CCD_CONTROLS"] = ccdGain;
2899 propertyMap[
"CCD_CONTROLS"].remove(
"Gain");
2900 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2901 propertyMap.remove(
"CCD_CONTROLS");
2908 if (devices()->getActiveCamera()->getProperty(
"CCD_OFFSET"))
2913 ccdOffset[
"OFFSET"] = value;
2914 propertyMap[
"CCD_OFFSET"] = ccdOffset;
2918 propertyMap[
"CCD_OFFSET"].remove(
"OFFSET");
2919 if (propertyMap[
"CCD_OFFSET"].size() == 0)
2920 propertyMap.remove(
"CCD_OFFSET");
2923 else if (devices()->getActiveCamera()->getProperty(
"CCD_CONTROLS"))
2928 ccdOffset[
"Offset"] = value;
2929 propertyMap[
"CCD_CONTROLS"] = ccdOffset;
2933 propertyMap[
"CCD_CONTROLS"].remove(
"Offset");
2934 if (propertyMap[
"CCD_CONTROLS"].size() == 0)
2935 propertyMap.remove(
"CCD_CONTROLS");
2943 if (!m_FITSViewerWindow.
isNull())
2944 return m_FITSViewerWindow;
2947 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2952 connect(m_FITSViewerWindow.
get(), &FITSViewer::closed,
this, [
this](
int tabIndex)
2954 if (tabIndex == m_fitsvViewerTabIDs.normalTabID)
2955 m_fitsvViewerTabIDs.normalTabID = -1;
2956 else if (tabIndex == m_fitsvViewerTabIDs.calibrationTabID)
2957 m_fitsvViewerTabIDs.calibrationTabID = -1;
2958 else if (tabIndex == m_fitsvViewerTabIDs.focusTabID)
2959 m_fitsvViewerTabIDs.focusTabID = -1;
2960 else if (tabIndex == m_fitsvViewerTabIDs.guideTabID)
2961 m_fitsvViewerTabIDs.guideTabID = -1;
2962 else if (tabIndex == m_fitsvViewerTabIDs.alignTabID)
2963 m_fitsvViewerTabIDs.alignTabID = -1;
2967 connect(m_FITSViewerWindow.
get(), &FITSViewer::terminated,
this, [
this]()
2969 m_fitsvViewerTabIDs = {-1, -1, -1, -1, -1};
2970 m_FITSViewerWindow.
clear();
2973 return m_FITSViewerWindow;
2978 return devices()->getActiveCamera();
2981void CameraProcess::checkCaptureOperationsTimeout(
const std::function<
void()> &slot)
2984 if (m_CaptureOperationsTimer.isValid() ==
false)
2985 m_CaptureOperationsTimer.start();
2988 if (state()->getCaptureState() == CAPTURE_PAUSED)
2989 m_CaptureOperationsTimer.restart();
2992 if (m_CaptureOperationsTimer.elapsed() >= Options::captureOperationsTimeout() * 1000)
2994 emit newLog(
i18n(
"Capture operations timed out after %1 seconds.", Options::captureOperationsTimeout()));
2995 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.