7 #include "indicamera.h"
8 #include "indicamerachip.h"
10 #include "config-kstars.h"
12 #include "indi_debug.h"
14 #include "clientmanager.h"
20 #include "fitsviewer/fitsdata.h"
21 #include "fitsviewer/fitstab.h"
24 #include <KNotifications/KNotification>
25 #include "auxiliary/ksmessagebox.h"
26 #include "ksnotification.h"
27 #include <QImageReader>
30 #include <QtConcurrent>
32 #include <basedevice.h>
34 const QStringList RAWFormats = {
"cr2",
"cr3",
"crw",
"nef",
"raf",
"dng",
"arw",
"orf" };
36 const QString &getFITSModeStringString(FITSMode mode)
38 return FITSModes[mode];
44 Camera::Camera(GenericDevice *parent) : ConcreteDevice(parent)
46 primaryChip.reset(
new CameraChip(
this, CameraChip::PRIMARY_CCD));
48 m_Media.reset(
new WSMedia(
this));
49 connect(m_Media.get(), &WSMedia::newFile,
this, &Camera::setWSBLOB);
57 if (m_ImageViewerWindow)
58 m_ImageViewerWindow->close();
61 if (fileWriteBuffer !=
nullptr)
62 delete [] fileWriteBuffer;
65 void Camera::setBLOBManager(
const char *device, INDI::Property prop)
67 if (!prop.getRegistered())
70 if (getDeviceName() == device)
71 emit newBLOBManager(prop);
74 void Camera::registerProperty(INDI::Property prop)
76 if (prop.isNameMatch(
"GUIDER_EXPOSURE"))
79 guideChip.reset(
new CameraChip(
this, CameraChip::GUIDE_CCD));
81 else if (prop.isNameMatch(
"CCD_FRAME_TYPE"))
83 primaryChip->clearFrameTypes();
85 for (
auto &it : *prop.getSwitch())
86 primaryChip->addFrameLabel(it.getLabel());
88 else if (prop.isNameMatch(
"CCD_FRAME"))
90 auto np = prop.getNumber();
91 if (np && np->getPermission() != IP_RO)
92 primaryChip->setCanSubframe(
true);
94 else if (prop.isNameMatch(
"GUIDER_FRAME"))
96 auto np = prop.getNumber();
97 if (np && np->getPermission() != IP_RO)
98 guideChip->setCanSubframe(
true);
100 else if (prop.isNameMatch(
"CCD_BINNING"))
102 auto np = prop.getNumber();
103 if (np && np->getPermission() != IP_RO)
104 primaryChip->setCanBin(
true);
106 else if (prop.isNameMatch(
"GUIDER_BINNING"))
108 auto np = prop.getNumber();
109 if (np && np->getPermission() != IP_RO)
110 guideChip->setCanBin(
true);
112 else if (prop.isNameMatch(
"CCD_ABORT_EXPOSURE"))
114 auto sp = prop.getSwitch();
115 if (sp && sp->getPermission() != IP_RO)
116 primaryChip->setCanAbort(
true);
118 else if (prop.isNameMatch(
"GUIDER_ABORT_EXPOSURE"))
120 auto sp = prop.getSwitch();
121 if (sp && sp->getPermission() != IP_RO)
122 guideChip->setCanAbort(
true);
124 else if (prop.isNameMatch(
"CCD_TEMPERATURE"))
126 auto np = prop.getNumber();
128 CanCool = (np->getPermission() != IP_RO);
130 emit newTemperatureValue(np->at(0)->getValue());
132 else if (prop.isNameMatch(
"CCD_COOLER"))
135 HasCoolerControl =
true;
137 else if (prop.isNameMatch(
"CCD_VIDEO_STREAM"))
140 HasVideoStream =
true;
142 else if (prop.isNameMatch(
"CCD_CAPTURE_FORMAT"))
144 auto sp = prop.getSwitch();
147 m_CaptureFormats.
clear();
148 for (
const auto &oneSwitch : *sp)
149 m_CaptureFormats << oneSwitch.getLabel();
151 m_CaptureFormatIndex = sp->findOnSwitchIndex();
154 else if (prop.isNameMatch(
"CCD_TRANSFER_FORMAT"))
156 auto sp = prop.getSwitch();
159 m_EncodingFormats.
clear();
160 for (
const auto &oneSwitch : *sp)
161 m_EncodingFormats << oneSwitch.getLabel();
163 auto format = sp->findOnSwitch();
165 m_EncodingFormat = format->label;
168 else if (prop.isNameMatch(
"CCD_EXPOSURE_PRESETS"))
170 auto svp = prop.getSwitch();
175 for (
const auto &it : *svp)
182 if (parts.
count() == 2)
184 bool numOk =
false, denOk =
false;
185 double numerator = parts[0].toDouble(&numOk);
186 double denominator = parts[1].toDouble(&denOk);
187 if (numOk && denOk && denominator > 0)
190 value = numerator / denominator;
195 m_ExposurePresets.
insert(key, value);
197 double min = 1e6, max = 1e-6;
198 for (
auto oneValue : m_ExposurePresets.
values())
205 m_ExposurePresetsMinMax = qMakePair<double, double>(min, max);
209 else if (prop.isNameMatch(
"CCD_FAST_TOGGLE"))
211 auto sp = prop.getSwitch();
213 m_FastExposureEnabled = sp->findOnSwitchIndex() == 0;
215 m_FastExposureEnabled =
false;
217 else if (prop.isNameMatch(
"TELESCOPE_TYPE"))
219 auto sp = prop.getSwitch();
222 auto format = sp->findWidgetByName(
"TELESCOPE_PRIMARY");
223 if (format && format->getState() == ISS_ON)
224 telescopeType = TELESCOPE_PRIMARY;
226 telescopeType = TELESCOPE_GUIDE;
229 else if (prop.isNameMatch(
"CCD_WEBSOCKET_SETTINGS"))
231 auto np = prop.getNumber();
232 m_Media->setURL(
QUrl(
QString(
"ws://%1:%2").arg(m_Parent->getClientManager()->getHost()).
arg(np->at(0)->getValue())));
233 m_Media->connectServer();
235 else if (prop.isNameMatch(
"CCD1"))
237 primaryCCDBLOB = prop;
240 else if ( (gainN ==
nullptr || offsetN ==
nullptr) && prop.getType() == INDI_NUMBER)
244 auto controlNP = prop.getNumber();
247 for (
auto &it : *controlNP)
252 if (name ==
"gain" || label ==
"gain")
255 gainPerm = controlNP->getPermission();
257 else if (name ==
"offset" || label ==
"offset")
260 offsetPerm = controlNP->getPermission();
266 ConcreteDevice::registerProperty(prop);
269 void Camera::removeProperty(INDI::Property prop)
271 if (prop.isNameMatch(
"CCD_WEBSOCKET_SETTINGS"))
273 m_Media->disconnectServer();
277 void Camera::processNumber(INDI::Property prop)
279 auto nvp = prop.getNumber();
280 if (nvp->isNameMatch(
"CCD_EXPOSURE"))
282 auto np = nvp->findWidgetByName(
"CCD_EXPOSURE_VALUE");
284 emit newExposureValue(primaryChip.get(), np->getValue(), nvp->getState());
285 if (nvp->getState() == IPS_ALERT)
286 emit error(ERROR_CAPTURE);
288 else if (prop.isNameMatch(
"CCD_TEMPERATURE"))
291 auto np = nvp->findWidgetByName(
"CCD_TEMPERATURE_VALUE");
293 emit newTemperatureValue(np->getValue());
295 else if (prop.isNameMatch(
"GUIDER_EXPOSURE"))
297 auto np = nvp->findWidgetByName(
"GUIDER_EXPOSURE_VALUE");
299 emit newExposureValue(guideChip.get(), np->getValue(), nvp->getState());
301 else if (prop.isNameMatch(
"FPS"))
303 emit newFPS(nvp->at(0)->getValue(), nvp->at(1)->getValue());
305 else if (prop.isNameMatch(
"CCD_RAPID_GUIDE_DATA"))
307 if (nvp->getState() == IPS_ALERT)
309 emit newGuideStarData(primaryChip.get(), -1, -1, -1);
313 double dx = -1, dy = -1, fit = -1;
315 auto np = nvp->findWidgetByName(
"GUIDESTAR_X");
318 np = nvp->findWidgetByName(
"GUIDESTAR_Y");
321 np = nvp->findWidgetByName(
"GUIDESTAR_FIT");
323 fit = np->getValue();
325 if (dx >= 0 && dy >= 0 && fit >= 0)
326 emit newGuideStarData(primaryChip.get(), dx, dy, fit);
329 else if (prop.isNameMatch(
"GUIDER_RAPID_GUIDE_DATA"))
331 if (nvp->getState() == IPS_ALERT)
333 emit newGuideStarData(guideChip.get(), -1, -1, -1);
337 double dx = -1, dy = -1, fit = -1;
338 auto np = nvp->findWidgetByName(
"GUIDESTAR_X");
341 np = nvp->findWidgetByName(
"GUIDESTAR_Y");
344 np = nvp->findWidgetByName(
"GUIDESTAR_FIT");
346 fit = np->getValue();
348 if (dx >= 0 && dy >= 0 && fit >= 0)
349 emit newGuideStarData(guideChip.get(), dx, dy, fit);
354 void Camera::processSwitch(INDI::Property prop)
356 auto svp = prop.getSwitch();
358 if (svp->isNameMatch(
"CCD_COOLER"))
361 HasCoolerControl =
true;
362 emit coolerToggled(svp->sp[0].s == ISS_ON);
367 if (isBLOBEnabled() ==
false || m_StreamingEnabled ==
false)
370 HasVideoStream =
true;
372 if (!streamWindow && svp->sp[0].s == ISS_ON)
374 streamWindow.reset(
new StreamWG(
this));
376 INumberVectorProperty *streamFrame = getNumber(
"CCD_STREAM_FRAME");
377 INumber *w =
nullptr, *h =
nullptr;
381 w = IUFindNumber(streamFrame,
"WIDTH");
382 h = IUFindNumber(streamFrame,
"HEIGHT");
396 int x = 0, y = 0, w = 0, h = 0;
397 int binx = 0, biny = 0;
399 primaryChip->getFrame(&x, &y, &w, &h);
400 primaryChip->getBinning(&binx, &biny);
406 streamWindow->setSize(streamW, streamH);
414 streamWindow->enableStream(svp->sp[0].s == ISS_ON);
415 emit videoStreamToggled(svp->sp[0].s == ISS_ON);
418 else if (svp->isNameMatch(
"CCD_CAPTURE_FORMAT"))
420 m_CaptureFormats.
clear();
421 for (
int i = 0; i < svp->nsp; i++)
423 m_CaptureFormats << svp->sp[i].label;
424 if (svp->sp[i].s == ISS_ON)
425 m_CaptureFormatIndex = i;
428 else if (svp->isNameMatch(
"CCD_TRANSFER_FORMAT"))
430 ISwitch *format = IUFindOnSwitch(svp);
432 m_EncodingFormat = format->label;
434 else if (svp->isNameMatch(
"RECORD_STREAM"))
436 ISwitch *recordOFF = IUFindSwitch(svp,
"RECORD_OFF");
438 if (recordOFF && recordOFF->s == ISS_ON)
440 emit videoRecordToggled(
false);
441 KSNotification::event(
QLatin1String(
"IndiServerMessage"),
i18n(
"Video Recording Stopped"), KSNotification::INDI);
445 emit videoRecordToggled(
true);
446 KSNotification::event(
QLatin1String(
"IndiServerMessage"),
i18n(
"Video Recording Started"), KSNotification::INDI);
449 else if (svp->isNameMatch(
"TELESCOPE_TYPE"))
451 ISwitch *format = IUFindSwitch(svp,
"TELESCOPE_PRIMARY");
452 if (format && format->s == ISS_ON)
453 telescopeType = TELESCOPE_PRIMARY;
455 telescopeType = TELESCOPE_GUIDE;
457 else if (!strcmp(svp->name,
"CCD_FAST_TOGGLE"))
459 m_FastExposureEnabled = IUFindOnSwitchIndex(svp) == 0;
461 else if (svp->isNameMatch(
"CONNECTION"))
463 auto dSwitch = svp->findWidgetByName(
"DISCONNECT");
465 if (dSwitch && dSwitch->getState() == ISS_ON)
469 streamWindow->enableStream(
false);
470 emit videoStreamToggled(
false);
471 streamWindow->close();
472 streamWindow.reset();
478 primaryCCDBLOB = INDI::Property();
483 void Camera::processText(INDI::Property prop)
485 auto tvp = prop.getText();
486 if (tvp->isNameMatch(
"CCD_FILE_PATH"))
488 auto filepath = tvp->findWidgetByName(
"FILE_PATH");
490 emit newRemoteFile(
QString(filepath->getText()));
499 auto bvp = primaryCCDBLOB.getBLOB();
500 auto bp = bvp->at(0);
505 processBLOB(primaryCCDBLOB);
508 bp->setBlob(
nullptr);
511 void Camera::processStream(INDI::Property prop)
513 if (!streamWindow || streamWindow->isStreamEnabled() ==
false)
516 INumberVectorProperty *streamFrame = getNumber(
"CCD_STREAM_FRAME");
517 INumber *w =
nullptr, *h =
nullptr;
521 w = IUFindNumber(streamFrame,
"WIDTH");
522 h = IUFindNumber(streamFrame,
"HEIGHT");
532 int x = 0, y = 0, w = 0, h = 0;
533 int binx = 1, biny = 1;
535 primaryChip->getFrame(&x, &y, &w, &h);
536 primaryChip->getBinning(&binx, &biny);
541 streamWindow->setSize(streamW, streamH);
543 streamWindow->show();
544 streamWindow->newFrame(prop);
547 bool Camera::generateFilename(
bool batch_mode,
const QString &extension,
QString *filename)
550 *filename = placeholderPath.generateOutputFilename(
true, batch_mode, nextSequenceID, extension,
"");
553 if (currentDir.
exists() ==
false)
559 QString oldFilename = *filename;
560 *filename = placeholderPath.repairFilename(*filename);
561 if (filename != oldFilename)
562 qCWarning(KSTARS_INDI) <<
"File over-write detected: changing" << oldFilename <<
"to" << *filename;
564 qCWarning(KSTARS_INDI) <<
"File over-write detected for" << oldFilename <<
"but could not correct filename";
567 QFile test_file(*filename);
575 bool Camera::writeImageFile(
const QString &filename, INDI::Property prop,
bool is_fits)
589 fileWriteFilename = filename;
594 auto bp = prop.getBLOB()->
at(0);
596 if (fileWriteBufferSize != bp->getBlobLen())
598 if (fileWriteBuffer !=
nullptr)
599 delete [] fileWriteBuffer;
600 fileWriteBufferSize = bp->getBlobLen();
601 fileWriteBuffer =
new char[fileWriteBufferSize];
606 memcpy(fileWriteBuffer, bp->getBlob(), bp->getBlobLen());
607 fileWriteThread =
QtConcurrent::run(
this, &ISD::Camera::WriteImageFileInternal, fileWriteFilename, fileWriteBuffer,
612 auto bp = prop.getBLOB()->at(0);
613 if (!WriteImageFileInternal(filename,
static_cast<char*
>(bp->getBlob()), bp->getBlobLen()))
625 if (!m_FITSViewerWindow.
isNull() && ! m_FITSViewerWindow.
isNull())
626 return m_FITSViewerWindow;
629 normalTabID = calibrationTabID = focusTabID = guideTabID = alignTabID = -1;
634 connect(m_FITSViewerWindow.
get(), &FITSViewer::closed,
this, [
this](
int tabIndex)
636 if (tabIndex == normalTabID)
638 else if (tabIndex == calibrationTabID)
639 calibrationTabID = -1;
640 else if (tabIndex == focusTabID)
642 else if (tabIndex == guideTabID)
644 else if (tabIndex == alignTabID)
649 connect(m_FITSViewerWindow.
get(), &FITSViewer::terminated,
this, [
this]()
652 calibrationTabID = -1;
656 m_FITSViewerWindow.clear();
659 return m_FITSViewerWindow;
662 bool Camera::processBLOB(INDI::Property prop)
664 auto bvp = prop.getBLOB();
666 if (bvp->getPermission() == IP_WO || bvp->at(0)->getSize() == 0)
671 auto bp = bvp->at(0);
676 if (format.contains(
"stream"))
678 if (m_StreamingEnabled ==
false)
680 else if (streamWindow)
691 else if (format.contains(
"fits"))
693 else if (format.contains(
"xisf"))
695 else if (RAWFormats.
contains(shortFormat))
698 if (BType == BLOB_OTHER)
700 emit newImage(
nullptr);
704 CameraChip *targetChip =
nullptr;
706 if (bvp->isNameMatch(
"CCD2"))
707 targetChip = guideChip.get();
710 targetChip = primaryChip.get();
711 qCDebug(KSTARS_INDI) <<
"Image received. Mode:" << getFITSModeStringString(targetChip->getCaptureMode()) <<
"Size:" <<
721 if (targetChip->isBatchMode() ==
false || targetChip->getCaptureMode() != FITS_NORMAL)
723 if (!writeTempImageFile(format,
static_cast<char *
>(bp->blob), bp->size, &filename))
725 emit BLOBUpdated(
nullptr);
728 if (BType == BLOB_FITS)
729 addFITSKeywords(filename, filter);
734 if (targetChip->isBatchMode() && targetChip->getCaptureMode() != FITS_CALIBRATE)
738 if (!generateFilename(targetChip->isBatchMode(), format, &filename) ||
739 !writeImageFile(filename, prop, BType == BLOB_FITS))
746 KSMessageBox::Instance()->error(
i18n(
"Failed writing image to %1\nPlease check folder, filename & permissions.",
748 i18n(
"Image Write Failed"), 30);
750 emit propertyUpdated(prop);
757 if (targetChip->getCaptureMode() == FITS_NORMAL && targetChip->isBatchMode() ==
true)
760 qCInfo(KSTARS_INDI) << shortFormat.
toUpper() <<
"file saved to" << filename;
772 if (BType == BLOB_IMAGE || BType == BLOB_RAW)
774 bool useFITSViewer = Options::autoImageToFITS() &&
775 (Options::useFITSViewer() || (Options::useDSLRImageViewer() ==
false && targetChip->isBatchMode() ==
false));
776 bool useDSLRViewer = (Options::useDSLRImageViewer() || targetChip->isBatchMode() ==
false);
778 if (BType == BLOB_RAW && (useFITSViewer || useDSLRViewer))
780 QString rawFileName = filename;
786 imgPreview.setAutoRemove(
false);
789 QString preview_filename = imgPreview.fileName();
792 if (KSUtils::RAWToJPEG(filename, preview_filename, errorMessage) ==
false)
795 emit BLOBUpdated(bp);
800 if (targetChip->isBatchMode() ==
false)
803 filename = preview_filename;
810 if (useFITSViewer && (FITSData::ImageToFITS(filename, shortFormat, output)))
812 if (BType == BLOB_RAW || targetChip->isBatchMode() ==
false)
818 emit previewFITSGenerated(output);
820 FITSData *blob_fits_data =
new FITSData(targetChip->getCaptureMode());
822 QFuture<bool> fitsloader = blob_fits_data->loadFromFile(filename,
false);
828 delete (blob_fits_data);
829 qCCritical(KSTARS_INDI) <<
"failed reading FITS memory buffer";
830 emit newExposureValue(targetChip, 0, IPS_ALERT);
833 displayFits(targetChip, filename, bp, blob_fits_data);
836 else if (useDSLRViewer)
838 if (m_ImageViewerWindow.
isNull())
841 m_ImageViewerWindow->loadImage(filename);
843 emit previewJPEGGenerated(filename, m_ImageViewerWindow->metadata());
858 if (targetChip->getCaptureMode() == FITS_NORMAL &&
859 Options::useFITSViewer() ==
false &&
860 Options::useSummaryPreview() ==
false &&
861 targetChip->isBatchMode())
863 emit propertyUpdated(prop);
864 emit newImage(
nullptr);
871 if (!imageData->loadFromBuffer(buffer, shortFormat, filename))
877 handleImage(targetChip, filename, prop, imageData);
881 void Camera::handleImage(CameraChip *targetChip,
const QString &filename, INDI::Property prop,
884 FITSMode captureMode = targetChip->getCaptureMode();
885 auto bp = prop.getBLOB()->at(0);
888 data->setProperty(
"device", getDeviceName());
889 data->setProperty(
"blobVector", prop.getName());
890 data->setProperty(
"blobElement", bp->getName());
891 data->setProperty(
"chip", targetChip->getType());
893 targetChip->setImageData(data);
900 if (Options::useFITSViewer())
904 emit propertyUpdated(prop);
907 bool success =
false;
909 int *tabID = &normalTabID;
911 FITSScale captureFilter = targetChip->getCaptureFilter();
912 if (*tabID == -1 || Options::singlePreviewFITS() ==
false)
918 if (Options::singlePreviewFITS())
922 if (Options::singleWindowCapturedFITS())
923 previewTitle =
i18n(
"%1 Preview", getDeviceName());
926 previewTitle =
i18n(
"Preview");
929 success = getFITSViewer()->loadData(data, fileURL, &tabIndex, captureMode, captureFilter, previewTitle);
932 auto tabs = getFITSViewer()->tabs();
933 if (tabIndex < tabs.size() && captureMode == FITS_NORMAL)
935 emit newView(tabs[tabIndex]->getView());
936 tabs[tabIndex]->disconnect(
this);
937 connect(tabs[tabIndex].
get(), &FITSTab::updated,
this, [
this]
939 auto tab = qobject_cast<FITSTab *>(
sender());
940 emit newView(tab->getView());
945 success = getFITSViewer()->updateData(data, fileURL, *tabID, &tabIndex, captureFilter, captureMode);
951 qCCritical(KSTARS_INDI) <<
"error adding/updating FITS";
956 if (Options::focusFITSOnNewImage())
957 getFITSViewer()->raise();
967 emit propertyUpdated(prop);
971 void Camera::StreamWindowHidden()
976 auto streamSP =
getSwitch(
"CCD_VIDEO_STREAM");
980 streamSP->at(0)->setState(ISS_OFF);
981 streamSP->at(1)->setState(ISS_ON);
982 streamSP->setState(IPS_IDLE);
990 streamSP->at(0)->setState(ISS_OFF);
991 streamSP->at(1)->setState(ISS_ON);
992 streamSP->setState(IPS_IDLE);
996 streamSP =
getSwitch(
"AUX_VIDEO_STREAM");
1000 streamSP->at(0)->setState(ISS_OFF);
1001 streamSP->at(1)->setState(ISS_ON);
1002 streamSP->setState(IPS_IDLE);
1008 streamWindow->disconnect();
1011 bool Camera::hasGuideHead()
1013 return HasGuideHead;
1016 bool Camera::hasCooler()
1021 bool Camera::hasCoolerControl()
1023 return HasCoolerControl;
1026 bool Camera::setCoolerControl(
bool enable)
1028 if (HasCoolerControl ==
false)
1031 auto coolerSP =
getSwitch(
"CCD_COOLER");
1037 auto coolerON = coolerSP->findWidgetByName(
"COOLER_ON");
1038 auto coolerOFF = coolerSP->findWidgetByName(
"COOLER_OFF");
1039 if (!coolerON || !coolerOFF)
1042 coolerON->setState(enable ? ISS_ON : ISS_OFF);
1043 coolerOFF->setState(enable ? ISS_OFF : ISS_ON);
1049 CameraChip *Camera::getChip(CameraChip::ChipType cType)
1053 case CameraChip::PRIMARY_CCD:
1054 return primaryChip.get();
1056 case CameraChip::GUIDE_CCD:
1057 return guideChip.get();
1063 bool Camera::setRapidGuide(CameraChip *targetChip,
bool enable)
1065 ISwitchVectorProperty *rapidSP =
nullptr;
1066 ISwitch *enableS =
nullptr;
1068 if (targetChip == primaryChip.get())
1071 rapidSP =
getSwitch(
"GUIDER_RAPID_GUIDE");
1073 if (rapidSP ==
nullptr)
1076 enableS = IUFindSwitch(rapidSP,
"ENABLE");
1078 if (enableS ==
nullptr)
1082 if ((enable && enableS->s == ISS_ON) || (!enable && enableS->s == ISS_OFF))
1085 IUResetSwitch(rapidSP);
1086 rapidSP->sp[0].s = enable ? ISS_ON : ISS_OFF;
1087 rapidSP->sp[1].s = enable ? ISS_OFF : ISS_ON;
1094 bool Camera::configureRapidGuide(CameraChip *targetChip,
bool autoLoop,
bool sendImage,
bool showMarker)
1096 ISwitchVectorProperty *rapidSP =
nullptr;
1097 ISwitch *autoLoopS =
nullptr, *sendImageS =
nullptr, *showMarkerS =
nullptr;
1099 if (targetChip == primaryChip.get())
1100 rapidSP =
getSwitch(
"CCD_RAPID_GUIDE_SETUP");
1102 rapidSP =
getSwitch(
"GUIDER_RAPID_GUIDE_SETUP");
1104 if (rapidSP ==
nullptr)
1107 autoLoopS = IUFindSwitch(rapidSP,
"AUTO_LOOP");
1108 sendImageS = IUFindSwitch(rapidSP,
"SEND_IMAGE");
1109 showMarkerS = IUFindSwitch(rapidSP,
"SHOW_MARKER");
1111 if (!autoLoopS || !sendImageS || !showMarkerS)
1115 if (((autoLoop && autoLoopS->s == ISS_ON) || (!autoLoop && autoLoopS->s == ISS_OFF)) &&
1116 ((sendImage && sendImageS->s == ISS_ON) || (!sendImage && sendImageS->s == ISS_OFF)) &&
1117 ((showMarker && showMarkerS->s == ISS_ON) || (!showMarker && showMarkerS->s == ISS_OFF)))
1120 autoLoopS->s = autoLoop ? ISS_ON : ISS_OFF;
1121 sendImageS->s = sendImage ? ISS_ON : ISS_OFF;
1122 showMarkerS->s = showMarker ? ISS_ON : ISS_OFF;
1129 void Camera::updateUploadSettings(
const QString &uploadDirectory,
const QString &uploadFile)
1131 ITextVectorProperty *uploadSettingsTP =
nullptr;
1132 IText *uploadT =
nullptr;
1134 uploadSettingsTP =
getText(
"UPLOAD_SETTINGS");
1135 if (uploadSettingsTP)
1137 uploadT = IUFindText(uploadSettingsTP,
"UPLOAD_DIR");
1138 if (uploadT && uploadDirectory.
isEmpty() ==
false)
1140 auto posixDirectory = uploadDirectory;
1143 IUSaveText(uploadT, posixDirectory.toLatin1().constData());
1146 uploadT = IUFindText(uploadSettingsTP,
"UPLOAD_PREFIX");
1154 Camera::UploadMode Camera::getUploadMode()
1156 ISwitchVectorProperty *uploadModeSP =
nullptr;
1158 uploadModeSP =
getSwitch(
"UPLOAD_MODE");
1160 if (uploadModeSP ==
nullptr)
1162 qWarning() <<
"No UPLOAD_MODE in CCD driver. Please update driver to INDI compliant CCD driver.";
1163 return UPLOAD_CLIENT;
1168 ISwitch *modeS =
nullptr;
1170 modeS = IUFindSwitch(uploadModeSP,
"UPLOAD_CLIENT");
1171 if (modeS && modeS->s == ISS_ON)
1172 return UPLOAD_CLIENT;
1173 modeS = IUFindSwitch(uploadModeSP,
"UPLOAD_LOCAL");
1174 if (modeS && modeS->s == ISS_ON)
1175 return UPLOAD_LOCAL;
1176 modeS = IUFindSwitch(uploadModeSP,
"UPLOAD_BOTH");
1177 if (modeS && modeS->s == ISS_ON)
1182 return UPLOAD_CLIENT;
1185 bool Camera::setUploadMode(UploadMode mode)
1187 ISwitch *modeS =
nullptr;
1189 auto uploadModeSP =
getSwitch(
"UPLOAD_MODE");
1193 qWarning() <<
"No UPLOAD_MODE in CCD driver. Please update driver to INDI compliant CCD driver.";
1200 modeS = uploadModeSP->findWidgetByName(
"UPLOAD_CLIENT");
1203 if (modeS->s == ISS_ON)
1208 modeS = uploadModeSP->findWidgetByName(
"UPLOAD_BOTH");
1211 if (modeS->s == ISS_ON)
1216 modeS = uploadModeSP->findWidgetByName(
"UPLOAD_LOCAL");
1219 if (modeS->s == ISS_ON)
1224 uploadModeSP->reset();
1232 bool Camera::getTemperature(
double *value)
1234 if (HasCooler ==
false)
1237 auto temperatureNP = getNumber(
"CCD_TEMPERATURE");
1242 *value = temperatureNP->at(0)->getValue();
1247 bool Camera::setTemperature(
double value)
1249 auto nvp = getNumber(
"CCD_TEMPERATURE");
1254 auto np = nvp->findWidgetByName(
"CCD_TEMPERATURE_VALUE");
1259 np->setValue(value);
1266 bool Camera::setEncodingFormat(
const QString &value)
1268 if (value.
isEmpty() || value == m_EncodingFormat)
1271 auto svp =
getSwitch(
"CCD_TRANSFER_FORMAT");
1277 for (
int i = 0; i < svp->nsp; i++)
1279 if (svp->at(i)->getLabel() == value)
1281 svp->at(i)->setState(ISS_ON);
1286 m_EncodingFormat = value;
1291 bool Camera::setTelescopeType(TelescopeType type)
1293 if (type == telescopeType)
1301 auto typePrimary = svp->findWidgetByName(
"TELESCOPE_PRIMARY");
1302 auto typeGuide = svp->findWidgetByName(
"TELESCOPE_GUIDE");
1304 if (!typePrimary || !typeGuide)
1307 telescopeType =
type;
1309 if ( (telescopeType == TELESCOPE_PRIMARY && typePrimary->getState() == ISS_OFF) ||
1310 (telescopeType == TELESCOPE_GUIDE && typeGuide->getState() == ISS_OFF))
1312 typePrimary->setState(telescopeType == TELESCOPE_PRIMARY ? ISS_ON : ISS_OFF);
1313 typeGuide->setState(telescopeType == TELESCOPE_PRIMARY ? ISS_OFF : ISS_ON);
1315 setConfig(SAVE_CONFIG);
1321 bool Camera::setVideoStreamEnabled(
bool enable)
1323 if (HasVideoStream ==
false)
1326 auto svp =
getSwitch(
"CCD_VIDEO_STREAM");
1332 if ((enable && svp->at(0)->getState() == ISS_ON) || (!enable && svp->at(1)->getState() == ISS_ON))
1335 svp->at(0)->setState(enable ? ISS_ON : ISS_OFF);
1336 svp->at(1)->setState(enable ? ISS_OFF : ISS_ON);
1343 bool Camera::resetStreamingFrame()
1345 auto frameProp = getNumber(
"CCD_STREAM_FRAME");
1350 auto xarg = frameProp->findWidgetByName(
"X");
1351 auto yarg = frameProp->findWidgetByName(
"Y");
1352 auto warg = frameProp->findWidgetByName(
"WIDTH");
1353 auto harg = frameProp->findWidgetByName(
"HEIGHT");
1355 if (xarg && yarg && warg && harg)
1357 if (!std::fabs(xarg->getValue() - xarg->getMin()) &&
1358 !std::fabs(yarg->getValue() - yarg->getMin()) &&
1359 !std::fabs(warg->getValue() - warg->getMax()) &&
1360 !std::fabs(harg->getValue() - harg->getMax()))
1363 xarg->setValue(xarg->getMin());
1364 yarg->setValue(yarg->getMin());
1365 warg->setValue(warg->getMax());
1366 harg->setValue(harg->getMax());
1375 bool Camera::setStreamLimits(uint16_t maxBufferSize, uint16_t maxPreviewFPS)
1377 auto limitsProp = getNumber(
"LIMITS");
1382 auto bufferMax = limitsProp->findWidgetByName(
"LIMITS_BUFFER_MAX");
1383 auto previewFPS = limitsProp->findWidgetByName(
"LIMITS_PREVIEW_FPS");
1385 if (bufferMax && previewFPS)
1387 if(std::fabs(bufferMax->getValue() - maxBufferSize) > 0 || std::fabs(previewFPS->getValue() - maxPreviewFPS) > 0)
1389 bufferMax->setValue(maxBufferSize);
1390 previewFPS->setValue(maxPreviewFPS);
1400 bool Camera::setStreamingFrame(
int x,
int y,
int w,
int h)
1402 auto frameProp = getNumber(
"CCD_STREAM_FRAME");
1407 auto xarg = frameProp->findWidgetByName(
"X");
1408 auto yarg = frameProp->findWidgetByName(
"Y");
1409 auto warg = frameProp->findWidgetByName(
"WIDTH");
1410 auto harg = frameProp->findWidgetByName(
"HEIGHT");
1412 if (xarg && yarg && warg && harg)
1414 if (!std::fabs(xarg->getValue() - x) &&
1415 !std::fabs(yarg->getValue() - y) &&
1416 !std::fabs(warg->getValue() - w) &&
1417 !std::fabs(harg->getValue() - h))
1421 xarg->value = qBound(xarg->getMin(),
static_cast<double>(x) + xarg->getValue(), xarg->getMax());
1422 yarg->value = qBound(yarg->getMin(),
static_cast<double>(y) + yarg->getValue(), yarg->getMax());
1423 warg->value = qBound(warg->getMin(),
static_cast<double>(w), warg->getMax());
1424 harg->value = qBound(harg->getMin(),
static_cast<double>(h), harg->getMax());
1433 bool Camera::isStreamingEnabled()
1435 if (HasVideoStream ==
false || !streamWindow)
1438 return streamWindow->isStreamEnabled();
1441 bool Camera::setSERNameDirectory(
const QString &filename,
const QString &directory)
1443 auto tvp =
getText(
"RECORD_FILE");
1448 auto filenameT = tvp->findWidgetByName(
"RECORD_FILE_NAME");
1449 auto dirT = tvp->findWidgetByName(
"RECORD_FILE_DIR");
1451 if (!filenameT || !dirT)
1462 bool Camera::getSERNameDirectory(
QString &filename,
QString &directory)
1464 auto tvp =
getText(
"RECORD_FILE");
1469 auto filenameT = tvp->findWidgetByName(
"RECORD_FILE_NAME");
1470 auto dirT = tvp->findWidgetByName(
"RECORD_FILE_DIR");
1472 if (!filenameT || !dirT)
1475 filename =
QString(filenameT->getText());
1476 directory =
QString(dirT->getText());
1481 bool Camera::startRecording()
1488 auto recordON = svp->findWidgetByName(
"RECORD_ON");
1493 if (recordON->getState() == ISS_ON)
1497 recordON->setState(ISS_ON);
1504 bool Camera::startDurationRecording(
double duration)
1506 auto nvp = getNumber(
"RECORD_OPTIONS");
1511 auto durationN = nvp->findWidgetByName(
"RECORD_DURATION");
1521 auto recordON = svp->findWidgetByName(
"RECORD_DURATION_ON");
1526 if (recordON->getState() == ISS_ON)
1529 durationN->setValue(duration);
1533 recordON->setState(ISS_ON);
1540 bool Camera::startFramesRecording(uint32_t frames)
1542 auto nvp = getNumber(
"RECORD_OPTIONS");
1547 auto frameN = nvp->findWidgetByName(
"RECORD_FRAME_TOTAL");
1550 if (!frameN || !svp)
1553 auto recordON = svp->findWidgetByName(
"RECORD_FRAME_ON");
1558 if (recordON->getState() == ISS_ON)
1561 frameN->setValue(frames);
1565 recordON->setState(ISS_ON);
1572 bool Camera::stopRecording()
1579 auto recordOFF = svp->findWidgetByName(
"RECORD_OFF");
1585 if (recordOFF->getState() == ISS_ON)
1589 recordOFF->setState(ISS_ON);
1598 auto tvp =
getText(
"FITS_HEADER");
1601 if (!tvp || tvp->count() < 3)
1604 for (
auto &record : values)
1606 tvp->at(0)->setText(record.key.toLatin1().constData());
1607 tvp->at(1)->setText(record.value.toString().toLatin1().constData());
1608 tvp->at(2)->setText(record.comment.toLatin1().constData());
1616 bool Camera::setGain(
double value)
1621 gainN->value = value;
1626 bool Camera::getGain(
double *value)
1631 *value = gainN->value;
1636 bool Camera::getGainMinMaxStep(
double *min,
double *max,
double *step)
1643 *step = gainN->step;
1648 bool Camera::setOffset(
double value)
1653 offsetN->value = value;
1658 bool Camera::getOffset(
double *value)
1663 *value = offsetN->value;
1668 bool Camera::getOffsetMinMaxStep(
double *min,
double *max,
double *step)
1673 *min = offsetN->min;
1674 *max = offsetN->max;
1675 *step = offsetN->step;
1680 bool Camera::isBLOBEnabled()
1682 return (m_Parent->getClientManager()->isBLOBEnabled(getDeviceName(),
"CCD1"));
1685 bool Camera::setBLOBEnabled(
bool enable,
const QString &prop)
1687 m_Parent->getClientManager()->setBLOBEnabled(enable, getDeviceName(), prop);
1692 bool Camera::setFastExposureEnabled(
bool enable)
1695 m_FastExposureEnabled = enable;
1697 auto svp =
getSwitch(
"CCD_FAST_TOGGLE");
1702 svp->at(0)->setState(enable ? ISS_ON : ISS_OFF);
1703 svp->at(1)->setState(enable ? ISS_OFF : ISS_ON);
1709 bool Camera::setCaptureFormat(
const QString &format)
1711 auto svp =
getSwitch(
"CCD_CAPTURE_FORMAT");
1715 for (
auto &oneSwitch : *svp)
1716 oneSwitch.setState(oneSwitch.label == format ? ISS_ON : ISS_OFF);
1722 bool Camera::setFastCount(uint32_t count)
1724 auto nvp = getNumber(
"CCD_FAST_COUNT");
1729 nvp->at(0)->setValue(count);
1736 bool Camera::setStreamExposure(
double duration)
1738 auto nvp = getNumber(
"STREAMING_EXPOSURE");
1743 nvp->at(0)->setValue(duration);
1750 bool Camera::getStreamExposure(
double *duration)
1752 auto nvp = getNumber(
"STREAMING_EXPOSURE");
1757 *duration = nvp->at(0)->getValue();
1762 bool Camera::isCoolerOn()
1769 return (svp->at(0)->getState() == ISS_ON);
1772 bool Camera::getTemperatureRegulation(
double &ramp,
double &threshold)
1774 auto regulation = getProperty(
"CCD_TEMP_RAMP");
1775 if (!regulation.isValid())
1778 ramp = regulation.getNumber()->at(0)->getValue();
1779 threshold = regulation.getNumber()->at(1)->getValue();
1783 bool Camera::setTemperatureRegulation(
double ramp,
double threshold)
1785 auto regulation = getProperty(
"CCD_TEMP_RAMP");
1786 if (!regulation.isValid())
1789 regulation.getNumber()->at(0)->setValue(ramp);
1790 regulation.getNumber()->at(1)->setValue(threshold);
1795 bool Camera::setScopeInfo(
double focalLength,
double aperture)
1797 auto scopeInfo = getProperty(
"SCOPE_INFO");
1798 if (!scopeInfo.isValid())
1801 auto nvp = scopeInfo.getNumber();
1802 nvp->at(0)->setValue(focalLength);
1803 nvp->at(1)->setValue(aperture);
1809 bool Camera::WriteImageFileInternal(
const QString &filename,
char *buffer,
const size_t size)
1811 QFile file(filename);
1814 qCCritical(KSTARS_INDI) <<
"ISD:CCD Error: Unable to open write file: " <<
1821 for (
size_t nr = 0; nr < size; nr += n)
1823 n = out.writeRawData(buffer + nr, size - nr);
1830 ok = file.flush() &&
ok;
1839 QString Camera::getCaptureFormat()
const
1841 if (m_CaptureFormatIndex < 0 || m_CaptureFormats.
isEmpty() || m_CaptureFormatIndex >= m_CaptureFormats.
size())
1844 return m_CaptureFormats[m_CaptureFormatIndex];
1847 void Camera::setStretchValues(
double shadows,
double midtones,
double highlights)
1849 if (Options::useFITSViewer() ==
false || normalTabID < 0)
1852 auto tab = getFITSViewer()->tabs().at(normalTabID);
1857 tab->setStretchValues(shadows, midtones, highlights);
1860 void Camera::setAutoStretch()
1862 if (Options::useFITSViewer() ==
false || normalTabID < 0)
1865 auto tab = getFITSViewer()->tabs().at(normalTabID);
1870 auto view = tab->getView();
1872 if (!view->getAutoStretch())
1873 view->setAutoStretchParams();
1876 void Camera::toggleHiPSOverlay()
1878 if (Options::useFITSViewer() ==
false || normalTabID < 0)
1881 auto tab = getFITSViewer()->tabs().at(normalTabID);
1886 auto view = tab->getView();
1888 view->toggleHiPSOverlay();