summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJasem Mutlaq <mutlaqja@ikarustech.com>2016-09-24 20:14:49 (GMT)
committerJasem Mutlaq <mutlaqja@ikarustech.com>2016-09-24 20:14:49 (GMT)
commitaeb023954713b066e43f7e85d4c44cdf50255915 (patch)
treec7b90eeb3aee7f0809de120e12ba965881057ade
parentf19e281f7d0d928ffe53c9592b46bab173d7a253 (diff)
Moving all focus module files to their own dedicated directly
-rw-r--r--kstars/ekos/focus/focus.cpp2332
-rw-r--r--kstars/ekos/focus/focus.h483
-rw-r--r--kstars/ekos/focus/focus.ui938
3 files changed, 3753 insertions, 0 deletions
diff --git a/kstars/ekos/focus/focus.cpp b/kstars/ekos/focus/focus.cpp
new file mode 100644
index 0000000..88b4d22
--- /dev/null
+++ b/kstars/ekos/focus/focus.cpp
@@ -0,0 +1,2332 @@
+/* Ekos
+ Copyright (C) 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
+
+ This application is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ */
+
+#include "focus.h"
+#include "Options.h"
+
+#include <KMessageBox>
+#include <KLocalizedString>
+#include <KPlotting/KPlotWidget>
+#include <KPlotting/KPlotObject>
+#include <KPlotting/KPlotAxis>
+
+#include <KNotifications/KNotification>
+
+#include "indi/driverinfo.h"
+#include "indi/indicommon.h"
+#include "indi/clientmanager.h"
+#include "indi/indifilter.h"
+
+#include "auxiliary/kspaths.h"
+
+#include "fitsviewer/fitsviewer.h"
+#include "fitsviewer/fitstab.h"
+#include "fitsviewer/fitsview.h"
+#include "ekosmanager.h"
+#include "darklibrary.h"
+
+#include "kstars.h"
+#include "focusadaptor.h"
+
+#include <basedevice.h>
+
+#define MAXIMUM_ABS_ITERATIONS 30
+#define MAXIMUM_RESET_ITERATIONS 2
+#define DEFAULT_SUBFRAME_DIM 128
+#define AUTO_STAR_TIMEOUT 45000
+#define MINIMUM_PULSE_TIMER 32
+#define MAX_RECAPTURE_RETRIES 3
+
+namespace Ekos
+{
+
+Focus::Focus()
+{
+ setupUi(this);
+
+ new FocusAdaptor(this);
+ QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Focus", this);
+
+ currentFocuser = NULL;
+ currentCCD = NULL;
+ currentFilter = NULL;
+ filterName = NULL;
+ filterSlot = NULL;
+
+ canAbsMove = false;
+ canRelMove = false;
+ inAutoFocus = false;
+ inFocusLoop = false;
+ captureInProgress = false;
+ inSequenceFocus = false;
+ //starSelected = false;
+ //frameModified = false;
+ subFramed = false;
+ resetFocus = false;
+ m_autoFocusSuccesful = false;
+ filterPositionPending= false;
+
+ rememberUploadMode = ISD::CCD::UPLOAD_CLIENT;
+ HFRInc =0;
+ noStarCount=0;
+ reverseDir = false;
+ initialFocuserAbsPosition = -1;
+
+ state = Ekos::FOCUS_IDLE;
+
+ pulseDuration = 1000;
+
+ resetFocusIteration=0;
+ //fy=fw=fh=0;
+ //orig_x = orig_y = orig_w = orig_h =-1;
+ lockedFilterIndex=-1;
+ maxHFR=1;
+ minimumRequiredHFR = -1;
+ currentFilterIndex=-1;
+ minPos=1e6;
+ maxPos=0;
+ frameNum=0;
+
+ connect(startFocusB, SIGNAL(clicked()), this, SLOT(start()));
+ connect(stopFocusB, SIGNAL(clicked()), this, SLOT(checkStopFocus()));
+
+ connect(focusOutB, SIGNAL(clicked()), this, SLOT(FocusOut()));
+ connect(focusInB, SIGNAL(clicked()), this, SLOT(FocusIn()));
+
+ connect(captureB, SIGNAL(clicked()), this, SLOT(capture()));
+
+ connect(AutoModeR, SIGNAL(toggled(bool)), this, SLOT(toggleAutofocus(bool)));
+
+ connect(startLoopB, SIGNAL(clicked()), this, SLOT(startFraming()));
+
+ connect(kcfg_subFrame, SIGNAL(toggled(bool)), this, SLOT(toggleSubframe(bool)));
+
+ connect(resetFrameB, SIGNAL(clicked()), this, SLOT(resetFrame()));
+
+ connect(CCDCaptureCombo, SIGNAL(activated(QString)), this, SLOT(setDefaultCCD(QString)));
+ connect(CCDCaptureCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCCD(int)));
+
+ connect(focuserCombo, SIGNAL(activated(int)), this, SLOT(checkFocuser(int)));
+ connect(FilterCaptureCombo, SIGNAL(activated(int)), this, SLOT(checkFilter(int)));
+ connect(FilterPosCombo, SIGNAL(activated(int)), this, SLOT(updateFilterPos(int)));
+ connect(lockFilterCheck, SIGNAL(toggled(bool)), this, SLOT(filterLockToggled(bool)));
+ connect(setAbsTicksB, SIGNAL(clicked()), this, SLOT(setAbsoluteFocusTicks()));
+ connect(binningCombo, SIGNAL(activated(int)), this, SLOT(setActiveBinning(int)));
+ connect(focusBoxSize, SIGNAL(valueChanged(int)), this, SLOT(updateBoxSize(int)));
+
+ activeBin=Options::focusXBin();
+ binningCombo->setCurrentIndex(activeBin-1);
+
+ connect(clearDataB, SIGNAL(clicked()) , this, SLOT(clearDataPoints()));
+
+ lastFocusDirection = FOCUS_NONE;
+
+ focusType = FOCUS_MANUAL;
+
+ profilePlot->setBackground(QBrush(Qt::black));
+ profilePlot->xAxis->setBasePen(QPen(Qt::white, 1));
+ profilePlot->yAxis->setBasePen(QPen(Qt::white, 1));
+ profilePlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
+ profilePlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
+ profilePlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
+ profilePlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
+ profilePlot->xAxis->grid()->setZeroLinePen(Qt::NoPen);
+ profilePlot->yAxis->grid()->setZeroLinePen(Qt::NoPen);
+ profilePlot->xAxis->setBasePen(QPen(Qt::white, 1));
+ profilePlot->yAxis->setBasePen(QPen(Qt::white, 1));
+ profilePlot->xAxis->setTickPen(QPen(Qt::white, 1));
+ profilePlot->yAxis->setTickPen(QPen(Qt::white, 1));
+ profilePlot->xAxis->setSubTickPen(QPen(Qt::white, 1));
+ profilePlot->yAxis->setSubTickPen(QPen(Qt::white, 1));
+ profilePlot->xAxis->setTickLabelColor(Qt::white);
+ profilePlot->yAxis->setTickLabelColor(Qt::white);
+ profilePlot->xAxis->setLabelColor(Qt::white);
+ profilePlot->yAxis->setLabelColor(Qt::white);
+
+ firstGaus = NULL;
+
+ currentGaus = profilePlot->addGraph();
+ currentGaus->setLineStyle(QCPGraph::lsLine);
+ currentGaus->setPen(QPen(Qt::red, 2));
+
+ lastGaus = profilePlot->addGraph();
+ lastGaus->setLineStyle(QCPGraph::lsLine);
+ QPen pen(Qt::darkGreen);
+ pen.setStyle(Qt::DashLine);
+ pen.setWidth(2);
+ lastGaus->setPen(pen);
+
+ HFRPlot->setBackground(QBrush(Qt::black));
+
+ HFRPlot->xAxis->setBasePen(QPen(Qt::white, 1));
+ HFRPlot->yAxis->setBasePen(QPen(Qt::white, 1));
+
+ HFRPlot->xAxis->setTickPen(QPen(Qt::white, 1));
+ HFRPlot->yAxis->setTickPen(QPen(Qt::white, 1));
+
+ HFRPlot->xAxis->setSubTickPen(QPen(Qt::white, 1));
+ HFRPlot->yAxis->setSubTickPen(QPen(Qt::white, 1));
+
+ HFRPlot->xAxis->setTickLabelColor(Qt::white);
+ HFRPlot->yAxis->setTickLabelColor(Qt::white);
+
+ HFRPlot->xAxis->setLabelColor(Qt::white);
+ HFRPlot->yAxis->setLabelColor(Qt::white);
+
+ HFRPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
+ HFRPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
+ HFRPlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
+ HFRPlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
+ HFRPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen);
+ HFRPlot->yAxis->grid()->setZeroLinePen(Qt::NoPen);
+
+ HFRPlot->yAxis->setLabel(i18n("HFR"));
+
+ v_graph = HFRPlot->addGraph();
+ v_graph->setLineStyle(QCPGraph::lsNone);
+ v_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::red, 3));
+
+ resetButtons();
+
+ appendLogText(i18n("Idle."));
+
+ foreach(QString filter, FITSViewer::filterTypes)
+ filterCombo->addItem(filter);
+
+ filterCombo->setCurrentIndex(Options::focusEffect());
+ defaultScale = static_cast<FITSScale>(Options::focusEffect());
+ connect(filterCombo, SIGNAL(activated(int)), this, SLOT(filterChangeWarning(int)));
+
+ exposureIN->setValue(Options::focusExposure());
+ toleranceIN->setValue(Options::focusTolerance());
+ stepIN->setValue(Options::focusTicks());
+ kcfg_autoSelectStar->setChecked(Options::autoSelectStar());
+ focusBoxSize->setValue(Options::focusBoxSize());
+ maxTravelIN->setValue(Options::focusMaxTravel());
+ kcfg_subFrame->setChecked(Options::focusSubFrame());
+ suspendGuideCheck->setChecked(Options::suspendGuiding());
+ lockFilterCheck->setChecked(Options::lockFocusFilter());
+ darkFrameCheck->setChecked(Options::useFocusDarkFrame());
+ thresholdSpin->setValue(Options::focusThreshold());
+ focusFramesSpin->setValue(Options::focusFrames());
+
+ connect(thresholdSpin, SIGNAL(valueChanged(double)), this, SLOT(setThreshold(double)));
+ connect(focusFramesSpin, SIGNAL(valueChanged(int)), this, SLOT(setFrames(int)));
+}
+
+Focus::~Focus()
+{
+ //qDeleteAll(HFRAbsolutePoints);
+ // HFRAbsolutePoints.clear();
+}
+
+void Focus::toggleAutofocus(bool enable)
+{
+ if (enable)
+ {
+ focusType = FOCUS_AUTO;
+ drawHFRPlot();
+ }
+ else
+ {
+ focusType = FOCUS_MANUAL;
+ drawHFRPlot();
+ }
+
+ if (inFocusLoop || inAutoFocus)
+ abort();
+ else
+ resetButtons();
+}
+
+void Focus::resetFrame()
+{
+ if (currentCCD)
+ {
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ if (targetChip)
+ {
+ //fx=fy=fw=fh=0;
+ targetChip->resetFrame();
+
+ int x,y,w,h;
+ targetChip->getFrame(&x,&y,&w, &h);
+
+ QVariantMap settings;
+ settings["x"] = x;
+ settings["y"] = y;
+ settings["w"] = w;
+ settings["h"] = h;
+ settings["binx"] = 1;
+ settings["biny"] = 1;
+ frameSettings[targetChip] = settings;
+ //targetChip->setFocusFrame(0,0,0,0);
+
+ //starSelected = false;
+ starCenter = QVector3D();
+ subFramed = false;
+
+ FITSView *targetImage = targetChip->getImage(FITS_FOCUS);
+ if (targetImage)
+ targetImage->setTrackingBox(QRect());
+ }
+ }
+}
+
+bool Focus::setCCD(QString device)
+{
+ for (int i=0; i < CCDCaptureCombo->count(); i++)
+ if (device == CCDCaptureCombo->itemText(i))
+ {
+ CCDCaptureCombo->setCurrentIndex(i);
+ return true;
+ }
+
+ return false;
+}
+
+void Focus::setDefaultCCD(QString ccd)
+{
+ Options::setDefaultFocusCCD(ccd);
+}
+
+void Focus::checkCCD(int ccdNum)
+{
+ if (ccdNum == -1)
+ {
+ ccdNum = CCDCaptureCombo->currentIndex();
+
+ if (ccdNum == -1)
+ return;
+ }
+
+ if (ccdNum >=0 && ccdNum <= CCDs.count())
+ {
+ currentCCD = CCDs.at(ccdNum);
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+ if (targetChip)
+ {
+ binningCombo->setEnabled(targetChip->canBin());
+ kcfg_subFrame->setEnabled(targetChip->canSubframe());
+ kcfg_autoSelectStar->setEnabled(targetChip->canSubframe());
+ if (targetChip->canBin())
+ {
+ int subBinX=1,subBinY=1;
+ binningCombo->clear();
+ targetChip->getMaxBin(&subBinX, &subBinY);
+ for (int i=1; i <= subBinX; i++)
+ binningCombo->addItem(QString("%1x%2").arg(i).arg(i));
+
+ binningCombo->setCurrentIndex(activeBin-1);
+
+ }
+
+ QStringList isoList = targetChip->getISOList();
+ ISOCombo->clear();
+
+ if (isoList.isEmpty())
+ {
+ ISOCombo->setEnabled(false);
+ ISOLabel->setEnabled(false);
+ }
+ else
+ {
+ ISOCombo->setEnabled(true);
+ ISOLabel->setEnabled(true);
+ ISOCombo->addItems(isoList);
+ ISOCombo->setCurrentIndex(targetChip->getISOIndex());
+ }
+
+ }
+ }
+
+ syncCCDInfo();
+}
+
+void Focus::syncCCDInfo()
+{
+
+ if (currentCCD == NULL)
+ return;
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ if (frameSettings.contains(targetChip) == false)
+ {
+ int x,y,w,h;
+ if (targetChip->getFrame(&x, &y, &w, &h))
+ {
+ int binx=1,biny=1;
+ targetChip->getBinning(&binx, &biny);
+ if (w > 0 && h > 0)
+ {
+ QVariantMap settings;
+
+ settings["x"] = x;
+ settings["y"] = y;
+ settings["w"] = w;
+ settings["h"] = h;
+ settings["binx"] = binx;
+ settings["biny"] = biny;
+
+ frameSettings[targetChip] = settings;
+ }
+ }
+ }
+}
+
+void Focus::addFilter(ISD::GDInterface *newFilter)
+{
+ foreach(ISD::GDInterface *filter, Filters)
+ {
+ if (!strcmp(filter->getDeviceName(), newFilter->getDeviceName()))
+ return;
+ }
+
+ FilterCaptureLabel->setEnabled(true);
+ FilterCaptureCombo->setEnabled(true);
+ FilterPosLabel->setEnabled(true);
+ FilterPosCombo->setEnabled(true);
+ lockFilterCheck->setEnabled(true);
+
+ FilterCaptureCombo->addItem(newFilter->getDeviceName());
+
+ Filters.append(static_cast<ISD::Filter *>(newFilter));
+
+ checkFilter(0);
+
+ FilterCaptureCombo->setCurrentIndex(0);
+
+}
+
+bool Focus::setFilter(QString device, int filterSlot)
+{
+ bool deviceFound=false;
+
+ for (int i=0; i < FilterCaptureCombo->count(); i++)
+ if (device == FilterCaptureCombo->itemText(i))
+ {
+ checkFilter(i);
+ deviceFound = true;
+ break;
+ }
+
+ if (deviceFound == false)
+ return false;
+
+ if (filterSlot < FilterCaptureCombo->count())
+ FilterCaptureCombo->setCurrentIndex(filterSlot);
+
+ return true;
+}
+
+void Focus::checkFilter(int filterNum)
+{
+ if (filterNum == -1)
+ {
+ filterNum = FilterCaptureCombo->currentIndex();
+ if (filterNum == -1)
+ return;
+ }
+
+ QStringList filterAlias = Options::filterAlias();
+
+ if (filterNum <= Filters.count())
+ currentFilter = Filters.at(filterNum);
+
+ FilterPosCombo->clear();
+
+ filterName = currentFilter->getBaseDevice()->getText("FILTER_NAME");
+ filterSlot = currentFilter->getBaseDevice()->getNumber("FILTER_SLOT");
+
+ if (filterSlot == NULL)
+ {
+ KMessageBox::error(0, i18n("Unable to find FILTER_SLOT property in driver %1", currentFilter->getBaseDevice()->getDeviceName()));
+ return;
+ }
+
+ currentFilterIndex = filterSlot->np[0].value - 1;
+
+ for (int i=0; i < filterSlot->np[0].max; i++)
+ {
+ QString item;
+
+ if (filterName != NULL && (i < filterName->ntp))
+ item = filterName->tp[i].text;
+ else if (i < filterAlias.count() && filterAlias[i].isEmpty() == false)
+ item = filterAlias.at(i);
+ else
+ item = QString("Filter_%1").arg(i+1);
+
+ FilterPosCombo->addItem(item);
+
+ }
+
+ if (lockFilterCheck->isChecked() == false)
+ FilterPosCombo->setCurrentIndex( currentFilterIndex);
+ else
+ {
+ if (lockedFilterIndex < 0)
+ {
+ //lockedFilterIndex = currentFilterIndex;
+ lockedFilterIndex = Options::lockFocusFilterIndex();
+ emit filterLockUpdated(currentFilter, lockedFilterIndex);
+ }
+ FilterPosCombo->setCurrentIndex(lockedFilterIndex);
+ }
+
+ // If we are waiting to change the filter wheel, let's check if the condition is now met.
+ if (filterPositionPending)
+ {
+ if (lockedFilterIndex == currentFilterIndex)
+ {
+ filterPositionPending = false;
+ capture();
+ }
+ }
+
+}
+
+void Focus::filterLockToggled(bool enable)
+{
+ if (enable)
+ {
+ lockedFilterIndex = FilterPosCombo->currentIndex();
+ if (lockedFilterIndex >= 0)
+ Options::setLockFocusFilterIndex(lockedFilterIndex);
+ emit filterLockUpdated(currentFilter, lockedFilterIndex);
+ }
+ else if (filterSlot != NULL)
+ {
+ FilterPosCombo->setCurrentIndex(filterSlot->np[0].value-1);
+ emit filterLockUpdated(NULL, 0);
+ }
+}
+
+void Focus::updateFilterPos(int index)
+{
+ if (lockFilterCheck->isChecked() == true)
+ {
+ lockedFilterIndex = index;
+ Options::setLockFocusFilterIndex(lockedFilterIndex);
+ emit filterLockUpdated(currentFilter, lockedFilterIndex);
+ }
+}
+
+void Focus::addFocuser(ISD::GDInterface *newFocuser)
+{
+ focuserCombo->addItem(newFocuser->getDeviceName());
+
+ Focusers.append(static_cast<ISD::Focuser*>(newFocuser));
+
+ currentFocuser = static_cast<ISD::Focuser *> (newFocuser);
+
+ checkFocuser();
+}
+
+bool Focus::setFocuser(QString device)
+{
+ for (int i=0; i < focuserCombo->count(); i++)
+ if (device == focuserCombo->itemText(i))
+ {
+ checkFocuser(i);
+ return true;
+ }
+
+ return false;
+}
+
+void Focus::checkFocuser(int FocuserNum)
+{
+ if (FocuserNum == -1)
+ FocuserNum = focuserCombo->currentIndex();
+
+ if (FocuserNum <= Focusers.count())
+ currentFocuser = Focusers.at(FocuserNum);
+
+ if (currentFocuser->canAbsMove())
+ {
+ canAbsMove = true;
+ getAbsFocusPosition();
+
+ absTicksSpin->setEnabled(true);
+ setAbsTicksB->setEnabled(true);
+ }
+ else
+ {
+ canAbsMove = false;
+ absTicksSpin->setEnabled(false);
+ setAbsTicksB->setEnabled(false);
+ }
+
+ if (currentFocuser->canRelMove())
+ {
+ // We pretend this is an absolute focuser
+ canRelMove = true;
+ currentPosition = 50000;
+ absMotionMax = 100000;
+ absMotionMin = 0;
+ }
+
+ connect(currentFocuser, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SLOT(processFocusNumber(INumberVectorProperty*)), Qt::UniqueConnection);
+
+ AutoModeR->setEnabled(true);
+
+ resetButtons();
+
+ //if (!inAutoFocus && !inFocusLoop && !captureInProgress && !inSequenceFocus)
+ // emit autoFocusFinished(true, -1);
+}
+
+void Focus::addCCD(ISD::GDInterface *newCCD)
+{
+ if (CCDs.contains(static_cast<ISD::CCD *>(newCCD)))
+ return;
+
+ CCDs.append(static_cast<ISD::CCD *>(newCCD));
+
+ CCDCaptureCombo->addItem(newCCD->getDeviceName());
+
+ //checkCCD(CCDs.count()-1);
+ //CCDCaptureCombo->setCurrentIndex(CCDs.count()-1);
+}
+
+void Focus::getAbsFocusPosition()
+{
+ INumberVectorProperty *absMove = NULL;
+ if (canAbsMove)
+ {
+ absMove = currentFocuser->getBaseDevice()->getNumber("ABS_FOCUS_POSITION");
+
+ if (absMove)
+ {
+ currentPosition = absMove->np[0].value;
+ absMotionMax = absMove->np[0].max;
+ absMotionMin = absMove->np[0].min;
+
+ absTicksSpin->setMinimum(absMove->np[0].min);
+ absTicksSpin->setMaximum(absMove->np[0].max);
+ absTicksSpin->setSingleStep(absMove->np[0].step);
+
+ absTicksSpin->setValue(currentPosition);
+ }
+ }
+
+}
+
+void Focus::start()
+{
+ if (currentCCD == NULL)
+ {
+ appendLogText(i18n("No CCD connected."));
+ return;
+ }
+
+ lastFocusDirection = FOCUS_NONE;
+
+ lastHFR = 0;
+
+ if (canAbsMove)
+ {
+ absIterations = 0;
+ getAbsFocusPosition();
+ pulseDuration = stepIN->value();
+ }
+ else if (canRelMove)
+ {
+ appendLogText(i18n("Setting dummy central position to 50000"));
+ absIterations = 0;
+ pulseDuration = stepIN->value();
+ currentPosition = 50000;
+ absMotionMax = 100000;
+ absMotionMin = 0;
+ }
+ else
+ {
+ pulseDuration=stepIN->value();
+
+ if (pulseDuration <= MINIMUM_PULSE_TIMER)
+ {
+ appendLogText(i18n("Starting pulse step is too low. Increase the step size to %1 or higher...", MINIMUM_PULSE_TIMER*5));
+ return;
+ }
+ }
+
+ inAutoFocus = true;
+ m_autoFocusSuccesful = false;
+ frameNum=0;
+
+ resetButtons();
+
+ reverseDir = false;
+
+ /*if (fw > 0 && fh > 0)
+ starSelected= true;
+ else
+ starSelected= false;*/
+
+ clearDataPoints();
+
+ if (firstGaus)
+ {
+ profilePlot->removeGraph(firstGaus);
+ firstGaus = NULL;
+ }
+
+ Options::setFocusTicks(stepIN->value());
+ Options::setFocusTolerance(toleranceIN->value());
+ Options::setFocusExposure(exposureIN->value());
+ Options::setFocusXBin(activeBin);
+ Options::setFocusMaxTravel(maxTravelIN->value());
+
+ Options::setFocusSubFrame(kcfg_subFrame->isChecked());
+ Options::setAutoSelectStar(kcfg_autoSelectStar->isChecked());
+ Options::setSuspendGuiding(suspendGuideCheck->isChecked());
+ Options::setLockFocusFilter(lockFilterCheck->isChecked());
+ Options::setUseFocusDarkFrame(darkFrameCheck->isChecked());
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Starting focus with box size: " << focusBoxSize->value() << " Step Size: " << stepIN->value() << " Threshold: " << thresholdSpin->value() << " Tolerance: " << toleranceIN->value()
+ << " Frames: " << focusFramesSpin->value() << " Maximum Travel: " << maxTravelIN->value();
+
+ if (kcfg_autoSelectStar->isChecked())
+ appendLogText(i18n("Autofocus in progress..."));
+ else
+ appendLogText(i18n("Please wait until image capture is complete..."));
+
+ if (suspendGuideCheck->isChecked())
+ emit suspendGuiding(true);
+
+ //emit statusUpdated(true);
+ state = Ekos::FOCUS_PROGRESS;
+ emit newStatus(state);
+
+ // Denoise with median filter
+ //defaultScale = FITS_MEDIAN;
+
+ capture();
+}
+
+void Focus::checkStopFocus()
+{
+ if (inSequenceFocus == true)
+ {
+ inSequenceFocus = false;
+ setAutoFocusResult(false);
+ }
+
+ if (captureInProgress && inAutoFocus == false && inFocusLoop == false)
+ {
+ captureB->setEnabled(true);
+ stopFocusB->setEnabled(false);
+
+ appendLogText(i18n("Capture aborted."));
+ }
+
+ abort();
+}
+
+void Focus::abort()
+{
+ if (Options::focusLogging())
+ qDebug() << "Focus: Stopppig Focus";
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ inAutoFocus = false;
+ inFocusLoop = false;
+ //starSelected= false;
+ minimumRequiredHFR = -1;
+ noStarCount = 0;
+ frameNum=0;
+ //maxHFR=1;
+
+ disconnect(currentCCD, SIGNAL(BLOBUpdated(IBLOB*)), this, SLOT(newFITS(IBLOB*)));
+
+ if (rememberUploadMode != currentCCD->getUploadMode())
+ currentCCD->setUploadMode(rememberUploadMode);
+
+ targetChip->abortExposure();
+
+ //resetFrame();
+
+ FITSView *targetImage = targetChip->getImage(FITS_FOCUS);
+ if (targetImage)
+ targetImage->updateMode(FITS_FOCUS);
+
+ resetButtons();
+
+ absIterations = 0;
+ HFRInc=0;
+ reverseDir = false;
+
+ //emit statusUpdated(false);
+ state = Ekos::FOCUS_ABORTED;
+ emit newStatus(state);
+}
+
+void Focus::capture()
+{
+ if (currentCCD == NULL)
+ {
+ appendLogText(i18n("No CCD connected."));
+ return;
+ }
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ double seqExpose = exposureIN->value();
+
+ if (currentCCD->isConnected() == false)
+ {
+ appendLogText(i18n("Error: Lost connection to CCD."));
+ return;
+ }
+
+ if (currentFilter != NULL && lockFilterCheck->isChecked())
+ {
+ if (currentFilter->isConnected() == false)
+ {
+ appendLogText(i18n("Error: Lost connection to filter wheel."));
+ return;
+ }
+
+ if (lockedFilterIndex != currentFilterIndex)
+ {
+ int lockedFilterPosition = lockedFilterIndex + 1;
+ filterPositionPending = true;
+ appendLogText(i18n("Changing filter to %1", FilterPosCombo->currentText()));
+ currentFilter->runCommand(INDI_SET_FILTER, &lockedFilterPosition);
+ return;
+ }
+ }
+
+ if (currentCCD->getUploadMode() == ISD::CCD::UPLOAD_LOCAL)
+ {
+ rememberUploadMode = ISD::CCD::UPLOAD_LOCAL;
+ currentCCD->setUploadMode(ISD::CCD::UPLOAD_CLIENT);
+ }
+
+ targetChip->setBinning(activeBin, activeBin);
+
+ targetChip->setCaptureMode(FITS_FOCUS);
+
+ // Always disable filtering if using a dark frame and then re-apply after subtraction. TODO: Implement this in capture and guide and align
+ if (darkFrameCheck->isChecked())
+ targetChip->setCaptureFilter(FITS_NONE);
+ else
+ targetChip->setCaptureFilter(defaultScale);
+
+ if (ISOCombo->isEnabled() && ISOCombo->currentIndex() != -1 && targetChip->getISOIndex() != ISOCombo->currentIndex())
+ targetChip->setISOIndex(ISOCombo->currentIndex());
+
+ connect(currentCCD, SIGNAL(BLOBUpdated(IBLOB*)), this, SLOT(newFITS(IBLOB*)));
+
+ targetChip->setFrameType(FRAME_LIGHT);
+
+ if (frameSettings.contains(targetChip))
+ {
+ QVariantMap settings = frameSettings[targetChip];
+ targetChip->setFrame(settings["x"].toInt(), settings["y"].toInt(), settings["w"].toInt(), settings["h"].toInt());
+ settings["binx"] = activeBin;
+ settings["biny"] = activeBin;
+ frameSettings[targetChip] = settings;
+ }
+
+ captureInProgress = true;
+
+ targetChip->capture(seqExpose);
+
+ if (inFocusLoop == false)
+ {
+ appendLogText(i18n("Capturing image..."));
+
+ if (inAutoFocus == false)
+ {
+ captureB->setEnabled(false);
+ stopFocusB->setEnabled(true);
+ }
+ }
+}
+
+void Focus::FocusIn(int ms)
+{
+ if (currentFocuser == NULL)
+ return;
+
+ if (currentFocuser->isConnected() == false)
+ {
+ appendLogText(i18n("Error: Lost connection to Focuser."));
+ return;
+ }
+
+ if (ms == -1)
+ ms = stepIN->value();
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Focus in (" << ms << ")" ;
+
+ lastFocusDirection = FOCUS_IN;
+
+ currentFocuser->focusIn();
+
+ if (canAbsMove)
+ {
+ currentFocuser->moveAbs(currentPosition-ms);
+ appendLogText(i18n("Focusing inward..."));
+ }
+ else if (canRelMove)
+ {
+ currentFocuser->moveRel(ms);
+ appendLogText(i18n("Focusing inward..."));
+ }
+ else
+ {
+ currentFocuser->moveByTimer(ms);
+ appendLogText(i18n("Focusing inward by %1 ms...", ms));
+ }
+
+
+
+}
+
+void Focus::FocusOut(int ms)
+{
+ if (currentFocuser == NULL)
+ return;
+
+ if (currentFocuser->isConnected() == false)
+ {
+ appendLogText(i18n("Error: Lost connection to Focuser."));
+ return;
+ }
+
+ lastFocusDirection = FOCUS_OUT;
+
+ if (ms == -1)
+ ms = stepIN->value();
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Focus out (" << ms << ")" ;
+
+ currentFocuser->focusOut();
+
+ if (canAbsMove)
+ {
+ currentFocuser->moveAbs(currentPosition+ms);
+ appendLogText(i18n("Focusing outward..."));
+ }
+ else if (canRelMove)
+ {
+ currentFocuser->moveRel(ms);
+ appendLogText(i18n("Focusing outward..."));
+ }
+ else
+ {
+ currentFocuser->moveByTimer(ms);
+ appendLogText(i18n("Focusing outward by %1 ms...", ms));
+ }
+}
+
+void Focus::newFITS(IBLOB *bp)
+{
+ if (bp == NULL)
+ {
+ capture();
+ return;
+ }
+
+ // Ignore guide head if there is any.
+ if (!strcmp(bp->name, "CCD2"))
+ return;
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+ disconnect(currentCCD, SIGNAL(BLOBUpdated(IBLOB*)), this, SLOT(newFITS(IBLOB*)));
+
+ if (darkFrameCheck->isChecked())
+ {
+ FITSView *currentImage = targetChip->getImage(FITS_FOCUS);
+ FITSData *darkData = NULL;
+ QVariantMap settings = frameSettings[targetChip];
+ uint16_t offsetX = settings["x"].toInt() / settings["binx"].toInt();
+ uint16_t offsetY = settings["y"].toInt() / settings["biny"].toInt();
+
+ darkData = DarkLibrary::Instance()->getDarkFrame(targetChip, exposureIN->value());
+
+ connect(DarkLibrary::Instance(), SIGNAL(darkFrameCompleted(bool)), this, SLOT(setCaptureComplete()));
+ connect(DarkLibrary::Instance(), SIGNAL(newLog(QString)), this, SLOT(appendLogText(QString)));
+
+ if (darkData)
+ DarkLibrary::Instance()->subtract(darkData, currentImage, defaultScale, offsetX, offsetY);
+ else
+ DarkLibrary::Instance()->captureAndSubtract(targetChip, currentImage, exposureIN->value(), offsetX, offsetY);
+
+ return;
+ }
+
+ setCaptureComplete();
+}
+
+void Focus::setCaptureComplete()
+{
+ DarkLibrary::Instance()->disconnect(this);
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ // Always reset capture mode to NORMAL
+ targetChip->setCaptureMode(FITS_NORMAL);
+
+ FITSView *targetImage = targetChip->getImage(FITS_FOCUS);
+
+ if (targetImage == NULL)
+ {
+ appendLogText(i18n("FITS image failed to load, aborting..."));
+ abort();
+ return;
+ }
+
+ int subBinX=1, subBinY=1;
+ targetChip->getBinning(&subBinX, &subBinY);
+
+ if (starCenter.isNull() == false)
+ {
+ // If binning changed, update coords accordingly
+ if (subBinX != starCenter.z())
+ {
+ starCenter.setX(starCenter.x() * (starCenter.z()/subBinX));
+ starCenter.setY(starCenter.y() * (starCenter.z()/subBinY));
+ starCenter.setZ(subBinX);
+ }
+
+ QRect starRect = QRect( (starCenter.x()-focusBoxSize->value()/(2*subBinX)), starCenter.y()-focusBoxSize->value()/(2*subBinY), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY);
+ targetImage->setTrackingBox(starRect);
+ }
+
+ connect(targetImage, SIGNAL(trackingStarSelected(int,int)), this, SLOT(focusStarSelected(int, int)), Qt::UniqueConnection);
+
+ if (inFocusLoop == false)
+ appendLogText(i18n("Image received."));
+
+ if (captureInProgress && inFocusLoop == false && inAutoFocus==false)
+ {
+ captureB->setEnabled(true);
+ stopFocusB->setEnabled(false);
+ currentCCD->setUploadMode(rememberUploadMode);
+ }
+
+ captureInProgress = false;
+
+ FITSData *image_data = targetChip->getImageData();
+
+ starPixmap = targetImage->getTrackingBoxPixmap();
+ emit newStarPixmap(starPixmap);
+
+ // If we're not framing, let's try to detect stars
+ if (inFocusLoop == false || (inFocusLoop && targetImage->isTrackingBoxEnabled()))
+ {
+ if (image_data->areStarsSearched() == false)
+ {
+ if (targetImage->isTrackingBoxEnabled())
+ image_data->findStars(targetImage->getTrackingBox());
+ else
+ image_data->findStars();
+ }
+
+ currentHFR= image_data->getHFR(HFR_MAX);
+
+ /*if (currentHFR == -1)
+ {
+ currentHFR = image_data->getHFR();
+ }*/
+
+ if (Options::focusLogging())
+ qDebug() << "Focus newFITS #" << frameNum+1 << ": Current HFR " << currentHFR;
+
+ HFRFrames[frameNum++] = currentHFR;
+
+ if (frameNum >= focusFramesSpin->value())
+ {
+ currentHFR=0;
+ for (int i=0; i < frameNum; i++)
+ currentHFR+= HFRFrames[i];
+
+ currentHFR /= frameNum;
+ frameNum =0;
+ }
+ else
+ {
+ capture();
+ return;
+ }
+
+ emit newHFR(currentHFR);
+
+ QString HFRText = QString("%1").arg(currentHFR, 0,'g', 3);
+
+ if (focusType == FOCUS_MANUAL && lastHFR == -1)
+ appendLogText(i18n("FITS received. No stars detected."));
+
+ HFROut->setText(HFRText);
+
+ if (currentHFR > 0)
+ {
+ // Center tracking box around selected star
+ //if (starSelected && inAutoFocus)
+ if (starCenter.isNull() == false && inAutoFocus)
+ {
+ Edge *maxStarHFR = image_data->getMaxHFRStar();
+
+ if (maxStarHFR)
+ {
+ //int x = qMax(0, static_cast<int>(maxStarHFR->x-focusBoxSize->value()/(2*subBinX)));
+ //int y = qMax(0, static_cast<int>(maxStarHFR->y-focusBoxSize->value()/(2*subBinY)));
+
+ //targetImage->setTrackingBox(QRect(x, y, focusBoxSize->value(), focusBoxSize->value()));
+ starCenter.setX(qMax(0, static_cast<int>(maxStarHFR->x)));
+ starCenter.setY(qMax(0, static_cast<int>(maxStarHFR->y)));
+ targetImage->setTrackingBox(QRect( (starCenter.x()-focusBoxSize->value()/(2*subBinX)), starCenter.y()-focusBoxSize->value()/(2*subBinY), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY));
+ }
+ }
+
+ if (currentHFR > maxHFR)
+ maxHFR = currentHFR;
+
+ if (hfr_position.empty())
+ hfr_position.append(1);
+ else
+ hfr_position.append(hfr_position.last()+1);
+ hfr_value.append(currentHFR);
+
+ if (focusType == FOCUS_MANUAL || (inAutoFocus && canAbsMove == false && canRelMove == false))
+ drawHFRPlot();
+ }
+ }
+
+ // If just framing, let's capture again
+ if (inFocusLoop)
+ {
+ capture();
+ return;
+ }
+
+ //if (starSelected == false)
+ if (starCenter.isNull())
+ {
+ int x=0, y=0, w=0,h=0;
+
+ if (frameSettings.contains(targetChip))
+ {
+ QVariantMap settings = frameSettings[targetChip];
+ x = settings["x"].toInt();
+ y = settings["y"].toInt();
+ w = settings["w"].toInt();
+ h = settings["h"].toInt();
+ }
+ else
+ targetChip->getFrame(&x, &y, &w, &h);
+
+ if (kcfg_autoSelectStar->isEnabled() && kcfg_autoSelectStar->isChecked() && focusType == FOCUS_AUTO)
+ {
+ Edge *maxStar = image_data->getMaxHFRStar();
+ if (maxStar == NULL)
+ {
+ appendLogText(i18n("Failed to automatically select a star. Please select a star manually."));
+
+ //if (fw == 0 || fh == 0)
+ //targetChip->getFrame(&fx, &fy, &fw, &fh);
+
+ //targetImage->setTrackingBox(QRect((fw-focusBoxSize->value())/2, (fh-focusBoxSize->value())/2, focusBoxSize->value(), focusBoxSize->value()));
+ targetImage->setTrackingBox(QRect(w-focusBoxSize->value()/(subBinX*2), h-focusBoxSize->value()/(subBinY*2), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY));
+ targetImage->setTrackingBoxEnabled(true);
+
+ state = Ekos::FOCUS_WAITING;
+ emit newStatus(state);
+
+ QTimer::singleShot(AUTO_STAR_TIMEOUT, this, SLOT(checkAutoStarTimeout()));
+
+ return;
+ }
+
+ if (subFramed == false && kcfg_subFrame->isEnabled() && kcfg_subFrame->isChecked())
+ {
+ int offset = focusBoxSize->value();
+ int subX=(maxStar->x - offset) * subBinX;
+ int subY=(maxStar->y - offset) * subBinY;
+ int subW=offset*2*subBinX;
+ int subH=offset*2*subBinY;
+
+ int minX, maxX, minY, maxY, minW, maxW, minH, maxH;
+ targetChip->getFrameMinMax(&minX, &maxX, &minY, &maxY, &minW, &maxW, &minH, &maxH);
+
+ if (subX< minX)
+ subX=minX;
+ if (subY< minY)
+ subY= minY;
+ if ((subW+subX)> maxW)
+ subW=maxW-subX;
+ if ((subH+subY)> maxH)
+ subH=maxH-subY;
+
+ //targetChip->setFocusFrame(subX, subY, subW, subH);
+
+ //fx += subX;
+ //fy += subY;
+ //fw = subW;
+ //fh = subH;
+ //frameModified = true;
+
+ //x += subX;
+ //y += subY;
+ //w = subW;
+ //h = subH;
+
+ QVariantMap settings = frameSettings[targetChip];
+ settings["x"] = subX;
+ settings["y"] = subY;
+ settings["w"] = subW;
+ settings["h"] = subH;
+ settings["binx"] = subBinX;
+ settings["biny"] = subBinY;
+
+ frameSettings[targetChip] = settings;
+
+ starCenter.setX(subW/(2*subBinX));
+ starCenter.setY(subH/(2*subBinY));
+
+ subFramed = true;
+ }
+ else
+ {
+ starCenter.setX(maxStar->x);
+ starCenter.setY(maxStar->y);
+ }
+
+ starCenter.setZ(subBinX);
+ //else
+ //targetChip->getFrame(&fx, &fy, &fw, &fh);
+
+ //targetImage->setTrackingBox(QRect((w-focusBoxSize->value())/(subBinX*2), (h-focusBoxSize->value())/(subBinY*2), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinX));
+ targetImage->setTrackingBoxEnabled(true);
+
+ //starSelected=true;
+
+ defaultScale = static_cast<FITSScale>(filterCombo->currentIndex());
+
+ capture();
+
+ return;
+
+
+ }
+ else// if (kcfg_subFrame->isEnabled() && kcfg_subFrame->isChecked())
+ {
+ appendLogText(i18n("Capture complete. Select a star to focus."));
+
+ //if (fw == 0 || fh == 0)
+ //targetChip->getFrame(&fx, &fy, &fw, &fh);
+
+ int subBinX=1,subBinY=1;
+ targetChip->getBinning(&subBinX, &subBinY);
+
+ targetImage->setTrackingBox(QRect((w-focusBoxSize->value())/(subBinX*2), (h-focusBoxSize->value())/(2*subBinY), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY));
+ targetImage->setTrackingBoxEnabled(true);
+ connect(targetImage, SIGNAL(trackingStarSelected(int,int)), this, SLOT(focusStarSelected(int, int)), Qt::UniqueConnection);
+ return;
+ }
+ }
+
+ if (minimumRequiredHFR >= 0)
+ {
+ if (currentHFR == -1)
+ {
+ if (noStarCount++ < MAX_RECAPTURE_RETRIES)
+ {
+ appendLogText(i18n("No stars detected, capturing again..."));
+ // On Last Attempt reset focus frame to capture full frame and recapture star if possible
+ if (noStarCount == MAX_RECAPTURE_RETRIES)
+ resetFrame();
+ capture();
+ return;
+ }
+ else
+ {
+ noStarCount = 0;
+ setAutoFocusResult(false);
+ }
+ }
+ else if (currentHFR > minimumRequiredHFR)
+ {
+ inSequenceFocus = true;
+ AutoModeR->setChecked(true);
+ start();
+ }
+ else
+ {
+ setAutoFocusResult(true);
+ }
+
+ minimumRequiredHFR = -1;
+
+ return;
+ }
+
+ drawProfilePlot();
+
+ if (focusType == FOCUS_MANUAL || inAutoFocus==false)
+ return;
+
+ if (Options::focusLogging())
+ {
+ QDir dir;
+ QString path = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "autofocus/" + QDateTime::currentDateTime().toString("yyyy-MM-dd");
+ dir.mkpath(path);
+ QString name = "autofocus_frame_" + QDateTime::currentDateTime().toString("HH:mm:ss") + ".fits";
+ QString filename = path + QStringLiteral("/") + name;
+ targetImage->getImageData()->saveFITS(filename);
+ }
+
+ if (canAbsMove || canRelMove)
+ autoFocusAbs();
+ else
+ autoFocusRel();
+
+}
+
+void Focus::clearDataPoints()
+{
+ maxHFR=1;
+ hfr_position.clear();
+ hfr_value.clear();
+
+ drawHFRPlot();
+}
+
+void Focus::drawHFRPlot()
+{
+ v_graph->setData(hfr_position, hfr_value);
+
+ if (focusType == FOCUS_AUTO && (canAbsMove || canRelMove) )
+ {
+ //HFRPlot->xAxis->setLabel(i18n("Position"));
+ HFRPlot->xAxis->setRange(minPos-pulseDuration, maxPos+pulseDuration);
+ HFRPlot->yAxis->setRange(currentHFR/1.5, maxHFR);
+ }
+ else
+ {
+ //HFRPlot->xAxis->setLabel(i18n("Iteration"));
+ HFRPlot->xAxis->setRange(1, hfr_value.count()+1);
+ HFRPlot->yAxis->setRange(currentHFR/1.5, maxHFR);
+ }
+
+ HFRPlot->replot();
+
+}
+
+void Focus::drawProfilePlot()
+{
+ QVector<double> currentIndexes;
+ QVector<double> currentFrequencies;
+
+ // HFR = 50% * 1.36 = 68% aka one standard deviation
+ double stdDev = currentHFR * 1.36;
+ float start= -stdDev*4;
+ float end = stdDev*4;
+ float step = stdDev*4 / 20.0;
+ for (float x=start; x < end; x+= step)
+ {
+ currentIndexes.append(x);
+ currentFrequencies.append((1/(stdDev*sqrt(2*M_PI))) * exp(-1 * ( x*x ) / (2 * (stdDev * stdDev))));
+ }
+
+ currentGaus->setData(currentIndexes, currentFrequencies);
+
+ if (lastGausIndexes.count() > 0)
+ lastGaus->setData(lastGausIndexes, lastGausFrequencies);
+
+ if (focusType == FOCUS_AUTO && firstGaus == NULL)
+ {
+ firstGaus = profilePlot->addGraph();
+ QPen pen;
+ pen.setStyle(Qt::DashDotLine);
+ pen.setWidth(2);
+ pen.setColor(Qt::darkMagenta);
+ firstGaus->setPen(pen);
+
+ firstGaus->setData(currentIndexes, currentFrequencies);
+ }
+ else if (focusType == FOCUS_MANUAL && firstGaus)
+ {
+ profilePlot->removeGraph(firstGaus);
+ firstGaus=NULL;
+ }
+
+ profilePlot->rescaleAxes();
+ profilePlot->replot();
+
+ lastGausIndexes = currentIndexes;
+ lastGausFrequencies = currentFrequencies;
+
+ profilePixmap = profilePlot->grab();
+ emit newProfilePixmap(profilePixmap);
+}
+
+void Focus::autoFocusAbs()
+{
+ static int lastHFRPos=0, minHFRPos=0, initSlopePos=0, focusOutLimit=0, focusInLimit=0;
+ static double minHFR=0, initSlopeHFR=0;
+ double targetPosition=0, delta=0;
+
+ QString deltaTxt = QString("%1").arg(fabs(currentHFR-minHFR)*100.0, 0,'g', 3);
+ QString HFRText = QString("%1").arg(currentHFR, 0,'g', 3);
+
+ if (Options::focusLogging())
+ {
+ qDebug() << "Focus: ########################################";
+ qDebug() << "Focus: ========================================";
+ qDebug() << "Focus: Current HFR: " << currentHFR << " Current Position: " << currentPosition;
+ qDebug() << "Focus: Last minHFR: " << minHFR << " Last MinHFR Pos: " << minHFRPos;
+ qDebug() << "Focus: Delta: " << deltaTxt << "%";
+ qDebug() << "Focus: ========================================";
+ }
+
+ if (minHFR)
+ appendLogText(i18n("FITS received. HFR %1 @ %2. Delta (%3%)", HFRText, currentPosition, deltaTxt));
+ else
+ appendLogText(i18n("FITS received. HFR %1 @ %2.", HFRText, currentPosition));
+
+ if (++absIterations > MAXIMUM_ABS_ITERATIONS)
+ {
+ appendLogText(i18n("Autofocus failed to reach proper focus. Try increasing tolerance value."));
+ abort();
+ setAutoFocusResult(false);
+ return;
+ }
+
+ // No stars detected, try to capture again
+ if (currentHFR == -1)
+ {
+ if (noStarCount < MAX_RECAPTURE_RETRIES)
+ {
+ appendLogText(i18n("No stars detected, capturing again..."));
+ capture();
+ noStarCount++;
+ return;
+ }
+ else if (noStarCount == MAX_RECAPTURE_RETRIES)
+ {
+ currentHFR = 20;
+ noStarCount++;
+ }
+ else
+ {
+ appendLogText(i18n("Failed to detect any stars. Reset frame and try again."));
+ abort();
+ setAutoFocusResult(false);
+ return;
+ }
+ }
+ else
+ noStarCount = 0;
+
+ /*if (currentHFR > maxHFR || HFRAbsolutePoints.empty())
+ {
+ maxHFR = currentHFR;
+
+ if (HFRAbsolutePoints.empty())
+ {
+ maxPos=1;
+ minPos=1e6;
+ }
+ }*/
+
+ if (hfr_position.empty())
+ {
+ maxPos=1;
+ minPos=1e6;
+ }
+
+ if (currentPosition > maxPos)
+ maxPos = currentPosition;
+ if (currentPosition < minPos)
+ minPos = currentPosition;
+
+ //HFRPoint *p = new HFRPoint();
+
+ //p->HFR = currentHFR;
+ //p->pos = currentPosition;
+
+ hfr_position.append(currentPosition);
+ hfr_value.append(currentHFR);
+
+ //HFRAbsolutePoints.append(p);
+
+ drawHFRPlot();
+
+ switch (lastFocusDirection)
+ {
+ case FOCUS_NONE:
+ lastHFR = currentHFR;
+ initialFocuserAbsPosition = currentPosition;
+ minHFR=currentHFR;
+ minHFRPos=currentPosition;
+ HFRDec=0;
+ HFRInc=0;
+ focusOutLimit=0;
+ focusInLimit=0;
+ FocusOut(pulseDuration);
+ break;
+
+ case FOCUS_IN:
+ case FOCUS_OUT:
+ if (reverseDir && focusInLimit && focusOutLimit && fabs(currentHFR - minHFR) < (toleranceIN->value()/100.0) && HFRInc == 0 )
+ {
+ if (absIterations <= 2)
+ {
+ appendLogText(i18n("Change in HFR is too small. Try increasing the step size or decreasing the tolerance."));
+ abort();
+ setAutoFocusResult(false);
+ }
+ else if (noStarCount > 0)
+ {
+ appendLogText(i18n("Failed to detect focus star in frame. Capture and select a focus star."));
+ abort();
+ setAutoFocusResult(false);
+ }
+ else
+ {
+ appendLogText(i18n("Autofocus complete."));
+ abort();
+ emit suspendGuiding(false);
+ setAutoFocusResult(true);
+ }
+ break;
+ }
+ else if (currentHFR < lastHFR)
+ {
+ double slope=0;
+
+ // Let's try to calculate slope of the V curve.
+ if (initSlopeHFR == 0 && HFRInc == 0 && HFRDec >= 1)
+ {
+ initSlopeHFR = lastHFR;
+ initSlopePos = lastHFRPos;
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Setting initial slop to " << initSlopePos << " @ HFR " << initSlopeHFR;
+ }
+
+ // Let's now limit the travel distance of the focuser
+ if (lastFocusDirection == FOCUS_OUT && lastHFRPos < focusInLimit && fabs(currentHFR - lastHFR) > 0.1)
+ {
+ focusInLimit = lastHFRPos;
+ if (Options::focusLogging())
+ qDebug() << "Focus: New FocusInLimit " << focusInLimit;
+ }
+ else if (lastFocusDirection == FOCUS_IN && lastHFRPos > focusOutLimit && fabs(currentHFR - lastHFR) > 0.1)
+ {
+ focusOutLimit = lastHFRPos;
+ if (Options::focusLogging())
+ qDebug() << "Focus: New FocusOutLimit " << focusOutLimit;
+ }
+
+ // If we have slope, get next target position
+ if (initSlopeHFR && absMotionMax > 50)
+ {
+ double factor=0.5;
+ slope = (currentHFR - initSlopeHFR) / (currentPosition - initSlopePos);
+ if (fabs(currentHFR-minHFR)*100.0 < 0.5)
+ factor = 1 - fabs(currentHFR-minHFR)*10;
+ targetPosition = currentPosition + (currentHFR*factor - currentHFR)/slope;
+ if (targetPosition < 0)
+ {
+ factor = 1;
+ while (targetPosition < 0 && factor > 0)
+ {
+ factor -= 0.005;
+ targetPosition = currentPosition + (currentHFR*factor - currentHFR)/slope;
+ }
+ }
+ if (Options::focusLogging())
+ qDebug() << "Focus: Using slope to calculate target pulse...";
+ }
+ // Otherwise proceed iteratively
+ else
+ {
+ if (lastFocusDirection == FOCUS_IN)
+ targetPosition = currentPosition - pulseDuration;
+ else
+ targetPosition = currentPosition + pulseDuration;
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Proceeding iteratively to next target pulse ...";
+ }
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: V-Curve Slope " << slope << " current Position " << currentPosition << " targetPosition " << targetPosition;
+
+ lastHFR = currentHFR;
+
+ // Let's keep track of the minimum HFR
+ if (lastHFR < minHFR)
+ {
+ minHFR = lastHFR;
+ minHFRPos = currentPosition;
+ if (Options::focusLogging())
+ {
+ qDebug() << "Focus: new minHFR " << minHFR << " @ positioin " << minHFRPos;
+ qDebug() << "Focus: ########################################";
+ }
+
+ }
+
+ lastHFRPos = currentPosition;
+
+ // HFR is decreasing, we are on the right direction
+ HFRDec++;
+ HFRInc=0;
+ }
+ else
+
+ {
+ // HFR increased, let's deal with it.
+ HFRInc++;
+ HFRDec=0;
+
+ // Reality Check: If it's first time, let's capture again and see if it changes.
+ /*if (HFRInc <= 1 && reverseDir == false)
+ {
+ capture();
+ return;
+ }
+ // Looks like we're going away from optimal HFR
+ else
+ {*/
+ reverseDir = true;
+ lastHFR = currentHFR;
+ lastHFRPos = currentPosition;
+ initSlopeHFR=0;
+ HFRInc=0;
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: We are going away from optimal HFR ";
+
+ // Let's set new limits
+ if (lastFocusDirection == FOCUS_IN)
+ {
+ focusInLimit = currentPosition;
+ if (Options::focusLogging())
+ qDebug() << "Focus: Setting focus IN limit to " << focusInLimit;
+ }
+ else
+ {
+ focusOutLimit = currentPosition;
+ if (Options::focusLogging())
+ qDebug() << "Focus: Setting focus OUT limit to " << focusOutLimit;
+ }
+
+ // Decrease pulse
+ pulseDuration = pulseDuration * 0.75;
+
+ // Let's get close to the minimum HFR position so far detected
+ if (lastFocusDirection == FOCUS_OUT)
+ targetPosition = minHFRPos-pulseDuration/2;
+ else
+ targetPosition = minHFRPos+pulseDuration/2;
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: new targetPosition " << targetPosition;
+
+ // }
+ }
+
+ // Limit target Pulse to algorithm limits
+ if (focusInLimit != 0 && lastFocusDirection == FOCUS_IN && targetPosition < focusInLimit)
+ {
+ targetPosition = focusInLimit;
+ if (Options::focusLogging())
+ qDebug() << "Focus: Limiting target pulse to focus in limit " << targetPosition;
+ }
+ else if (focusOutLimit != 0 && lastFocusDirection == FOCUS_OUT && targetPosition > focusOutLimit)
+ {
+ targetPosition = focusOutLimit;
+ if (Options::focusLogging())
+ qDebug() << "Focus: Limiting target pulse to focus out limit " << targetPosition;
+ }
+
+ // Limit target pulse to focuser limits
+ if (targetPosition < absMotionMin)
+ targetPosition = absMotionMin;
+ else if (targetPosition > absMotionMax)
+ targetPosition = absMotionMax;
+
+ // Ops, we can't go any further, we're done.
+ if (targetPosition == currentPosition)
+ {
+ appendLogText(i18n("Autofocus complete."));
+ abort();
+ emit suspendGuiding(false);
+ setAutoFocusResult(true);
+ return;
+ }
+
+ // Ops, deadlock
+ if (focusOutLimit && focusOutLimit == focusInLimit)
+ {
+ appendLogText(i18n("Deadlock reached. Please try again with different settings."));
+ abort();
+ setAutoFocusResult(false);
+ return;
+ }
+
+ if (fabs(targetPosition - initialFocuserAbsPosition) > maxTravelIN->value())
+ {
+ if (Options::focusLogging())
+ qDebug() << "Focus: targetPosition (" << targetPosition << ") - initHFRAbsPos (" << initialFocuserAbsPosition << ") exceeds maxTravel distance of " << maxTravelIN->value();
+
+ appendLogText("Maximum travel limit reached. Autofocus aborted.");
+ abort();
+ setAutoFocusResult(false);
+ break;
+
+ }
+
+ // Get delta for next move
+ delta = (targetPosition - currentPosition);
+
+ if (Options::focusLogging())
+ {
+ qDebug() << "Focus: delta (targetPosition - currentPosition) " << delta;
+ qDebug() << "Focus: Focusing " << ((delta < 0) ? "IN" : "OUT");
+ qDebug() << "Focus: ########################################";
+ }
+
+ // Now cross your fingers and wait
+ if (delta > 0)
+ FocusOut(delta);
+ else
+ FocusIn(fabs(delta));
+ break;
+
+ }
+
+}
+
+void Focus::autoFocusRel()
+{
+ static int noStarCount=0;
+ static double minHFR=1e6;
+ QString deltaTxt = QString("%1").arg(fabs(currentHFR-minHFR)*100.0, 0,'g', 2);
+ QString minHFRText = QString("%1").arg(minHFR, 0, 'g', 3);
+ QString HFRText = QString("%1").arg(currentHFR, 0,'g', 3);
+
+ appendLogText(i18n("FITS received. HFR %1. Delta (%2%) Min HFR (%3)", HFRText, deltaTxt, minHFRText));
+
+ if (pulseDuration <= MINIMUM_PULSE_TIMER)
+ {
+ appendLogText(i18n("Autofocus failed to reach proper focus. Try adjusting the tolerance value."));
+ abort();
+ setAutoFocusResult(false);
+ return;
+ }
+
+ // No stars detected, try to capture again
+ if (currentHFR == -1)
+ {
+ if (noStarCount++ < MAX_RECAPTURE_RETRIES)
+ {
+ appendLogText(i18n("No stars detected, capturing again..."));
+ capture();
+ return;
+ }
+ else
+ currentHFR = 20;
+ }
+ else
+ noStarCount = 0;
+
+ switch (lastFocusDirection)
+ {
+ case FOCUS_NONE:
+ lastHFR = currentHFR;
+ minHFR=1e6;
+ FocusIn(pulseDuration);
+ break;
+
+ case FOCUS_IN:
+ //if (fabs(currentHFR - lastHFR) < (toleranceIN->value()/100.0) && HFRInc == 0)
+ if (fabs(currentHFR - minHFR) < (toleranceIN->value()/100.0) && HFRInc == 0)
+ {
+ appendLogText(i18n("Autofocus complete."));
+ abort();
+ emit suspendGuiding(false);
+ setAutoFocusResult(true);
+ break;
+ }
+ else if (currentHFR < lastHFR)
+ {
+ if (currentHFR < minHFR)
+ minHFR = currentHFR;
+
+ lastHFR = currentHFR;
+ FocusIn(pulseDuration);
+ HFRInc=0;
+ }
+ else
+ {
+ HFRInc++;
+
+ /*if (HFRInc <= 1)
+ {
+ capture();
+ return;
+ }
+ else
+ {*/
+
+ lastHFR = currentHFR;
+
+ HFRInc=0;
+
+ pulseDuration *= 0.75;
+ FocusOut(pulseDuration);
+ //}
+ }
+
+ break;
+
+ case FOCUS_OUT:
+ if (fabs(currentHFR - minHFR) < (toleranceIN->value()/100.0) && HFRInc == 0)
+ //if (fabs(currentHFR - lastHFR) < (toleranceIN->value()/100.0) && HFRInc == 0)
+ {
+ appendLogText(i18n("Autofocus complete."));
+ abort();
+ emit suspendGuiding(false);
+ setAutoFocusResult(true);
+ break;
+ }
+ else if (currentHFR < lastHFR)
+ {
+ if (currentHFR < minHFR)
+ minHFR = currentHFR;
+
+ lastHFR = currentHFR;
+ FocusOut(pulseDuration);
+ HFRInc=0;
+ }
+ else
+ {
+ HFRInc++;
+
+ /*if (HFRInc <= 1)
+ capture();
+ else
+ {*/
+
+ lastHFR = currentHFR;
+
+ HFRInc=0;
+
+ pulseDuration *= 0.75;
+ FocusIn(pulseDuration);
+ //}
+ }
+
+ break;
+
+ }
+
+}
+
+void Focus::processFocusNumber(INumberVectorProperty *nvp)
+{
+
+ if (canAbsMove == false && currentFocuser->canAbsMove())
+ {
+ canAbsMove = true;
+ getAbsFocusPosition();
+
+ absTicksSpin->setEnabled(true);
+ setAbsTicksB->setEnabled(true);
+ }
+
+ if (canRelMove == false && currentFocuser->canRelMove())
+ canRelMove = true;
+
+ if (!strcmp(nvp->name, "ABS_FOCUS_POSITION"))
+ {
+ INumber *pos = IUFindNumber(nvp, "FOCUS_ABSOLUTE_POSITION");
+ if (pos)
+ {
+ currentPosition = pos->value;
+ absTicksSpin->setValue(currentPosition);
+ }
+
+ if (resetFocus && nvp->s == IPS_OK)
+ {
+ resetFocus = false;
+ appendLogText(i18n("Restarting autofocus process..."));
+ start();
+ }
+
+ if (canAbsMove && inAutoFocus)
+ {
+ if (nvp->s == IPS_OK && captureInProgress == false)
+ capture();
+ else if (nvp->s == IPS_ALERT)
+ {
+ appendLogText(i18n("Focuser error, check INDI panel."));
+ abort();
+ setAutoFocusResult(false);
+ }
+
+ }
+
+ return;
+ }
+
+ if (canAbsMove)
+ return;
+
+ if (!strcmp(nvp->name, "REL_FOCUS_POSITION"))
+ {
+ INumber *pos = IUFindNumber(nvp, "FOCUS_RELATIVE_POSITION");
+ if (pos && nvp->s == IPS_OK)
+ currentPosition += pos->value * (lastFocusDirection == FOCUS_IN ? -1 : 1);
+
+ if (resetFocus && nvp->s == IPS_OK)
+ {
+ resetFocus = false;
+ appendLogText(i18n("Restarting autofocus process..."));
+ start();
+ }
+
+ if (canRelMove && inAutoFocus)
+ {
+ if (nvp->s == IPS_OK && captureInProgress == false)
+ capture();
+ else if (nvp->s == IPS_ALERT)
+ {
+ appendLogText(i18n("Focuser error, check INDI panel."));
+ abort();
+ setAutoFocusResult(false);
+ }
+ }
+
+ return;
+ }
+
+ if (canRelMove)
+ return;
+
+ if (!strcmp(nvp->name, "FOCUS_TIMER"))
+ {
+ if (resetFocus && nvp->s == IPS_OK)
+ {
+ resetFocus = false;
+ appendLogText(i18n("Restarting autofocus process..."));
+ start();
+ }
+
+ if (canAbsMove == false && canRelMove == false && inAutoFocus)
+ {
+ if (nvp->s == IPS_OK && captureInProgress == false)
+ capture();
+ else if (nvp->s == IPS_ALERT)
+ {
+ appendLogText(i18n("Focuser error, check INDI panel."));
+ abort();
+ setAutoFocusResult(false);
+ }
+
+ }
+
+ return;
+ }
+
+}
+
+void Focus::appendLogText(const QString &text)
+{
+
+ logText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text));
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: " << text;
+
+ emit newLog();
+}
+
+void Focus::clearLog()
+{
+ logText.clear();
+ emit newLog();
+}
+
+void Focus::startFraming()
+{
+ if (currentCCD == NULL)
+ {
+ appendLogText(i18n("No CCD connected."));
+ return;
+ }
+
+ inFocusLoop = true;
+ frameNum=0;
+
+ //emit statusUpdated(true);
+ state = Ekos::FOCUS_FRAMING;
+ emit newStatus(state);
+
+ resetButtons();
+
+ appendLogText(i18n("Starting continuous exposure..."));
+
+ capture();
+}
+
+void Focus::resetButtons()
+{
+
+ if (inFocusLoop)
+ {
+ startFocusB->setEnabled(false);
+ startLoopB->setEnabled(false);
+ stopFocusB->setEnabled(true);
+
+ captureB->setEnabled(false);
+ focusOutB->setEnabled(true);
+ focusInB->setEnabled(true);
+
+ return;
+ }
+
+ if (inAutoFocus)
+ {
+ stopFocusB->setEnabled(true);
+
+ startFocusB->setEnabled(false);
+ startLoopB->setEnabled(false);
+ captureB->setEnabled(false);
+ focusOutB->setEnabled(false);
+ focusInB->setEnabled(false);
+
+ return;
+ }
+
+ if (focusType == FOCUS_AUTO && currentFocuser)
+ startFocusB->setEnabled(true);
+ else
+ startFocusB->setEnabled(false);
+
+ stopFocusB->setEnabled(false);
+ startLoopB->setEnabled(true);
+
+
+ if (focusType == FOCUS_MANUAL)
+ {
+ if (currentFocuser)
+ {
+ focusOutB->setEnabled(true);
+ focusInB->setEnabled(true);
+ }
+
+ startLoopB->setEnabled(true);
+ }
+ else
+ {
+ focusOutB->setEnabled(false);
+ focusInB->setEnabled(false);
+ }
+
+ captureB->setEnabled(true);
+}
+
+void Focus::updateBoxSize(int value)
+{
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+ if (targetChip == NULL)
+ return;
+
+ FITSView *targetImage = targetChip->getImage(FITS_FOCUS);
+ if (targetImage == NULL)
+ return;
+
+ QRect trackBox = targetImage->getTrackingBox();
+ trackBox.setX(trackBox.x()+(trackBox.width()-value)/2);
+ trackBox.setY(trackBox.y()+(trackBox.height()-value)/2);
+ trackBox.setWidth(value);
+ trackBox.setHeight(value);
+
+ targetImage->setTrackingBox(trackBox);
+}
+
+void Focus::focusStarSelected(int x, int y)
+{
+ if (state == Ekos::FOCUS_PROGRESS)
+ return;
+
+ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
+
+ int subBinX, subBinY;
+ targetChip->getBinning(&subBinX, &subBinY);
+
+ // If binning was changed outside of the focus module, recapture
+ if (subBinX != activeBin)
+ {
+ capture();
+ return;
+ }
+
+ int offset = focusBoxSize->value()/subBinX;
+
+ FITSView *targetImage = targetChip->getImage(FITS_FOCUS);
+
+ //targetImage->updateMode(FITS_FOCUS);
+
+ QRect starRect;
+
+ if (subFramed == false && kcfg_subFrame->isChecked() && targetChip->canSubframe())
+ {
+ int minX, maxX, minY, maxY, minW, maxW, minH, maxH;//, fx,fy,fw,fh;
+
+ targetChip->getFrameMinMax(&minX, &maxX, &minY, &maxY, &minW, &maxW, &minH, &maxH);
+ //targetChip->getFrame(&fx, &fy, &fw, &fy);
+
+ x = (x - offset) * subBinX;
+ y = (y - offset) * subBinY;
+ int w=offset*2*subBinX;
+ int h=offset*2*subBinY;
+
+ if (x<minX)
+ x=minX;
+ if (y<minY)
+ y=minY;
+ if ((x+w)>maxW)
+ w=maxW-x;
+ if ((y+h)>maxH)
+ h=maxH-y;
+
+ //targetChip->getFrame(&orig_x, &orig_y, &orig_w, &orig_h);
+
+ //fx += x;
+ //fy += y;
+ //fw = w;
+ //fh = h;
+
+ //targetChip->setFocusFrame(fx, fy, fw, fh);
+ //frameModified=true;
+
+ QVariantMap settings = frameSettings[targetChip];
+ settings["x"] = x;
+ settings["y"] = y;
+ settings["w"] = w;
+ settings["h"] = h;
+ settings["binx"] = subBinX;
+ settings["biny"] = subBinY;
+
+ frameSettings[targetChip] = settings;
+
+ subFramed = true;
+
+ capture();
+
+ //starRect = QRect((w-focusBoxSize->value())/(subBinX*2), (h-focusBoxSize->value())/(subBinY*2), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY);
+ starCenter.setX(w/(2*subBinX));
+ starCenter.setY(h/(2*subBinY));
+ }
+ else
+ {
+ //starRect = QRect(x-focusBoxSize->value()/(subBinX*2), y-focusBoxSize->value()/(subBinY*2), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY);
+ starCenter.setX(x);
+ starCenter.setY(y);
+ starRect = QRect( starCenter.x()-focusBoxSize->value()/(2*subBinX), starCenter.y()-focusBoxSize->value()/(2*subBinY), focusBoxSize->value()/subBinX, focusBoxSize->value()/subBinY);
+ targetImage->setTrackingBox(starRect);
+ }
+
+ starCenter.setZ(subBinX);
+
+ //starSelected=true;
+
+ defaultScale = static_cast<FITSScale>(filterCombo->currentIndex());
+
+ //targetImage->setTrackingBox(starRect);
+}
+
+void Focus::checkFocus(double requiredHFR)
+{
+ minimumRequiredHFR = requiredHFR;
+
+ capture();
+}
+
+void Focus::toggleSubframe(bool enable)
+{
+ if (enable == false)
+ {
+ resetFrame();
+ kcfg_autoSelectStar->setChecked(false);
+ }
+
+ //starSelected = false;
+ starCenter = QVector3D();
+}
+
+void Focus::filterChangeWarning(int index)
+{
+ // index = 4 is MEDIAN filter which helps reduce noise
+ if (index != 0 && index != FITS_MEDIAN)
+ appendLogText(i18n("Warning: Only use filters for preview as they may interface with autofocus operation."));
+
+ Options::setFocusEffect(index);
+
+ defaultScale = static_cast<FITSScale>(index);
+}
+
+void Focus::setExposure(double value)
+{
+ exposureIN->setValue(value);
+}
+
+void Focus::setBinning(int subBinX, int subBinY)
+{
+ INDI_UNUSED(subBinY);
+ binningCombo->setCurrentIndex(subBinX-1);
+}
+
+void Focus::setImageFilter(const QString & value)
+{
+ for (int i=0; i < filterCombo->count(); i++)
+ if (filterCombo->itemText(i) == value)
+ {
+ filterCombo->setCurrentIndex(i);
+ break;
+ }
+}
+
+void Focus::setAutoFocusStar(bool enable)
+{
+ kcfg_autoSelectStar->setChecked(enable);
+}
+
+void Focus::setAutoFocusSubFrame(bool enable)
+{
+ kcfg_subFrame->setChecked(enable);
+}
+
+void Focus::setAutoFocusParameters(int boxSize, int stepSize, int maxTravel, double tolerance)
+{
+ focusBoxSize->setValue(boxSize);
+ stepIN->setValue(stepSize);
+ maxTravelIN->setValue(maxTravel);
+ toleranceIN->setValue(tolerance);
+}
+
+bool Focus::setFocusMode(int mode)
+{
+ // If either of them is disabled, return false
+ if ( (mode == 0 && manualModeR->isEnabled() == false) || (mode == 1 && AutoModeR->isEnabled() == false) )
+ return false;
+
+ if (mode == 0)
+ manualModeR->setChecked(true);
+ else
+ AutoModeR->setChecked(true);
+
+ checkCCD();
+
+ return true;
+}
+
+void Focus::setAutoFocusResult(bool status)
+{
+ m_autoFocusSuccesful = status;
+
+ // In case of failure, go back to last position if the focuser is absolute
+ if (status == false && canAbsMove && currentFocuser && initialFocuserAbsPosition >= 0)
+ {
+ currentFocuser->moveAbs(initialFocuserAbsPosition);
+ appendLogText(i18n("Autofocus failed, moving back to initial focus position %1.", initialFocuserAbsPosition));
+
+ // If we're doing in sequence focusing using an absolute focuser, let's retry focusing starting from last known good position before we give up
+ if (inSequenceFocus && resetFocusIteration++ < MAXIMUM_RESET_ITERATIONS && resetFocus == false)
+ {
+ resetFocus = true;
+ // Reset focus frame in case the star in subframe was lost
+ resetFrame();
+ return;
+ }
+ }
+
+ resetFocusIteration = 0;
+
+ //emit autoFocusFinished(status, currentHFR);
+
+ if (status)
+ {
+ KNotification::event( QLatin1String( "FocusSuccessful" ) , i18n("Autofocus operation completed successfully"));
+ state = Ekos::FOCUS_COMPLETE;
+ }
+ else
+ {
+ KNotification::event( QLatin1String( "FocusFailed" ) , i18n("Autofocus operation failed with errors"));
+ state = Ekos::FOCUS_FAILED;
+ }
+
+ emit newStatus(state);
+}
+
+void Focus::checkAutoStarTimeout()
+{
+ //if (starSelected == false && inAutoFocus)
+ if (starCenter.isNull() && inAutoFocus)
+ {
+ appendLogText(i18n("No star was selected. Aborting..."));
+ initialFocuserAbsPosition=-1;
+ abort();
+ setAutoFocusResult(false);
+ }
+}
+
+void Focus::setAbsoluteFocusTicks()
+{
+ if (currentFocuser == NULL)
+ return;
+
+ if (currentFocuser->isConnected() == false)
+ {
+ appendLogText(i18n("Error: Lost connection to Focuser."));
+ return;
+ }
+
+ if (Options::focusLogging())
+ qDebug() << "Focus: Setting focus ticks to " << absTicksSpin->value();
+
+ currentFocuser->moveAbs(absTicksSpin->value());
+}
+
+void Focus::setActiveBinning(int bin)
+{
+ activeBin = bin + 1;
+}
+
+void Focus::setThreshold(double value)
+{
+ Options::setFocusThreshold(value);
+}
+
+void Focus::setFrames(int value)
+{
+ Options::setFocusFrames(value);
+}
+
+}
+
+
diff --git a/kstars/ekos/focus/focus.h b/kstars/ekos/focus/focus.h
new file mode 100644
index 0000000..065f3b1
--- /dev/null
+++ b/kstars/ekos/focus/focus.h
@@ -0,0 +1,483 @@
+/* Ekos Focus tool
+ Copyright (C) 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
+
+ This application is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef FOCUS_H
+#define FOCUS_H
+
+#include <QtDBus/QtDBus>
+
+#include "focus.h"
+#include "capture.h"
+
+#include "ui_focus.h"
+
+#include "indi/indistd.h"
+#include "indi/indifocuser.h"
+
+
+namespace Ekos
+{
+
+struct HFRPoint
+{
+ int pos;
+ double HFR;
+};
+
+/**
+ *@class Focus
+ *@short Supports manual focusing and auto focusing using relative and absolute INDI focusers.
+ *@author Jasem Mutlaq
+ *@version 1.1
+ */
+class Focus : public QWidget, public Ui::Focus
+{
+
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Focus")
+
+public:
+ Focus();
+ ~Focus();
+
+ typedef enum { FOCUS_NONE, FOCUS_IN, FOCUS_OUT } FocusDirection;
+ typedef enum { FOCUS_MANUAL, FOCUS_AUTO, FOCUS_LOOP } FocusType;
+
+ /** @defgroup FocusDBusInterface Ekos DBus Interface - Focus Module
+ * Ekos::Focus interface provides advanced scripting capabilities to perform manual and automatic focusing operations.
+ */
+
+ /*@{*/
+
+ /** DBUS interface function.
+ * select the CCD device from the available CCD drivers.
+ * @param device The CCD device name
+ * @return Returns true if CCD device is found and set, false otherwise.
+ */
+ Q_SCRIPTABLE bool setCCD(QString device);
+
+ /** DBUS interface function.
+ * select the focuser device from the available focuser drivers. The focuser device can be the same as the CCD driver if the focuser functionality was embedded within the driver.
+ * @param device The focuser device name
+ * @return Returns true if focuser device is found and set, false otherwise.
+ */
+ Q_SCRIPTABLE bool setFocuser(QString device);
+
+ /** DBUS interface function.
+ * select the filter device from the available filter drivers. The filter device can be the same as the CCD driver if the filter functionality was embedded within the driver.
+ * @param device The filter device name
+ * @return Returns true if filter device is found and set, false otherwise.
+ */
+ Q_SCRIPTABLE bool setFilter(QString device, int filterSlot);
+
+ /** DBUS interface function.
+ * @return Returns true if autofocus operation is complete. False otherwise.
+ */
+ Q_SCRIPTABLE bool isAutoFocusComplete() { return (inAutoFocus == false);}
+
+ /** DBUS interface function.
+ * @return Returns true if autofocus operation is successful. False otherwise.
+ */
+ Q_SCRIPTABLE bool isAutoFocusSuccessful() { return m_autoFocusSuccesful;}
+
+ /** DBUS interface function.
+ * @return Returns Half-Flux-Radius in pixels.
+ */
+ Q_SCRIPTABLE double getHFR() { return currentHFR; }
+
+ /** DBUS interface function.
+ * Set Focus mode (Manual or Auto)
+ * @param mode 0 for manual, any other value for auto.
+ */
+ Q_SCRIPTABLE bool setFocusMode(int mode);
+
+ /** DBUS interface function.
+ * Set CCD exposure value
+ * @param value exposure value in seconds.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setExposure(double value);
+
+ /** DBUS interface function.
+ * Set CCD binning
+ * @param binX horizontal binning
+ * @param binY vertical binning
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setBinning(int binX, int binY);
+
+ /** DBUS interface function.
+ * Set image filter to apply to the image after capture.
+ * @param value Image filter (Auto Stretch, High Contrast, Equalize, High Pass)
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setImageFilter(const QString & value);
+
+ /** DBUS interface function.
+ * Set Auto Focus options. The options must be set before starting the autofocus operation. If no options are set, the options loaded from the user configuration are used.
+ * @param enable If true, Ekos will attempt to automatically select the best focus star in the frame. If it fails to select a star, the user will be asked to select a star manually.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setAutoFocusStar(bool enable);
+
+
+ /** DBUS interface function.
+ * Set Auto Focus options. The options must be set before starting the autofocus operation. If no options are set, the options loaded from the user configuration are used.
+ * @param enable if true, Ekos will capture a subframe around the selected focus star. The subframe size is determined by the boxSize parameter.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setAutoFocusSubFrame(bool enable);
+
+ /** DBUS interface function.
+ * Set Autofocus parameters
+ * @param boxSize the box size around the focus star in pixels. The boxsize is used to subframe around the focus star.
+ * @param stepSize the initial step size to be commanded to the focuser. If the focuser is absolute, the step size is in ticks. For relative focusers, the focuser will be commanded to focus inward for stepSize milliseconds initially.
+ * @param maxTravel the maximum steps permitted before the autofocus operation aborts.
+ * @param tolerance Measure of how accurate the autofocus algorithm is. If the difference between the current HFR and minimum measured HFR is less than %tolerance after the focuser traversed both ends of the V-curve, then the focusing operation
+ * is deemed successful. Otherwise, the focusing operation will continue.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void setAutoFocusParameters(int boxSize, int stepSize, int maxTravel, double tolerance);
+
+ /** DBUS interface function.
+ * resetFrame Resets the CCD frame to its full native resolution.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void resetFrame();
+
+ /** @}*/
+
+ /**
+ * @brief Add CCD to the list of available CCD.
+ * @param newCCD pointer to CCD device.
+ */
+ void addCCD(ISD::GDInterface *newCCD);
+
+ /**
+ * @brief addFocuser Add focuser to the list of available focusers.
+ * @param newFocuser pointer to focuser device.
+ */
+ void addFocuser(ISD::GDInterface *newFocuser);
+
+ /**
+ * @brief addFilter Add filter to the list of available filters.
+ * @param newFilter pointer to filter device.
+ */
+ void addFilter(ISD::GDInterface *newFilter);
+
+
+ void clearLog();
+ QString getLogText() { return logText.join("\n"); }
+
+public slots:
+
+ /** \addtogroup FocusDBusInterface
+ * @{
+ */
+
+ /* Focus */
+ /** DBUS interface function.
+ * Start the autofocus operation.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void start();
+
+ /** DBUS interface function.
+ * Abort the autofocus operation.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void abort();
+
+ /** DBUS interface function.
+ * Capture a focus frame.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void capture();
+
+ /** DBUS interface function.
+ * Focus inward
+ * @param ms If set, focus inward for ms ticks (Absolute Focuser), or ms milliseconds (Relative Focuser). If not set, it will use the value specified in the options.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void FocusIn(int ms=-1);
+
+ /** DBUS interface function.
+ * Focus outward
+ * @param ms If set, focus outward for ms ticks (Absolute Focuser), or ms milliseconds (Relative Focuser). If not set, it will use the value specified in the options.
+ */
+ Q_SCRIPTABLE Q_NOREPLY void FocusOut(int ms=-1);
+
+ /** @}*/
+
+ /**
+ * @brief startFraming Begins continious capture of the CCD and calculates HFR every frame.
+ */
+ void startFraming();
+
+ /**
+ * @brief checkStopFocus Perform checks before stopping the autofocus operation. Some checks are necessary for in-sequence focusing.
+ */
+ void checkStopFocus();
+
+ /**
+ * @brief Check CCD and make sure information is updated accordingly. This simply calls syncCCDInfo for the current CCD.
+ * @param CCDNum By default, we check the already selected CCD in the dropdown menu. If CCDNum is specified, the check is made against this specific CCD in the dropdown menu.
+ * CCDNum is the index of the CCD in the dropdown menu.
+ */
+ void checkCCD(int CCDNum=-1);
+
+ /**
+ * @brief syncCCDInfo Read current CCD information and update settings accordingly.
+ */
+ void syncCCDInfo();
+
+ /**
+ * @brief Check Focuser and make sure information is updated accordingly.
+ * @param FocuserNum By default, we check the already selected focuser in the dropdown menu. If FocuserNum is specified, the check is made against this specific focuser in the dropdown menu.
+ * FocuserNum is the index of the focuser in the dropdown menu.
+ */
+ void checkFocuser(int FocuserNum=-1);
+
+ /**
+ * @brief Check Filter and make sure information is updated accordingly.
+ * @param filterNum By default, we check the already selected filter in the dropdown menu. If filterNum is specified, the check is made against this specific filter in the dropdown menu.
+ * filterNum is the index of the filter in the dropdown menu.
+ */
+ void checkFilter(int filterNum=-1);
+
+
+ /**
+ * @brief clearDataPoints Remove all data points from HFR plots
+ */
+ void clearDataPoints();
+
+ /**
+ * @brief focusStarSelected The user selected a focus star, save its coordinates and subframe it if subframing is enabled.
+ * @param x X coordinate
+ * @param y Y coordinate
+ */
+ void focusStarSelected(int x, int y);
+
+ /**
+ * @brief newFITS A new FITS blob is received by the CCD driver.
+ * @param bp pointer to blob data
+ */
+ void newFITS(IBLOB *bp);
+
+ /**
+ * @brief processFocusNumber Read focus number properties of interest as they arrive from the focuser driver and process them accordingly.
+ * @param nvp pointer to updated focuser number property.
+ */
+ void processFocusNumber(INumberVectorProperty *nvp);
+
+ /**
+ * @brief checkFocus Given the minimum required HFR, check focus and calculate HFR. If current HFR exceeds required HFR, start autofocus process, otherwise do nothing.
+ * @param requiredHFR Minimum HFR to trigger autofocus process.
+ */
+ void checkFocus(double requiredHFR);
+
+ /**
+ * @brief setFocusStatus Upon completion of the focusing process, set its status (fail or pass) and reset focus process to clean state.
+ * @param status If true, the focus process finished successfully. Otherwise, it failed.
+ */
+ void setAutoFocusResult(bool status);
+
+ /**
+ * @brief filterChangeWarning Warn the user it is not a good idea to apply image filter in the filter process as they can skew the HFR calculations.
+ * @param index Index of image filter selected by the user.
+ */
+ void filterChangeWarning(int index);
+
+ // Log
+ void appendLogText(const QString &);
+
+private slots:
+ /**
+ * @brief filterLockToggled Process filter locking/unlocking. Filter lock causes the autofocus process to use the selected filter whenever it runs.
+ */
+ void filterLockToggled(bool);
+
+ /**
+ * @brief updateFilterPos If filter locking is checked, set the locked filter to the current filter position
+ * @param index current filter position
+ */
+ void updateFilterPos(int index);
+
+ /**
+ * @brief toggleAutofocus Process switching between manual and automated focus.
+ * @param enable If true, auto focus is selected, if false, manual mode is selected.
+ */
+ void toggleAutofocus(bool enable);
+
+ /**
+ * @brief toggleSubframe Process enabling and disabling subframing.
+ * @param enable If true, subframing is enabled. If false, subframing is disabled. Even if subframing is enabled, it must be supported by the CCD driver.
+ */
+ void toggleSubframe(bool enable);
+
+ void checkAutoStarTimeout();
+
+ void setAbsoluteFocusTicks();
+
+ void setActiveBinning(int bin);
+
+ void setDefaultCCD(QString ccd);
+
+ void updateBoxSize(int value);
+
+ void setThreshold(double value);
+
+ void setFrames(int value);
+
+ void setCaptureComplete();
+
+signals:
+ void newLog();
+ //void autoFocusFinished(bool status, double finalHFR);
+ void suspendGuiding(bool suspend);
+ void filterLockUpdated(ISD::GDInterface *filter, int lockedIndex);
+ void newStatus(Ekos::FocusState state);
+ void newStarPixmap(QPixmap &);
+ void newProfilePixmap(QPixmap &);
+ void newHFR(double hfr);
+
+private:
+ void drawHFRPlot();
+ void drawProfilePlot();
+ void getAbsFocusPosition();
+ void autoFocusAbs();
+ void autoFocusRel();
+ void resetButtons();
+
+ // Devices needed for Focus operation
+ ISD::Focuser *currentFocuser;
+ ISD::CCD *currentCCD;
+
+ // Optional device filter
+ ISD::GDInterface *currentFilter;
+ // Current filter position
+ int currentFilterIndex;
+ // True if we need to change filter position and wait for result before continuing capture
+ bool filterPositionPending;
+
+ // List of Focusers
+ QList<ISD::Focuser*> Focusers;
+ // List of CCDs
+ QList<ISD::CCD *> CCDs;
+ // They're generic GDInterface because they could be either ISD::CCD or ISD::Filter
+ QList<ISD::GDInterface *> Filters;
+
+ // As the name implies
+ FocusDirection lastFocusDirection;
+ // What type of focusing are we doing right now?
+ FocusType focusType;
+
+ /*********************
+ * HFR Club variables
+ *********************/
+
+ // Current HFR value just fetched from FITS file
+ double currentHFR;
+ // Last HFR value recorded
+ double lastHFR;
+ // If (currentHFR > deltaHFR) we start the autofocus process.
+ double minimumRequiredHFR;
+ // Maximum HFR recorded
+ double maxHFR;
+ // Is HFR increasing? We're going away from the sweet spot! If HFRInc=1, we re-capture just to make sure HFR calculations are correct, if HFRInc > 1, we switch directions
+ int HFRInc;
+ // If HFR decreasing? Well, good job. Once HFR start decreasing, we can start calculating HFR slope and estimating our next move.
+ int HFRDec;
+ // How many frames have we captured thus far? Do we need to average them?
+ uint8_t frameNum;
+
+ /****************************
+ * Absolute position focusers
+ ****************************/
+ // Absolute focus position
+ double currentPosition;
+ // What was our position before we started the focus process?
+ int initialFocuserAbsPosition;
+ // Pulse duration in ms for relative focusers that only support timers, or the number of ticks in a relative or absolute focuser
+ int pulseDuration;
+ // Does the focuser support absolute motion?
+ bool canAbsMove;
+ // Does the focuser support relative motion?
+ bool canRelMove;
+ // Range of motion for our lovely absolute focuser
+ double absMotionMax, absMotionMin;
+ // How many iterations have we completed now in our absolute autofocus algorithm? We can't go forever
+ int absIterations;
+
+ /****************************
+ * Misc. variables
+ ****************************/
+
+ // Are we in the process of capturing an image?
+ bool captureInProgress;
+ // Was the frame modified by us? Better keep track since we need to return it to its previous state once we are done with the focus operation.
+ //bool frameModified;
+ // Was the modified frame subFramed?
+ bool subFramed;
+ // If the autofocus process fails, let's not ruin the capture session probably taking place in the next tab. Instead, we should restart it and try again, but we keep count until we hit MAXIMUM_RESET_ITERATIONS
+ // and then we truly give up.
+ int resetFocusIteration;
+ // Which filter must we use once the autofocus process kicks in?
+ int lockedFilterIndex;
+ // Keep track of what we're doing right now
+ bool inAutoFocus, inFocusLoop, inSequenceFocus, m_autoFocusSuccesful, resetFocus;
+ // Did we reverse direction?
+ bool reverseDir;
+ // Did the user or the auto selection process finish selecting our focus star?
+ //bool starSelected;
+ // Target frame dimensions
+ //int fx,fy,fw,fh;
+ // Origianl frame dimensions
+ //int orig_x, orig_y, orig_w, orig_h;
+ // If HFR=-1 which means no stars detected, we need to decide how many times should the re-capture process take place before we give up or reverse direction.
+ int noStarCount;
+ // Track which upload mode the CCD is set to. If set to UPLOAD_LOCAL, then we need to switch it to UPLOAD_CLIENT in order to do focusing, and then switch it back to UPLOAD_LOCAL
+ ISD::CCD::UploadMode rememberUploadMode;
+ // Previous binning setting
+ int activeBin;
+ // HFR values for captured frames before averages
+ double HFRFrames[5];
+
+ QStringList logText;
+ ITextVectorProperty *filterName;
+ INumberVectorProperty *filterSlot;
+
+ /****************************
+ * Plot variables
+ ****************************/
+
+ // Plot minimum and maximum positions
+ int minPos, maxPos;
+ // List of V curve plot points
+ // Custom Plot object
+ QCustomPlot *customPlot;
+ // V-Curve graph
+ QCPGraph *v_graph;
+
+ // Last gaussian fit values
+ QVector<double> firstGausIndexes, lastGausIndexes;
+ QVector<double> firstGausFrequencies, lastGausFrequencies;
+ QCPGraph *currentGaus, *firstGaus, *lastGaus;
+
+ QVector<double> hfr_position, hfr_value;
+
+ // Pixmaps
+ QPixmap starPixmap;
+ QPixmap profilePixmap;
+
+ // State
+ Ekos::FocusState state;
+
+ // FITS Scale
+ FITSScale defaultScale;
+
+ // CCD Chip frame settings
+ QMap<ISD::CCDChip *, QVariantMap> frameSettings;
+
+ // Selected star coordinates
+ QVector3D starCenter;
+};
+
+}
+
+#endif // Focus_H
diff --git a/kstars/ekos/focus/focus.ui b/kstars/ekos/focus/focus.ui
new file mode 100644
index 0000000..91f8bf4
--- /dev/null
+++ b/kstars/ekos/focus/focus.ui
@@ -0,0 +1,938 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Focus</class>
+ <widget class="QWidget" name="Focus">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>615</width>
+ <height>385</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QGroupBox" name="ccdGroup">
+ <property name="title">
+ <string>CCD &amp;&amp; Filter Wheel</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="FilterCaptureCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="FilterPosLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Number of images to capture</string>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="text">
+ <string>Filter #:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="FilterPosCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="lockFilterCheck">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>If locked, the focus process will always use the specified filter when performing autofocus.
+Otherwise, the autofocus process will utilize whatever filter currently set by the driver.</string>
+ </property>
+ <property name="text">
+ <string>Lock</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="toolTip">
+ <string>Apply filter to image after capture to enhance it</string>
+ </property>
+ <property name="text">
+ <string>Effect:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="filterCombo">
+ <item>
+ <property name="text">
+ <string>--</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QComboBox" name="ISOCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="FilterCaptureLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Filter Wheel</string>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="text">
+ <string>FW:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="CCDCaptureCombo"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Exposure:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QDoubleSpinBox" name="exposureIN">
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>0.001000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>300.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="value">
+ <double>0.500000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>secs</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Bin:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="binningCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="ISOLabel">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>ISO:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QPushButton" name="resetFrameB">
+ <property name="toolTip">
+ <string>Reset focus subframe to full capture</string>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel1_6">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="text">
+ <string>CCD:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Frame:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="controlGroup">
+ <property name="title">
+ <string>Focuser</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Focuser:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="focuserCombo"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="focusInB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Focus In</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QPushButton" name="focusOutB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Focus Out</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Ticks:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="absTicksSpin">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="setAbsTicksB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Set</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="captureB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Capture</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Mode:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="manualModeR">
+ <property name="text">
+ <string>&amp;Manual</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="AutoModeR">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>A&amp;uto</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="startFocusB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Start Focus</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="stopFocusB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="startLoopB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Start Framing</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ <zorder>focusInB</zorder>
+ <zorder>focusOutB</zorder>
+ <zorder>label_15</zorder>
+ <zorder>focusInB</zorder>
+ <zorder>focusOutB</zorder>
+ <zorder>captureB</zorder>
+ <zorder>horizontalSpacer_3</zorder>
+ <zorder>horizontalSpacer_4</zorder>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="2,1,0">
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>V-Curve</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QCustomPlot" name="HFRPlot" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>100</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>HFR:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="HFROut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>pixels</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>13</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="clearDataB">
+ <property name="text">
+ <string>Clear Data</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="profileGroup">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Relative Profile</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QCustomPlot" name="profilePlot" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Autofocus Options</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="kcfg_autoSelectStar">
+ <property name="toolTip">
+ <string>Automatically select the best focus star from the image</string>
+ </property>
+ <property name="text">
+ <string>Auto Select Star</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="kcfg_subFrame">
+ <property name="toolTip">
+ <string>Subframe around the focus star during the autofocus procedure</string>
+ </property>
+ <property name="text">
+ <string>Sub Frame</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="darkFrameCheck">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>Dark Frame</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="suspendGuideCheck">
+ <property name="toolTip">
+ <string>Suspend Guiding while autofocus in progress</string>
+ </property>
+ <property name="text">
+ <string>Suspend Guiding</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="whatsThis">
+ <string>Delay between two consequent focus images</string>
+ </property>
+ <property name="text">
+ <string>Box Size:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="focusBoxSize">
+ <property name="minimum">
+ <number>16</number>
+ </property>
+ <property name="maximum">
+ <number>256</number>
+ </property>
+ <property name="singleStep">
+ <number>16</number>
+ </property>
+ <property name="value">
+ <number>64</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>pixels</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="toolTip">
+ <string>Maximum travel in ticks before the autofocus process aborts</string>
+ </property>
+ <property name="text">
+ <string>Max Travel:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="maxTravelIN">
+ <property name="minimum">
+ <double>10.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>50000.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>100.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>10000.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>ticks</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="toolTip">
+ <string>&lt;b&gt;Initial&lt;/b&gt; step size in ticks to cause a noticeable change in HFR value. For timer based focuser, it is the initial time in milliseconds to move the focuser inward or outward</string>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="text">
+ <string>Step:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="stepIN">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>250</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>ticks</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="toolTip">
+ <string>Decrease value to narrow optimal focus point solution radius. Increase to expand solution radius</string>
+ </property>
+ <property name="text">
+ <string>Tolerance:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDoubleSpinBox" name="toleranceIN">
+ <property name="minimum">
+ <double>0.010000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>20.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>%</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_19">
+ <property name="toolTip">
+ <string>Increase to restrict the centroid to bright cores. Decrease to enclose fuzzy stars.</string>
+ </property>
+ <property name="text">
+ <string>Threshold:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QDoubleSpinBox" name="thresholdSpin">
+ <property name="minimum">
+ <double>90.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>500.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>10.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>150.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>%</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_21">
+ <property name="toolTip">
+ <string>Number of frames to average</string>
+ </property>
+ <property name="text">
+ <string>Frames:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QSpinBox" name="focusFramesSpin">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ <zorder>groupBox_2</zorder>
+ <zorder>horizontalSpacer_5</zorder>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QCustomPlot</class>
+ <extends>QWidget</extends>
+ <header>auxiliary/qcustomplot.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>