diff --git a/kstars/ekos/auxiliary/opslogs.ui b/kstars/ekos/auxiliary/opslogs.ui
index 968b90b53..76f5ed444 100644
--- a/kstars/ekos/auxiliary/opslogs.ui
+++ b/kstars/ekos/auxiliary/opslogs.ui
@@ -1,520 +1,531 @@
OpsLogs005014003333348484848:/icons/AlignWarning.svgtrue<html><head/><body><p>Enable Logs to diagnose issues with Ekos and INDI. Only enable the necessary logs to help in diagnosing specific problems. Logs consume resources and may slow the system down. Turn off logs when no longer used.</p></body></html>trueLogsDisable all logging output&DisablefalseverbosityButtonGroupQt::HorizontalWeatherdriversGroupVerbosity:FocuserdriversGroupLog Ekos Scheduler module activitySchedulermodulesGroupLog INDI devices activityINDImodulesGroupMountdriversGroupLog Ekos Focus module activityFocusmodulesGroupRotatordriversGroupEnable verbose debug output&VerboseverbosityButtonGroupLog Ekos Guide module activityGuidemodulesGroupCCDdriversGroupGPSdriversGroupOutput:Have problems with INDI drivers? Turn on logging for drivers that exhibit issues.DriversEnable regular debug outputReg&ulartrueverbosityButtonGroupLog FITS processing activityFITSmodulesGroupLog Ekos Alignment module activityAlignmentmodulesGroupDomedriversGroupAdaptive OpticsAOdriversGroupDetectordriversGroupLog Ekos Capture module activityCapturemodulesGroupHave problem with Ekos? Turn on logging for Ekos modules that exhibit issues.EkosLog debug messages to default output device used by the platform (e.g. Standard Error)Defaul&tfalseLog Ekos Mount module activityMountmodulesGroupFilter WheeldriversGroupQt::HorizontalAuxiliarydriversGroupLog output to log fileFile<html><head/><body><p>Log Ekos Observatory module activity</p></body></html>ObservatorymodulesGroupDisplay INDI status messages in the status barDisplay incoming INDI status messages in KStars status barDisplay incoming INDI status messages in KStars status barINDI messages in status &bartrue
+
+
+
+ <html><head/><body><p>Save autofocus frames. Only enable to troubleshoot autofocus by examining frames. This can consume a lot of storage space.</p></body></html>
+
+
+ Save Focus Images
+
+
+ Qt::Horizontal4020font-weight:bold;Open Logs Directory22222222Clear all logs..2222
+ falsefalse
diff --git a/kstars/ekos/focus/focus.cpp b/kstars/ekos/focus/focus.cpp
index 8653a2101..eb4964ef3 100644
--- a/kstars/ekos/focus/focus.cpp
+++ b/kstars/ekos/focus/focus.cpp
@@ -1,3394 +1,3394 @@
/* Ekos
Copyright (C) 2012 Jasem Mutlaq
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 "focusadaptor.h"
#include "kstars.h"
#include "kstarsdata.h"
#include "Options.h"
#include "auxiliary/kspaths.h"
#include "auxiliary/ksmessagebox.h"
#include "ekos/manager.h"
#include "ekos/auxiliary/darklibrary.h"
#include "fitsviewer/fitsdata.h"
#include "fitsviewer/fitstab.h"
#include "fitsviewer/fitsview.h"
#include "indi/indifilter.h"
#include "ksnotification.h"
#include
#include
#include
#include
#include
#define FOCUS_TIMEOUT_THRESHOLD 120000
#define MAXIMUM_ABS_ITERATIONS 30
#define MAXIMUM_RESET_ITERATIONS 2
#define AUTO_STAR_TIMEOUT 45000
#define MINIMUM_PULSE_TIMER 32
#define MAX_RECAPTURE_RETRIES 3
#define MINIMUM_POLY_SOLUTIONS 2
namespace Ekos
{
Focus::Focus()
{
// #1 Set the UI
setupUi(this);
// #2 Register DBus
qRegisterMetaType("Ekos::FocusState");
qDBusRegisterMetaType();
new FocusAdaptor(this);
QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Focus", this);
// #3 Init connections
initConnections();
// #4 Init Plots
initPlots();
// #5 Init View
initView();
// #6 Reset all buttons to default states
resetButtons();
// #7 Image Effects
for (auto &filter : FITSViewer::filterTypes)
filterCombo->addItem(filter);
filterCombo->setCurrentIndex(Options::focusEffect());
defaultScale = static_cast(Options::focusEffect());
connect(filterCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::filterChangeWarning);
// #8 Load All settings
loadSettings();
// #9 Init Setting Connection now
initSettingsConnections();
//Note: This is to prevent a button from being called the default button
//and then executing when the user hits the enter key such as when on a Text Box
QList qButtons = findChildren();
for (auto &button : qButtons)
button->setAutoDefault(false);
appendLogText(i18n("Idle."));
}
Focus::~Focus()
{
if (focusingWidget->parent() == nullptr)
toggleFocusingWidgetFullScreen();
}
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);
qCDebug(KSTARS_EKOS_FOCUS) << "Frame is reset. X:" << x << "Y:" << y << "W:" << w << "H:" << h << "binX:" << 1 << "binY:" << 1;
QVariantMap settings;
settings["x"] = x;
settings["y"] = y;
settings["w"] = w;
settings["h"] = h;
settings["binx"] = 1;
settings["biny"] = 1;
frameSettings[targetChip] = settings;
starSelected = false;
starCenter = QVector3D();
subFramed = false;
focusView->setTrackingBox(QRect());
}
}
}
bool Focus::setCamera(const QString &device)
{
for (int i = 0; i < CCDCaptureCombo->count(); i++)
if (device == CCDCaptureCombo->itemText(i))
{
CCDCaptureCombo->setCurrentIndex(i);
checkCCD(i);
return true;
}
return false;
}
QString Focus::camera()
{
if (currentCCD)
return currentCCD->getDeviceName();
return QString();
}
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 && targetChip->isCapturing())
return;
for (ISD::CCD *oneCCD : CCDs)
{
if (oneCCD == currentCCD)
continue;
if (captureInProgress == false)
oneCCD->disconnect(this);
}
if (targetChip)
{
targetChip->setImageView(focusView, FITS_FOCUS);
binningCombo->setEnabled(targetChip->canBin());
useSubFrame->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));
activeBin = Options::focusXBin();
binningCombo->setCurrentIndex(activeBin - 1);
}
else
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());
}
connect(currentCCD, &ISD::CCD::videoStreamToggled, this, &Ekos::Focus::setVideoStreamEnabled, Qt::UniqueConnection);
liveVideoB->setEnabled(currentCCD->hasVideoStream());
if (currentCCD->hasVideoStream())
setVideoStreamEnabled(currentCCD->isStreamingEnabled());
else
liveVideoB->setIcon(QIcon::fromTheme("camera-off"));
bool hasGain = currentCCD->hasGain();
gainLabel->setEnabled(hasGain);
gainIN->setEnabled(hasGain && currentCCD->getGainPermission() != IP_RO);
if (hasGain)
{
double gain = 0, min = 0, max = 0, step = 1;
currentCCD->getGainMinMaxStep(&min, &max, &step);
if (currentCCD->getGain(&gain))
{
gainIN->setMinimum(min);
gainIN->setMaximum(max);
if (step > 0)
gainIN->setSingleStep(step);
double defaultGain = Options::focusGain();
if (defaultGain > 0)
gainIN->setValue(defaultGain);
else
gainIN->setValue(gain);
}
}
else
gainIN->clear();
}
}
syncCCDInfo();
}
void Focus::syncCCDInfo()
{
if (currentCCD == nullptr)
return;
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
useSubFrame->setEnabled(targetChip->canSubframe());
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)
{
int minX, maxX, minY, maxY, minW, maxW, minH, maxH;
targetChip->getFrameMinMax(&minX, &maxX, &minY, &maxY, &minW, &maxW, &minH, &maxH);
QVariantMap settings;
settings["x"] = useSubFrame->isChecked() ? x : minX;
settings["y"] = useSubFrame->isChecked() ? y : minY;
settings["w"] = useSubFrame->isChecked() ? w : maxW;
settings["h"] = useSubFrame->isChecked() ? h : maxH;
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);
FilterDevicesCombo->setEnabled(true);
FilterPosLabel->setEnabled(true);
FilterPosCombo->setEnabled(true);
filterManagerB->setEnabled(true);
FilterDevicesCombo->addItem(newFilter->getDeviceName());
Filters.append(static_cast(newFilter));
checkFilter(1);
FilterDevicesCombo->setCurrentIndex(1);
if (Options::defaultFocusFilterWheel().isEmpty() == false)
FilterDevicesCombo->setCurrentText(Options::defaultFocusFilterWheel());
}
bool Focus::setFilterWheel(const QString &device)
{
bool deviceFound = false;
for (int i = 1; i < FilterDevicesCombo->count(); i++)
if (device == FilterDevicesCombo->itemText(i))
{
checkFilter(i);
deviceFound = true;
break;
}
if (deviceFound == false)
return false;
return true;
}
QString Focus::filterWheel()
{
if (FilterDevicesCombo->currentIndex() >= 1)
return FilterDevicesCombo->currentText();
return QString();
}
bool Focus::setFilter(const QString &filter)
{
if (FilterDevicesCombo->currentIndex() >= 1)
{
FilterPosCombo->setCurrentText(filter);
return true;
}
return false;
}
QString Focus::filter()
{
return FilterPosCombo->currentText();
}
void Focus::checkFilter(int filterNum)
{
if (filterNum == -1)
{
filterNum = FilterDevicesCombo->currentIndex();
if (filterNum == -1)
return;
}
// "--" is no filter
if (filterNum == 0)
{
currentFilter = nullptr;
currentFilterPosition = -1;
FilterPosCombo->clear();
return;
}
if (filterNum <= Filters.count())
currentFilter = Filters.at(filterNum - 1);
//Options::setDefaultFocusFilterWheel(currentFilter->getDeviceName());
filterManager->setCurrentFilterWheel(currentFilter);
FilterPosCombo->clear();
FilterPosCombo->addItems(filterManager->getFilterLabels());
currentFilterPosition = filterManager->getFilterPosition();
FilterPosCombo->setCurrentIndex(currentFilterPosition - 1);
//Options::setDefaultFocusFilterWheelFilter(FilterPosCombo->currentText());
exposureIN->setValue(filterManager->getFilterExposure());
}
void Focus::addFocuser(ISD::GDInterface *newFocuser)
{
ISD::Focuser *oneFocuser = static_cast(newFocuser);
if (Focusers.contains(oneFocuser))
return;
focuserCombo->addItem(oneFocuser->getDeviceName());
Focusers.append(oneFocuser);
currentFocuser = oneFocuser;
checkFocuser();
}
bool Focus::setFocuser(const QString &device)
{
for (int i = 0; i < focuserCombo->count(); i++)
if (device == focuserCombo->itemText(i))
{
focuserCombo->setCurrentIndex(i);
checkFocuser(i);
return true;
}
return false;
}
QString Focus::focuser()
{
if (currentFocuser)
return currentFocuser->getDeviceName();
return QString();
}
void Focus::checkFocuser(int FocuserNum)
{
if (FocuserNum == -1)
FocuserNum = focuserCombo->currentIndex();
if (FocuserNum == -1)
{
currentFocuser = nullptr;
return;
}
if (FocuserNum < Focusers.count())
currentFocuser = Focusers.at(FocuserNum);
filterManager->setFocusReady(currentFocuser->isConnected());
// Disconnect all focusers
for (auto &oneFocuser : Focusers)
{
disconnect(oneFocuser, &ISD::GDInterface::numberUpdated, this, &Ekos::Focus::processFocusNumber);
}
canAbsMove = currentFocuser->canAbsMove();
if (canAbsMove)
{
getAbsFocusPosition();
absTicksSpin->setEnabled(true);
absTicksLabel->setEnabled(true);
startGotoB->setEnabled(true);
absTicksSpin->setValue(currentPosition);
}
else
{
absTicksSpin->setEnabled(false);
absTicksLabel->setEnabled(false);
startGotoB->setEnabled(false);
}
canRelMove = currentFocuser->canRelMove();
// In case we have a purely relative focuser, we pretend
// it is an absolute focuser with initial point set at 50,000
// This is done we can use the same algorithm used for absolute focuser
if (canAbsMove == false && canRelMove == true)
{
currentPosition = 50000;
absMotionMax = 100000;
absMotionMin = 0;
}
canTimerMove = currentFocuser->canTimerMove();
focusType = (canRelMove || canAbsMove || canTimerMove) ? FOCUS_AUTO : FOCUS_MANUAL;
bool hasBacklash = currentFocuser->hasBacklash();
focusBacklashSpin->setEnabled(hasBacklash);
focusBacklashSpin->disconnect(this);
if (hasBacklash)
{
double min = 0, max = 0, step = 0;
currentFocuser->getMinMaxStep("FOCUS_BACKLASH_STEPS", "FOCUS_BACKLASH_VALUE", &min, &max, &step);
focusBacklashSpin->setMinimum(min);
focusBacklashSpin->setMaximum(max);
focusBacklashSpin->setSingleStep(step);
focusBacklashSpin->setValue(currentFocuser->getBacklash());
connect(focusBacklashSpin, static_cast(&QSpinBox::valueChanged), this, [this](int value)
{
if (currentFocuser)
currentFocuser->setBacklash(value);
});
}
else
{
focusBacklashSpin->setValue(0);
}
connect(currentFocuser, &ISD::GDInterface::numberUpdated, this, &Ekos::Focus::processFocusNumber, Qt::UniqueConnection);
//connect(currentFocuser, SIGNAL(propertyDefined(INDI::Property*)), this, &Ekos::Focus::(registerFocusProperty(INDI::Property*)), Qt::UniqueConnection);
resetButtons();
//if (!inAutoFocus && !inFocusLoop && !captureInProgress && !inSequenceFocus)
// emit autoFocusFinished(true, -1);
}
void Focus::addCCD(ISD::GDInterface *newCCD)
{
if (CCDs.contains(static_cast(newCCD)))
return;
CCDs.append(static_cast(newCCD));
CCDCaptureCombo->addItem(newCCD->getDeviceName());
checkCCD();
}
void Focus::getAbsFocusPosition()
{
if (!canAbsMove)
return;
INumberVectorProperty *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);
maxTravelIN->setMinimum(absMove->np[0].min);
maxTravelIN->setMaximum(absMove->np[0].max);
absTicksLabel->setText(QString::number(static_cast(currentPosition)));
stepIN->setMaximum(absMove->np[0].max / 2);
//absTicksSpin->setValue(currentPosition);
}
}
void Focus::start()
{
if (currentCCD == nullptr)
{
appendLogText(i18n("No CCD connected."));
return;
}
lastFocusDirection = FOCUS_NONE;
polySolutionFound = 0;
waitStarSelectTimer.stop();
starsHFR.clear();
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;
HFRFrames.clear();
resetButtons();
reverseDir = false;
/*if (fw > 0 && fh > 0)
starSelected= true;
else
starSelected= false;*/
clearDataPoints();
if (firstGaus)
{
profilePlot->removeGraph(firstGaus);
firstGaus = nullptr;
}
// Options::setFocusTicks(stepIN->value());
// Options::setFocusTolerance(toleranceIN->value());
// //Options::setFocusExposure(exposureIN->value());
// Options::setFocusMaxTravel(maxTravelIN->value());
// Options::setFocusBoxSize(focusBoxSize->value());
// Options::setFocusSubFrame(useSubFrame->isChecked());
// Options::setFocusAutoStarEnabled(useAutoStar->isChecked());
// Options::setSuspendGuiding(suspendGuideCheck->isChecked());
// Options::setUseFocusDarkFrame(darkFrameCheck->isChecked());
// Options::setFocusFramesCount(focusFramesSpin->value());
// Options::setFocusUseFullField(useFullField->isChecked());
qCDebug(KSTARS_EKOS_FOCUS) << "Starting focus with box size: " << focusBoxSize->value()
<< " Subframe: " << ( useSubFrame->isChecked() ? "yes" : "no" )
<< " Autostar: " << ( useAutoStar->isChecked() ? "yes" : "no" )
<< " Full frame: " << ( useFullField->isChecked() ? "yes" : "no " )
<< " [" << fullFieldInnerRing->value() << "%," << fullFieldOuterRing->value() << "%]"
<< " Step Size: " << stepIN->value() << " Threshold: " << thresholdSpin->value()
<< " Tolerance: " << toleranceIN->value()
<< " Frames: " << 1 /*focusFramesSpin->value()*/ << " Maximum Travel: " << maxTravelIN->value();
if (useAutoStar->isChecked())
appendLogText(i18n("Autofocus in progress..."));
else
appendLogText(i18n("Please wait until image capture is complete..."));
if (suspendGuideCheck->isChecked())
{
m_GuidingSuspended = true;
emit suspendGuiding();
}
//emit statusUpdated(true);
state = Ekos::FOCUS_PROGRESS;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
// Denoise with median filter
//defaultScale = FITS_MEDIAN;
KSNotification::event(QLatin1String("FocusStarted"), i18n("Autofocus operation started"));
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()
{
stop(true);
}
void Focus::stop(bool aborted)
{
qCDebug(KSTARS_EKOS_FOCUS) << "Stopppig Focus";
captureTimeout.stop();
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
inAutoFocus = false;
inFocusLoop = false;
// Why starSelected is set to false below? We should retain star selection status under:
// 1. Autostar is off, or
// 2. Toggle subframe, or
// 3. Reset frame
// 4. Manual motion?
//starSelected = false;
polySolutionFound = 0;
captureInProgress = false;
minimumRequiredHFR = -1;
noStarCount = 0;
HFRFrames.clear();
//maxHFR=1;
disconnect(currentCCD, &ISD::CCD::BLOBUpdated, this, &Ekos::Focus::newFITS);
if (rememberUploadMode != currentCCD->getUploadMode())
currentCCD->setUploadMode(rememberUploadMode);
if (rememberCCDExposureLooping)
currentCCD->setExposureLoopingEnabled(true);
targetChip->abortExposure();
resetButtons();
absIterations = 0;
HFRInc = 0;
reverseDir = false;
//emit statusUpdated(false);
if (aborted)
{
state = Ekos::FOCUS_ABORTED;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
}
}
void Focus::capture()
{
captureTimeout.stop();
if (captureInProgress)
{
qCWarning(KSTARS_EKOS_FOCUS) << "Capture called while already in progress. Capture is ignored.";
return;
}
if (currentCCD == nullptr)
{
appendLogText(i18n("No CCD connected."));
return;
}
waitStarSelectTimer.stop();
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 (currentCCD->isBLOBEnabled() == false)
{
currentCCD->setBLOBEnabled(true);
}
if (currentFilter != nullptr && FilterPosCombo->currentIndex() != -1)
{
if (currentFilter->isConnected() == false)
{
appendLogText(i18n("Error: Lost connection to filter wheel."));
return;
}
int targetPosition = FilterPosCombo->currentIndex() + 1;
QString lockedFilter = filterManager->getFilterLock(FilterPosCombo->currentText());
// We change filter if:
// 1. Target position is not equal to current position.
// 2. Locked filter of CURRENT filter is a different filter.
if (lockedFilter != "--" && lockedFilter != FilterPosCombo->currentText())
{
int lockedFilterIndex = FilterPosCombo->findText(lockedFilter);
if (lockedFilterIndex >= 0)
{
// Go back to this filter one we are done
fallbackFilterPending = true;
fallbackFilterPosition = targetPosition;
targetPosition = lockedFilterIndex + 1;
}
}
filterPositionPending = (targetPosition != currentFilterPosition);
// If either the target position is not equal to the current position, OR
if (filterPositionPending)
{
// Apply all policies except autofocus since we are already in autofocus module doh.
filterManager->setFilterPosition(targetPosition, static_cast(FilterManager::CHANGE_POLICY | FilterManager::OFFSET_POLICY));
return;
}
}
if (currentCCD->getUploadMode() == ISD::CCD::UPLOAD_LOCAL)
{
rememberUploadMode = ISD::CCD::UPLOAD_LOCAL;
currentCCD->setUploadMode(ISD::CCD::UPLOAD_CLIENT);
}
rememberCCDExposureLooping = currentCCD->isLooping();
if (rememberCCDExposureLooping)
currentCCD->setExposureLoopingEnabled(false);
currentCCD->setTransformFormat(ISD::CCD::FORMAT_FITS);
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());
if (gainIN->isEnabled())
currentCCD->setGain(gainIN->value());
connect(currentCCD, &ISD::CCD::BLOBUpdated, this, &Ekos::Focus::newFITS);
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;
focusView->setBaseSize(focusingWidget->size());
// Timeout is exposure duration + timeout threshold in seconds
captureTimeout.start(seqExpose * 1000 + FOCUS_TIMEOUT_THRESHOLD);
targetChip->capture(seqExpose);
if (inFocusLoop == false)
{
appendLogText(i18n("Capturing image..."));
if (inAutoFocus == false)
{
captureB->setEnabled(false);
stopFocusB->setEnabled(true);
}
}
}
bool Focus::focusIn(int ms)
{
if (currentFocuser == nullptr)
return false;
if (currentFocuser->isConnected() == false)
{
appendLogText(i18n("Error: Lost connection to Focuser."));
return false;
}
if (ms == -1)
ms = stepIN->value();
qCDebug(KSTARS_EKOS_FOCUS) << "Focus in (" << ms << ")";
lastFocusDirection = FOCUS_IN;
currentFocuser->focusIn();
if (canAbsMove)
{
currentFocuser->moveAbs(currentPosition - ms);
appendLogText(i18n("Focusing inward by %1 steps...", ms));
}
else if (canRelMove)
{
currentFocuser->moveRel(ms);
appendLogText(i18n("Focusing inward by %1 steps...", ms));
}
else
{
currentFocuser->moveByTimer(ms);
appendLogText(i18n("Focusing inward by %1 ms...", ms));
}
return true;
}
bool Focus::focusOut(int ms)
{
if (currentFocuser == nullptr)
return false;
if (currentFocuser->isConnected() == false)
{
appendLogText(i18n("Error: Lost connection to Focuser."));
return false;
}
lastFocusDirection = FOCUS_OUT;
if (ms == -1)
ms = stepIN->value();
qCDebug(KSTARS_EKOS_FOCUS) << "Focus out (" << ms << ")";
currentFocuser->focusOut();
if (canAbsMove)
{
currentFocuser->moveAbs(currentPosition + ms);
appendLogText(i18n("Focusing outward by %1 steps...", ms));
}
else if (canRelMove)
{
currentFocuser->moveRel(ms);
appendLogText(i18n("Focusing outward by %1 steps...", ms));
}
else
{
currentFocuser->moveByTimer(ms);
appendLogText(i18n("Focusing outward by %1 ms...", ms));
}
return true;
}
void Focus::newFITS(IBLOB *bp)
{
if (bp == nullptr)
{
capture();
return;
}
// Ignore guide head if there is any.
if (!strcmp(bp->name, "CCD2"))
return;
captureTimeout.stop();
captureTimeoutCounter = 0;
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
disconnect(currentCCD, &ISD::CCD::BLOBUpdated, this, &Ekos::Focus::newFITS);
if (darkFrameCheck->isChecked())
{
FITSData *darkData = DarkLibrary::Instance()->getDarkFrame(targetChip, exposureIN->value());
QVariantMap settings = frameSettings[targetChip];
uint16_t offsetX = settings["x"].toInt() / settings["binx"].toInt();
uint16_t offsetY = settings["y"].toInt() / settings["biny"].toInt();
connect(DarkLibrary::Instance(), &DarkLibrary::darkFrameCompleted, this, [&](bool completed)
{
DarkLibrary::Instance()->disconnect(this);
darkFrameCheck->setChecked(completed);
if (completed)
setCaptureComplete();
else
abort();
});
connect(DarkLibrary::Instance(), &DarkLibrary::newLog, this, &Ekos::Focus::appendLogText);
targetChip->setCaptureFilter(defaultScale);
if (darkData)
DarkLibrary::Instance()->subtract(darkData, focusView, defaultScale, offsetX, offsetY);
else
{
DarkLibrary::Instance()->captureAndSubtract(targetChip, focusView, exposureIN->value(), offsetX, offsetY);
}
return;
}
setCaptureComplete();
}
void Focus::setCaptureComplete()
{
DarkLibrary::Instance()->disconnect(this);
// Get Binning
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
int subBinX = 1, subBinY = 1;
targetChip->getBinning(&subBinX, &subBinY);
// If we have a box, sync the bounding box to its position.
syncTrackingBoxPosition();
// Notify user if we're not looping
if (inFocusLoop == false)
appendLogText(i18n("Image received."));
// If we're not looping and not in autofocus, enable user to capture again.
if (captureInProgress && inFocusLoop == false && inAutoFocus == false)
{
captureB->setEnabled(true);
stopFocusB->setEnabled(false);
currentCCD->setUploadMode(rememberUploadMode);
}
if (rememberCCDExposureLooping)
currentCCD->setExposureLoopingEnabled(true);
captureInProgress = false;
// Get handle to the image data
FITSData *image_data = focusView->getImageData();
// Emit the tracking (bounding) box view
emit newStarPixmap(focusView->getTrackingBoxPixmap(10));
// If we are not looping; OR
// If we are looping but we already have tracking box enabled; OR
// If we are asked to analyze _all_ the stars within the field
// THEN let's find stars in the image and get current HFR
if (inFocusLoop == false || (inFocusLoop && (focusView->isTrackingBoxEnabled() || Options::focusUseFullField())))
{
// First check that we haven't already search for stars
// Since star-searching algorithm are time-consuming, we should only search when necessary
if (image_data->areStarsSearched() == false)
{
// Reset current HFR
currentHFR = -1;
// When we're using FULL field view, we always use either CENTROID algorithm which is the default
// standard algorithm in KStars, or SEP. The other algorithms are too inefficient to run on full frames and require
// a bounding box for them to be effective in near real-time application.
if (Options::focusUseFullField())
{
if (focusDetection != ALGORITHM_CENTROID && focusDetection != ALGORITHM_SEP)
focusView->findStars(ALGORITHM_CENTROID);
else
focusView->findStars(focusDetection);
focusView->setStarFilterRange(static_cast (fullFieldInnerRing->value() / 100.0),
static_cast (fullFieldOuterRing->value() / 100.0));
focusView->filterStars();
focusView->updateFrame();
// Get the average HFR of the whole frame
currentHFR = image_data->getHFR(HFR_AVERAGE);
}
else
{
// If star is already selected then use whatever algorithm currently selected.
if (starSelected)
{
focusView->findStars(focusDetection);
focusView->updateFrame();
currentHFR = image_data->getHFR(HFR_MAX);
}
else
{
// Disable tracking box
focusView->setTrackingBoxEnabled(false);
// If algorithm is set something other than Centeroid or SEP, then force Centroid
// Since it is the most reliable detector when nothing was selected before.
if (focusDetection != ALGORITHM_CENTROID && focusDetection != ALGORITHM_SEP)
focusView->findStars(ALGORITHM_CENTROID);
else
// Otherwise, continue to find use using the selected algorithm
focusView->findStars(focusDetection);
// Reenable tracking box
focusView->setTrackingBoxEnabled(true);
focusView->updateFrame();
// Get maximum HFR in the frame
currentHFR = image_data->getHFR(HFR_MAX);
}
}
}
// Let's now report the current HFR
qCDebug(KSTARS_EKOS_FOCUS) << "Focus newFITS #" << HFRFrames.count() + 1 << ": Current HFR " << currentHFR;
// Add it to existing frames in case we need to take an average
HFRFrames.append(currentHFR);
// Check if we need to average more than a single frame
if (HFRFrames.count() >= focusFramesSpin->value())
{
currentHFR = 0;
// Remove all -1
QMutableVectorIterator i(HFRFrames);
while (i.hasNext())
{
if (i.next() == -1)
i.remove();
}
if (HFRFrames.isEmpty())
currentHFR = -1;
else
{
// Perform simple sigma clipping if frames count > 3
if (HFRFrames.count() > 3)
{
// Sort all HFRs
std::sort(HFRFrames.begin(), HFRFrames.end());
const auto median =
((HFRFrames.size() % 2) ?
HFRFrames[HFRFrames.size() / 2] :
(static_cast(HFRFrames[HFRFrames.size() / 2 - 1]) + HFRFrames[HFRFrames.size() / 2]) * .5);
const auto mean = std::accumulate(HFRFrames.begin(), HFRFrames.end(), .0) / HFRFrames.size();
double variance = 0;
foreach (auto val, HFRFrames)
variance += (val - mean) * (val - mean);
const double stddev = sqrt(variance / HFRFrames.size());
// Reject those 2 sigma away from median
const double sigmaHigh = median + stddev * 2;
const double sigmaLow = median - stddev * 2;
QMutableVectorIterator i(HFRFrames);
while (i.hasNext())
{
auto val = i.next();
if (val > sigmaHigh || val < sigmaLow)
i.remove();
}
}
// Find average HFR
currentHFR = std::accumulate(HFRFrames.begin(), HFRFrames.end(), .0) / HFRFrames.size();
HFRFrames.clear();
}
}
else
{
// If we need to capture more frames to average the HFR, let's do that now.
capture();
return;
}
// Let signal the current HFR now depending on whether the focuser is absolute or relative
if (canAbsMove)
emit newHFR(currentHFR, static_cast(currentPosition));
else
emit newHFR(currentHFR, -1);
// Format the HFR value into a string
QString HFRText = QString("%1").arg(currentHFR, 0, 'f', 2);
HFROut->setText(HFRText);
// Display message in case _last_ HFR was negative
if (lastHFR == -1)
appendLogText(i18n("FITS received. No stars detected."));
// If we have a valid HFR value
if (currentHFR > 0)
{
// Check if we're done from polynomial fitting algorithm
if (focusAlgorithm == FOCUS_POLYNOMIAL && polySolutionFound == MINIMUM_POLY_SOLUTIONS)
{
polySolutionFound = 0;
appendLogText(i18n("Autofocus complete after %1 iterations.", hfr_position.count()));
stop();
setAutoFocusResult(true);
return;
}
Edge *maxStarHFR = nullptr;
// Center tracking box around selected star (if it valid) either in:
// 1. Autofocus
// 2. CheckFocus (minimumHFRCheck)
// The starCenter _must_ already be defined, otherwise, we proceed until
// the latter half of the function searches for a star and define it.
if (starCenter.isNull() == false && (inAutoFocus || minimumRequiredHFR >= 0) &&
(maxStarHFR = image_data->getMaxHFRStar()) != nullptr)
{
// Now we have star selected in the frame
starSelected = true;
starCenter.setX(qMax(0, static_cast(maxStarHFR->x)));
starCenter.setY(qMax(0, static_cast(maxStarHFR->y)));
syncTrackingBoxPosition();
// Record the star information (X, Y, currentHFR)
QVector3D oneStar = starCenter;
oneStar.setZ(currentHFR);
starsHFR.append(oneStar);
}
else
{
// Record the star information (X, Y, currentHFR)
QVector3D oneStar(starCenter.x(), starCenter.y(), currentHFR);
starsHFR.append(oneStar);
}
if (currentHFR > maxHFR)
maxHFR = currentHFR;
// Append point to the #Iterations vs #HFR chart in case of looping or in case in autofocus with a focus
// that does not support position feedback.
if (inFocusLoop || (inAutoFocus && canAbsMove == false && canRelMove == false))
{
if (hfr_position.empty())
hfr_position.append(1);
else
hfr_position.append(hfr_position.last() + 1);
hfr_value.append(currentHFR);
drawHFRPlot();
}
}
else
{
// Let's record an invalid star result
QVector3D oneStar(starCenter.x(), starCenter.y(), -1);
starsHFR.append(oneStar);
}
// Try to average values and find if we have bogus results
if (inAutoFocus && starsHFR.count() > 3)
{
float mean = 0, sum = 0, stddev = 0, noHFR = 0;
for (int i = 0; i < starsHFR.count(); i++)
{
sum += starsHFR[i].x();
if (starsHFR[i].z() == -1)
noHFR++;
}
mean = sum / starsHFR.count();
// Calculate standard deviation
for (int i = 0; i < starsHFR.count(); i++)
stddev += pow(starsHFR[i].x() - mean, 2);
stddev = sqrt(stddev / starsHFR.count());
if (currentHFR == -1 && (stddev > focusBoxSize->value() / 10.0 || noHFR / starsHFR.count() > 0.75))
{
appendLogText(i18n("No reliable star is detected. Aborting..."));
abort();
setAutoFocusResult(false);
return;
}
}
}
// If we are just framing, let's capture again
if (inFocusLoop)
{
capture();
return;
}
// If star is NOT yet selected in a non-full-frame situation
// then let's now try to find the star. This step is skipped for full frames
// since there isn't a single star to select as we are only interested in the overall average HFR.
// We need to check if we can find the star right away, or if we need to _subframe_ around the
// selected star.
if (Options::focusUseFullField() == false && starCenter.isNull())
{
int x = 0, y = 0, w = 0, h = 0;
// Let's get the stored frame settings for this particular chip
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
// Otherwise let's get the target chip frame coordinates.
targetChip->getFrame(&x, &y, &w, &h);
// In case auto star is selected.
if (useAutoStar->isChecked())
{
// Do we have a valid star detected?
Edge *maxStar = image_data->getMaxHFRStar();
if (maxStar == nullptr)
{
appendLogText(i18n("Failed to automatically select a star. Please select a star manually."));
// Center the tracking box in the frame and display it
focusView->setTrackingBox(QRect(w - focusBoxSize->value() / (subBinX * 2),
h - focusBoxSize->value() / (subBinY * 2),
focusBoxSize->value() / subBinX, focusBoxSize->value() / subBinY));
focusView->setTrackingBoxEnabled(true);
// Use can now move it to select the desired star
state = Ekos::FOCUS_WAITING;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
// Start the wait timer so we abort after a timeout if the user does not make a choice
waitStarSelectTimer.start();
return;
}
// set the tracking box on maxStar
starCenter.setX(maxStar->x);
starCenter.setY(maxStar->y);
starCenter.setZ(subBinX);
syncTrackingBoxPosition();
// Do we need to subframe?
if (subFramed == false && useSubFrame->isEnabled() && useSubFrame->isChecked())
{
int offset = (static_cast(focusBoxSize->value()) / subBinX) * 1.5;
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);
// Try to limit the subframed selection
if (subX < minX)
subX = minX;
if (subY < minY)
subY = minY;
if ((subW + subX) > maxW)
subW = maxW - subX;
if ((subH + subY) > maxH)
subH = maxH - subY;
// Now we store the subframe coordinates in the target chip frame settings so we
// reuse it later when we capture again.
QVariantMap settings = frameSettings[targetChip];
settings["x"] = subX;
settings["y"] = subY;
settings["w"] = subW;
settings["h"] = subH;
settings["binx"] = subBinX;
settings["biny"] = subBinY;
qCDebug(KSTARS_EKOS_FOCUS) << "Frame is subframed. X:" << subX << "Y:" << subY << "W:" << subW << "H:" << subH << "binX:" << subBinX << "binY:" << subBinY;
starsHFR.clear();
frameSettings[targetChip] = settings;
// Set the star center in the center of the subframed coordinates
starCenter.setX(subW / (2 * subBinX));
starCenter.setY(subH / (2 * subBinY));
starCenter.setZ(subBinX);
subFramed = true;
focusView->setFirstLoad(true);
// Now let's capture again for the actual requested subframed image.
capture();
}
// If we're subframed or don't need subframe, let's record the max star coordinates
else
{
starCenter.setX(maxStar->x);
starCenter.setY(maxStar->y);
starCenter.setZ(subBinX);
// Let's now capture again if we're autofocusing
if (inAutoFocus)
capture();
}
defaultScale = static_cast(filterCombo->currentIndex());
return;
}
// If manual selection is enabled then let's ask the user to select the focus star
else
{
appendLogText(i18n("Capture complete. Select a star to focus."));
starSelected = false;
// Let's now display and set the tracking box in the center of the frame
// so that the user moves it around to select the desired star.
int subBinX = 1, subBinY = 1;
targetChip->getBinning(&subBinX, &subBinY);
focusView->setTrackingBox(QRect((w - focusBoxSize->value()) / (subBinX * 2),
(h - focusBoxSize->value()) / (2 * subBinY),
focusBoxSize->value() / subBinX, focusBoxSize->value() / subBinY));
focusView->setTrackingBoxEnabled(true);
// Now we wait
state = Ekos::FOCUS_WAITING;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
// If the user does not select for a timeout period, we abort.
waitStarSelectTimer.start();
return;
}
}
// Check if the focus module is requested to verify if the minimum HFR value is met.
if (minimumRequiredHFR >= 0)
{
// In case we failed to detected, we capture again.
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;
}
// If we exceeded maximum tries we abort
else
{
noStarCount = 0;
setAutoFocusResult(false);
}
}
// If the detect current HFR is more than the minimum required HFR
// then we should start the autofocus process now to bring it down.
else if (currentHFR > minimumRequiredHFR)
{
qCDebug(KSTARS_EKOS_FOCUS) << "Current HFR:" << currentHFR << "is above required minimum HFR:" << minimumRequiredHFR << ". Starting AutoFocus...";
inSequenceFocus = true;
start();
}
// Otherwise, the current HFR is fine and lower than the required minimum HFR so we announce success.
else
{
qCDebug(KSTARS_EKOS_FOCUS) << "Current HFR:" << currentHFR << "is below required minimum HFR:" << minimumRequiredHFR << ". Autofocus successful.";
setAutoFocusResult(true);
drawProfilePlot();
}
// We reset minimum required HFR and call it a day.
minimumRequiredHFR = -1;
return;
}
// Let's draw the HFR Plot
drawProfilePlot();
// If focus logging is enabled, let's save the frame.
- if (Options::focusLogging())
+ if (Options::focusLogging() && Options::saveFocusImages())
{
QDir dir;
QString path = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "autofocus/" +
QDateTime::currentDateTime().toString("yyyy-MM-dd");
dir.mkpath(path);
// IS8601 contains colons but they are illegal under Windows OS, so replacing them with '-'
// The timestamp is no longer ISO8601 but it should solve interoperality issues between different OS hosts
QString name = "autofocus_frame_" + QDateTime::currentDateTime().toString("HH-mm-ss") + ".fits";
QString filename = path + QStringLiteral("/") + name;
focusView->getImageData()->saveFITS(filename);
}
// If we are not in autofocus process, we're done.
if (inAutoFocus == false)
return;
// Set state to progress
if (state != Ekos::FOCUS_PROGRESS)
{
state = Ekos::FOCUS_PROGRESS;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
}
// Now let's kick in the algorithms
// Position-based algorithms
if (canAbsMove || canRelMove)
autoFocusAbs();
else
// Time open-looped algorithms
autoFocusRel();
}
void Focus::clearDataPoints()
{
maxHFR = 1;
hfr_position.clear();
hfr_value.clear();
drawHFRPlot();
}
void Focus::drawHFRPlot()
{
v_graph->setData(hfr_position, hfr_value);
if (inFocusLoop == false && (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 * 1.25);
}
HFRPlot->replot();
}
void Focus::drawProfilePlot()
{
QVector currentIndexes;
QVector 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 (double 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 == nullptr)
{
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 (firstGaus)
{
profilePlot->removeGraph(firstGaus);
firstGaus = nullptr;
}
profilePlot->rescaleAxes();
profilePlot->replot();
lastGausIndexes = currentIndexes;
lastGausFrequencies = currentFrequencies;
profilePixmap = profilePlot->grab(); //.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
emit newProfilePixmap(profilePixmap);
}
void Focus::autoFocusAbs()
{
static int minHFRPos = 0, focusOutLimit = 0, focusInLimit = 0;
static double minHFR = 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);
qCDebug(KSTARS_EKOS_FOCUS) << "========================================";
qCDebug(KSTARS_EKOS_FOCUS) << "Current HFR: " << currentHFR << " Current Position: " << currentPosition;
qCDebug(KSTARS_EKOS_FOCUS) << "Last minHFR: " << minHFR << " Last MinHFR Pos: " << minHFRPos;
qCDebug(KSTARS_EKOS_FOCUS) << "Delta: " << deltaTxt << "%";
qCDebug(KSTARS_EKOS_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 (hfr_position.empty())
{
maxPos = 1;
minPos = 1e6;
}
if (currentPosition > maxPos)
maxPos = currentPosition;
if (currentPosition < minPos)
minPos = currentPosition;
hfr_position.append(currentPosition);
hfr_value.append(currentHFR);
drawHFRPlot();
switch (lastFocusDirection)
{
case FOCUS_NONE:
lastHFR = currentHFR;
initialFocuserAbsPosition = currentPosition;
minHFR = currentHFR;
minHFRPos = currentPosition;
HFRDec = 0;
HFRInc = 0;
focusOutLimit = 0;
focusInLimit = 0;
if (focusOut(pulseDuration) == false)
{
abort();
setAutoFocusResult(false);
}
break;
case FOCUS_IN:
case FOCUS_OUT:
static int lastHFRPos = 0, initSlopePos = 0;
static double initSlopeHFR = 0;
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 after %1 iterations.", hfr_position.count()));
stop();
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;
qCDebug(KSTARS_EKOS_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;
qCDebug(KSTARS_EKOS_FOCUS) << "New FocusInLimit " << focusInLimit;
}
else if (lastFocusDirection == FOCUS_IN && lastHFRPos > focusOutLimit &&
fabs(currentHFR - lastHFR) > 0.1)
{
focusOutLimit = lastHFRPos;
qCDebug(KSTARS_EKOS_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;
}
}
qCDebug(KSTARS_EKOS_FOCUS) << "Using slope to calculate target pulse...";
}
// Otherwise proceed iteratively
else
{
if (lastFocusDirection == FOCUS_IN)
targetPosition = currentPosition - pulseDuration;
else
targetPosition = currentPosition + pulseDuration;
qCDebug(KSTARS_EKOS_FOCUS) << "Proceeding iteratively to next target pulse ...";
}
qCDebug(KSTARS_EKOS_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;
qCDebug(KSTARS_EKOS_FOCUS) << "new minHFR " << minHFR << " @ position " << minHFRPos;
}
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;
qCDebug(KSTARS_EKOS_FOCUS) << "Focus is moving away from optimal HFR.";
// Let's set new limits
if (lastFocusDirection == FOCUS_IN)
{
focusInLimit = currentPosition;
qCDebug(KSTARS_EKOS_FOCUS) << "Setting focus IN limit to " << focusInLimit;
if (hfr_position.count() > 3)
{
focusOutLimit = hfr_position[hfr_position.count() - 3];
qCDebug(KSTARS_EKOS_FOCUS) << "Setting focus OUT limit to " << focusOutLimit;
}
}
else
{
focusOutLimit = currentPosition;
qCDebug(KSTARS_EKOS_FOCUS) << "Setting focus OUT limit to " << focusOutLimit;
if (hfr_position.count() > 3)
{
focusInLimit = hfr_position[hfr_position.count() - 3];
qCDebug(KSTARS_EKOS_FOCUS) << "Setting focus IN limit to " << focusInLimit;
}
}
bool polyMinimumFound = false;
if (focusAlgorithm == FOCUS_POLYNOMIAL && hfr_position.count() > 5)
{
double chisq = 0, min_position = 0, min_hfr = 0;
coeff = gsl_polynomial_fit(hfr_position.data(), hfr_value.data(), hfr_position.count(), 3, chisq);
polyMinimumFound = findMinimum(minHFRPos, &min_position, &min_hfr);
qCDebug(KSTARS_EKOS_FOCUS) << "Polynomial Coefficients c0:" << coeff[0] << "c1:" << coeff[1] << "c2:" << coeff[2]
<< "c3:" << coeff[3];
qCDebug(KSTARS_EKOS_FOCUS) << "Found Minimum?" << (polyMinimumFound ? "Yes" : "No");
if (polyMinimumFound)
{
qCDebug(KSTARS_EKOS_FOCUS) << "Minimum Solution:" << min_hfr << "@" << min_position;
polySolutionFound++;
targetPosition = floor(min_position);
appendLogText(i18n("Found polynomial solution @ %1", QString::number(min_position, 'f', 0)));
}
}
if (polyMinimumFound == false)
{
// 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;
}
qCDebug(KSTARS_EKOS_FOCUS) << "new targetPosition " << targetPosition;
}
// Limit target Pulse to algorithm limits
if (focusInLimit != 0 && lastFocusDirection == FOCUS_IN && targetPosition < focusInLimit)
{
targetPosition = focusInLimit;
qCDebug(KSTARS_EKOS_FOCUS) << "Limiting target pulse to focus in limit " << targetPosition;
}
else if (focusOutLimit != 0 && lastFocusDirection == FOCUS_OUT && targetPosition > focusOutLimit)
{
targetPosition = focusOutLimit;
qCDebug(KSTARS_EKOS_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 after %1 iterations.", hfr_position.count()));
stop();
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())
{
int minTravelLimit = qMax(0.0, initialFocuserAbsPosition - maxTravelIN->value());
int maxTravelLimit = qMin(absMotionMax, initialFocuserAbsPosition + maxTravelIN->value());
// In case we are asked to go below travel limit, but we are not there yet
// let us go there and see the result before aborting
if (fabs(currentPosition - minTravelLimit) > 10 && targetPosition < minTravelLimit)
{
targetPosition = minTravelLimit;
}
// Same for max travel
else if (fabs(currentPosition - maxTravelLimit) > 10 && targetPosition > maxTravelLimit)
{
targetPosition = maxTravelLimit;
}
else
{
qCDebug(KSTARS_EKOS_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);
qCDebug(KSTARS_EKOS_FOCUS) << "delta (targetPosition - currentPosition) " << delta;
qCDebug(KSTARS_EKOS_FOCUS) << "Focusing " << ((delta < 0) ? "IN" : "OUT");
// Now cross your fingers and wait
bool rc = false;
if (delta > 0)
rc = focusOut(delta);
else
rc = focusIn(fabs(delta));
if (rc == false)
{
abort();
setAutoFocusResult(false);
}
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:
case FOCUS_OUT:
if (fabs(currentHFR - minHFR) < (toleranceIN->value() / 100.0) && HFRInc == 0)
{
appendLogText(i18n("Autofocus complete after %1 iterations.", hfr_position.count()));
stop();
setAutoFocusResult(true);
break;
}
else if (currentHFR < lastHFR)
{
if (currentHFR < minHFR)
minHFR = currentHFR;
lastHFR = currentHFR;
if (lastFocusDirection == FOCUS_IN)
focusIn(pulseDuration);
else
focusOut(pulseDuration);
HFRInc = 0;
}
else
{
HFRInc++;
lastHFR = currentHFR;
HFRInc = 0;
pulseDuration *= 0.75;
bool rc = false;
if (lastFocusDirection == FOCUS_IN)
rc = focusOut(pulseDuration);
else
rc = focusIn(pulseDuration);
if (rc == false)
{
abort();
setAutoFocusResult(false);
}
}
break;
}
}
/*void Focus::registerFocusProperty(INDI::Property *prop)
{
// Return if it is not our current focuser
if (strcmp(prop->getDeviceName(), currentFocuser->getDeviceName()))
return;
// Do not make unnecessary function call
// Check if current focuser supports absolute mode
if (canAbsMove == false && currentFocuser->canAbsMove())
{
canAbsMove = true;
getAbsFocusPosition();
absTicksSpin->setEnabled(true);
absTicksLabel->setEnabled(true);
startGotoB->setEnabled(true);
}
// Do not make unnecessary function call
// Check if current focuser supports relative mode
if (canRelMove == false && currentFocuser->canRelMove())
canRelMove = true;
if (canTimerMove == false && currentFocuser->canTimerMove())
{
canTimerMove = true;
resetButtons();
}
}*/
void Focus::processFocusNumber(INumberVectorProperty *nvp)
{
// Return if it is not our current focuser
if (strcmp(nvp->device, currentFocuser->getDeviceName()))
return;
if (!strcmp(nvp->name, "FOCUS_BACKLASH_STEPS"))
{
focusBacklashSpin->setValue(nvp->np[0].value);
return;
}
if (!strcmp(nvp->name, "ABS_FOCUS_POSITION"))
{
INumber *pos = IUFindNumber(nvp, "FOCUS_ABSOLUTE_POSITION");
if (pos)
{
currentPosition = pos->value;
absTicksLabel->setText(QString::number(static_cast(currentPosition)));
emit absolutePositionChanged(currentPosition);
}
if (adjustFocus && nvp->s == IPS_OK)
{
adjustFocus = false;
lastFocusDirection = FOCUS_NONE;
emit focusPositionAdjusted();
return;
}
if (resetFocus && nvp->s == IPS_OK)
{
resetFocus = false;
appendLogText(i18n("Restarting autofocus process..."));
start();
}
if (canAbsMove && inAutoFocus)
{
if (nvp->s == IPS_OK && captureInProgress == false)
QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture);
//capture();
else if (nvp->s == IPS_ALERT)
{
appendLogText(i18n("Focuser error, check INDI panel."));
abort();
setAutoFocusResult(false);
}
}
else if (nvp->s == IPS_ALERT)
appendLogText(i18n("Focuser error, check INDI panel."));
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);
absTicksLabel->setText(QString::number(static_cast(currentPosition)));
emit absolutePositionChanged(currentPosition);
}
if (adjustFocus && nvp->s == IPS_OK)
{
adjustFocus = false;
lastFocusDirection = FOCUS_NONE;
emit focusPositionAdjusted();
return;
}
if (resetFocus && nvp->s == IPS_OK)
{
resetFocus = false;
appendLogText(i18n("Restarting autofocus process..."));
start();
}
if (canRelMove && inAutoFocus)
{
if (nvp->s == IPS_OK && captureInProgress == false)
QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture);
else if (nvp->s == IPS_ALERT)
{
appendLogText(i18n("Focuser error, check INDI panel."));
abort();
setAutoFocusResult(false);
}
}
else if (nvp->s == IPS_ALERT)
appendLogText(i18n("Focuser error, check INDI panel."));
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)
QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture);
else if (nvp->s == IPS_ALERT)
{
appendLogText(i18n("Focuser error, check INDI panel."));
abort();
setAutoFocusResult(false);
}
}
else if (nvp->s == IPS_ALERT)
appendLogText(i18n("Focuser error, check INDI panel."));
return;
}
}
void Focus::appendLogText(const QString &text)
{
m_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));
qCInfo(KSTARS_EKOS_FOCUS) << text;
emit newLog(text);
}
void Focus::clearLog()
{
m_LogText.clear();
emit newLog(QString());
}
void Focus::startFraming()
{
if (currentCCD == nullptr)
{
appendLogText(i18n("No CCD connected."));
return;
}
waitStarSelectTimer.stop();
inFocusLoop = true;
HFRFrames.clear();
clearDataPoints();
//emit statusUpdated(true);
state = Ekos::FOCUS_FRAMING;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
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);
return;
}
if (inAutoFocus)
{
stopFocusB->setEnabled(true);
startFocusB->setEnabled(false);
startLoopB->setEnabled(false);
captureB->setEnabled(false);
focusOutB->setEnabled(false);
focusInB->setEnabled(false);
startGotoB->setEnabled(false);
stopGotoB->setEnabled(false);
resetFrameB->setEnabled(false);
return;
}
if (currentFocuser)
{
focusOutB->setEnabled(true);
focusInB->setEnabled(true);
startFocusB->setEnabled(focusType == FOCUS_AUTO);
startGotoB->setEnabled(canAbsMove);
stopGotoB->setEnabled(true);
}
else
{
focusOutB->setEnabled(false);
focusInB->setEnabled(false);
startFocusB->setEnabled(false);
startGotoB->setEnabled(false);
stopGotoB->setEnabled(false);
}
stopFocusB->setEnabled(false);
startLoopB->setEnabled(true);
if (captureInProgress == false)
{
captureB->setEnabled(true);
resetFrameB->setEnabled(true);
}
}
void Focus::updateBoxSize(int value)
{
if (currentCCD == nullptr)
return;
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
if (targetChip == nullptr)
return;
int subBinX, subBinY;
targetChip->getBinning(&subBinX, &subBinY);
QRect trackBox = focusView->getTrackingBox();
QPoint center(trackBox.x() + (trackBox.width() / 2), trackBox.y() + (trackBox.height() / 2));
trackBox =
QRect(center.x() - value / (2 * subBinX), center.y() - value / (2 * subBinY), value / subBinX, value / subBinY);
focusView->setTrackingBox(trackBox);
}
void Focus::focusStarSelected(int x, int y)
{
if (state == Ekos::FOCUS_PROGRESS)
return;
if (subFramed == false)
{
rememberStarCenter.setX(x);
rememberStarCenter.setY(y);
}
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 = (static_cast(focusBoxSize->value()) / subBinX) * 1.5;
QRect starRect;
bool squareMovedOutside = false;
if (subFramed == false && useSubFrame->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;
//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;
qCDebug(KSTARS_EKOS_FOCUS) << "Frame is subframed. X:" << x << "Y:" << y << "W:" << w << "H:" << h << "binX:" << subBinX << "binY:" << subBinY;
focusView->setFirstLoad(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);
double dist = sqrt((starCenter.x() - x) * (starCenter.x() - x) + (starCenter.y() - y) * (starCenter.y() - y));
squareMovedOutside = (dist > (static_cast(focusBoxSize->value()) / subBinX));
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);
starRect = QRect(starCenter.x() - focusBoxSize->value() / (2 * subBinX),
starCenter.y() - focusBoxSize->value() / (2 * subBinY), focusBoxSize->value() / subBinX,
focusBoxSize->value() / subBinY);
focusView->setTrackingBox(starRect);
}
starsHFR.clear();
starCenter.setZ(subBinX);
//starSelected=true;
defaultScale = static_cast(filterCombo->currentIndex());
if (squareMovedOutside && inAutoFocus == false && useAutoStar->isChecked())
{
useAutoStar->blockSignals(true);
useAutoStar->setChecked(false);
useAutoStar->blockSignals(false);
appendLogText(i18n("Disabling Auto Star Selection as star selection box was moved manually."));
starSelected = false;
}
else if (starSelected == false)
{
appendLogText(i18n("Focus star is selected."));
starSelected = true;
capture();
}
waitStarSelectTimer.stop();
state = inAutoFocus ? FOCUS_PROGRESS : FOCUS_IDLE;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
}
void Focus::checkFocus(double requiredHFR)
{
qCDebug(KSTARS_EKOS_FOCUS) << "Check Focus requested with minimum required HFR" << requiredHFR;
minimumRequiredHFR = requiredHFR;
capture();
}
void Focus::toggleSubframe(bool enable)
{
if (enable == false)
resetFrame();
starSelected = false;
starCenter = QVector3D();
if (useFullField->isChecked())
useFullField->setChecked(!enable);
}
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(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::setAutoStarEnabled(bool enable)
{
useAutoStar->setChecked(enable);
Options::setFocusAutoStarEnabled(enable);
}
void Focus::setAutoSubFrameEnabled(bool enable)
{
useSubFrame->setChecked(enable);
Options::setFocusSubFrame(enable);
}
void Focus::setAutoFocusParameters(int boxSize, int stepSize, int maxTravel, double tolerance)
{
focusBoxSize->setValue(boxSize);
stepIN->setValue(stepSize);
maxTravelIN->setValue(maxTravel);
toleranceIN->setValue(tolerance);
}
void Focus::setAutoFocusResult(bool status)
{
qCDebug(KSTARS_EKOS_FOCUS) << "AutoFocus result:" << status;
// In case of failure, go back to last position if the focuser is absolute
if (status == false && canAbsMove && currentFocuser && currentFocuser->isConnected() &&
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;
}
}
int settleTime = m_GuidingSuspended ? GuideSettleTime->value() : 0;
// Always resume guiding if we suspended it before
if (m_GuidingSuspended)
{
emit resumeGuiding();
m_GuidingSuspended = false;
}
resetFocusIteration = 0;
if (settleTime > 0)
appendLogText(i18n("Settling..."));
QTimer::singleShot(settleTime * 1000, this, [ &, status, settleTime]()
{
if (settleTime > 0)
appendLogText(i18n("Settling complete."));
if (status)
{
KSNotification::event(QLatin1String("FocusSuccessful"), i18n("Autofocus operation completed successfully"));
state = Ekos::FOCUS_COMPLETE;
}
else
{
KSNotification::event(QLatin1String("FocusFailed"), i18n("Autofocus operation failed with errors"), KSNotification::EVENT_ALERT);
state = Ekos::FOCUS_FAILED;
}
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
// Do not emit result back yet if we have a locked filter pending return to original filter
if (fallbackFilterPending)
{
filterManager->setFilterPosition(fallbackFilterPosition, static_cast(FilterManager::CHANGE_POLICY | FilterManager::OFFSET_POLICY));
return;
}
emit newStatus(state);
});
}
void Focus::checkAutoStarTimeout()
{
//if (starSelected == false && inAutoFocus)
if (starCenter.isNull() && (inAutoFocus || minimumRequiredHFR > 0))
{
if (inAutoFocus)
{
if (rememberStarCenter.isNull() == false)
{
focusStarSelected(rememberStarCenter.x(), rememberStarCenter.y());
appendLogText(i18n("No star was selected. Using last known position..."));
return;
}
}
appendLogText(i18n("No star was selected. Aborting..."));
initialFocuserAbsPosition = -1;
abort();
setAutoFocusResult(false);
}
else if (state == FOCUS_WAITING)
{
state = FOCUS_IDLE;
qCDebug(KSTARS_EKOS_FOCUS) << "State:" << Ekos::getFocusStatusString(state);
emit newStatus(state);
}
}
void Focus::setAbsoluteFocusTicks()
{
if (currentFocuser == nullptr)
return;
if (currentFocuser->isConnected() == false)
{
appendLogText(i18n("Error: Lost connection to Focuser."));
return;
}
qCDebug(KSTARS_EKOS_FOCUS) << "Setting focus ticks to " << absTicksSpin->value();
currentFocuser->moveAbs(absTicksSpin->value());
}
//void Focus::setActiveBinning(int bin)
//{
// activeBin = bin + 1;
// Options::setFocusXBin(activeBin);
//}
// TODO remove from kstars.kcfg
/*void Focus::setFrames(int value)
{
Options::setFocusFrames(value);
}*/
void Focus::syncTrackingBoxPosition()
{
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
Q_ASSERT(targetChip);
int subBinX = 1, subBinY = 1;
targetChip->getBinning(&subBinX, &subBinY);
if (starCenter.isNull() == false)
{
double boxSize = focusBoxSize->value();
int x, y, w, h;
targetChip->getFrame(&x, &y, &w, &h);
// If box size is larger than image size, set it to lower index
if (boxSize / subBinX >= w || boxSize / subBinY >= h)
{
focusBoxSize->setValue((boxSize / subBinX >= w) ? w : h);
return;
}
// If binning changed, update coords accordingly
if (subBinX != starCenter.z())
{
if (starCenter.z() > 0)
{
starCenter.setX(starCenter.x() * (starCenter.z() / subBinX));
starCenter.setY(starCenter.y() * (starCenter.z() / subBinY));
}
starCenter.setZ(subBinX);
}
QRect starRect = QRect(starCenter.x() - boxSize / (2 * subBinX), starCenter.y() - boxSize / (2 * subBinY),
boxSize / subBinX, boxSize / subBinY);
focusView->setTrackingBoxEnabled(true);
focusView->setTrackingBox(starRect);
}
}
void Focus::showFITSViewer()
{
FITSData *data = focusView->getImageData();
if (data)
{
QUrl url = QUrl::fromLocalFile(data->filename());
if (fv.isNull())
{
if (Options::singleWindowCapturedFITS())
fv = KStars::Instance()->genericFITSViewer();
else
{
fv = new FITSViewer(Options::independentWindowFITS() ? nullptr : KStars::Instance());
KStars::Instance()->addFITSViewer(fv);
}
fv->addFITS(url);
FITSView *currentView = fv->getCurrentView();
if (currentView)
currentView->getImageData()->setAutoRemoveTemporaryFITS(false);
}
else
fv->updateFITS(url, 0);
fv->show();
}
}
void Focus::adjustFocusOffset(int value, bool useAbsoluteOffset)
{
adjustFocus = true;
int relativeOffset = 0;
if (useAbsoluteOffset == false)
relativeOffset = value;
else
relativeOffset = value - currentPosition;
if (relativeOffset > 0)
focusOut(relativeOffset);
else
focusIn(abs(relativeOffset));
}
void Focus::toggleFocusingWidgetFullScreen()
{
if (focusingWidget->parent() == nullptr)
{
focusingWidget->setParent(this);
rightLayout->insertWidget(0, focusingWidget);
focusingWidget->showNormal();
}
else
{
focusingWidget->setParent(nullptr);
focusingWidget->setWindowTitle(i18n("Focus Frame"));
focusingWidget->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
focusingWidget->showMaximized();
focusingWidget->show();
}
}
void Focus::setMountStatus(ISD::Telescope::Status newState)
{
switch (newState)
{
case ISD::Telescope::MOUNT_PARKING:
case ISD::Telescope::MOUNT_SLEWING:
case ISD::Telescope::MOUNT_MOVING:
captureB->setEnabled(false);
startFocusB->setEnabled(false);
startLoopB->setEnabled(false);
// If mount is moved while we have a star selected and subframed
// let us reset the frame.
if (subFramed)
resetFrame();
break;
default:
resetButtons();
break;
}
}
double Focus::fn1(double x, void *params)
{
Focus *module = static_cast(params);
return (module->coeff[0] + module->coeff[1] * x + module->coeff[2] * pow(x, 2) + module->coeff[3] * pow(x, 3));
}
bool Focus::findMinimum(double expected, double *position, double *hfr)
{
int status;
int iter = 0, max_iter = 100;
const gsl_min_fminimizer_type *T;
gsl_min_fminimizer *s;
double m = expected;
double a = *std::min_element(hfr_position.constBegin(), hfr_position.constEnd());
double b = *std::max_element(hfr_position.constBegin(), hfr_position.constEnd());
;
gsl_function F;
F.function = &Focus::fn1;
F.params = this;
// Must turn off error handler or it aborts on error
gsl_set_error_handler_off();
T = gsl_min_fminimizer_brent;
s = gsl_min_fminimizer_alloc(T);
status = gsl_min_fminimizer_set(s, &F, m, a, b);
if (status != GSL_SUCCESS)
{
qCWarning(KSTARS_EKOS_FOCUS) << "Focus GSL error:" << gsl_strerror(status);
return false;
}
do
{
iter++;
status = gsl_min_fminimizer_iterate(s);
m = gsl_min_fminimizer_x_minimum(s);
a = gsl_min_fminimizer_x_lower(s);
b = gsl_min_fminimizer_x_upper(s);
status = gsl_min_test_interval(a, b, 0.01, 0.0);
if (status == GSL_SUCCESS)
{
*position = m;
*hfr = fn1(m, this);
}
}
while (status == GSL_CONTINUE && iter < max_iter);
gsl_min_fminimizer_free(s);
return (status == GSL_SUCCESS);
}
void Focus::removeDevice(ISD::GDInterface *deviceRemoved)
{
// Check in Focusers
for (ISD::GDInterface *focuser : Focusers)
{
if (!strcmp(focuser->getDeviceName(), deviceRemoved->getDeviceName()))
{
Focusers.removeAll(dynamic_cast(focuser));
focuserCombo->removeItem(focuserCombo->findText(focuser->getDeviceName()));
checkFocuser();
resetButtons();
}
}
// Check in CCDs
for (ISD::GDInterface *ccd : CCDs)
{
if (!strcmp(ccd->getDeviceName(), deviceRemoved->getDeviceName()))
{
CCDs.removeAll(dynamic_cast(ccd));
CCDCaptureCombo->removeItem(CCDCaptureCombo->findText(ccd->getDeviceName()));
CCDCaptureCombo->removeItem(CCDCaptureCombo->findText(ccd->getDeviceName() + QString(" Guider")));
checkCCD();
resetButtons();
}
}
// Check in Filters
for (ISD::GDInterface *filter : Filters)
{
if (!strcmp(filter->getDeviceName(), deviceRemoved->getDeviceName()))
{
Filters.removeAll(filter);
FilterDevicesCombo->removeItem(FilterDevicesCombo->findText(filter->getDeviceName()));
if (Filters.empty())
currentFilter = nullptr;
checkFilter();
resetButtons();
}
}
}
void Focus::setFilterManager(const QSharedPointer &manager)
{
filterManager = manager;
connect(filterManagerB, &QPushButton::clicked, [this]()
{
filterManager->show();
filterManager->raise();
});
connect(filterManager.data(), &FilterManager::ready, [this]()
{
if (filterPositionPending)
{
filterPositionPending = false;
capture();
}
else if (fallbackFilterPending)
{
fallbackFilterPending = false;
emit newStatus(state);
}
}
);
connect(filterManager.data(), &FilterManager::failed, [this]()
{
appendLogText(i18n("Filter operation failed."));
abort();
}
);
connect(this, &Focus::newStatus, [this](Ekos::FocusState state)
{
if (FilterPosCombo->currentIndex() != -1 && canAbsMove && state == Ekos::FOCUS_COMPLETE)
{
filterManager->setFilterAbsoluteFocusPosition(FilterPosCombo->currentIndex(), currentPosition);
}
});
connect(exposureIN, &QDoubleSpinBox::editingFinished, [this]()
{
if (currentFilter)
filterManager->setFilterExposure(FilterPosCombo->currentIndex(), exposureIN->value());
});
connect(filterManager.data(), &FilterManager::labelsChanged, this, [this]()
{
FilterPosCombo->clear();
FilterPosCombo->addItems(filterManager->getFilterLabels());
currentFilterPosition = filterManager->getFilterPosition();
FilterPosCombo->setCurrentIndex(currentFilterPosition - 1);
//Options::setDefaultFocusFilterWheelFilter(FilterPosCombo->currentText());
});
connect(filterManager.data(), &FilterManager::positionChanged, this, [this]()
{
currentFilterPosition = filterManager->getFilterPosition();
FilterPosCombo->setCurrentIndex(currentFilterPosition - 1);
//Options::setDefaultFocusFilterWheelFilter(FilterPosCombo->currentText());
});
connect(filterManager.data(), &FilterManager::exposureChanged, this, [this]()
{
exposureIN->setValue(filterManager->getFilterExposure());
;
});
connect(FilterPosCombo, static_cast(&QComboBox::currentIndexChanged),
[ = ](const QString & text)
{
exposureIN->setValue(filterManager->getFilterExposure(text));
//Options::setDefaultFocusFilterWheelFilter(text);
});
}
void Focus::toggleVideo(bool enabled)
{
if (currentCCD == nullptr)
return;
if (currentCCD->isBLOBEnabled() == false)
{
if (Options::guiderType() != Ekos::Guide::GUIDE_INTERNAL)
currentCCD->setBLOBEnabled(true);
else
{
connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, enabled]()
{
//QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr);
KSMessageBox::Instance()->disconnect(this);
currentCCD->setVideoStreamEnabled(enabled);
});
KSMessageBox::Instance()->questionYesNo(i18n("Image transfer is disabled for this camera. Would you like to enable it?"));
}
}
else
currentCCD->setVideoStreamEnabled(enabled);
}
void Focus::setVideoStreamEnabled(bool enabled)
{
if (enabled)
{
liveVideoB->setChecked(true);
liveVideoB->setIcon(QIcon::fromTheme("camera-on"));
}
else
{
liveVideoB->setChecked(false);
liveVideoB->setIcon(QIcon::fromTheme("camera-ready"));
}
}
void Focus::processCaptureTimeout()
{
captureTimeoutCounter++;
if (captureTimeoutCounter >= 3)
{
captureTimeoutCounter = 0;
appendLogText(i18n("Exposure timeout. Aborting..."));
abort();
if (inAutoFocus)
setAutoFocusResult(false);
else if (m_GuidingSuspended)
{
emit resumeGuiding();
m_GuidingSuspended = false;
}
return;
}
appendLogText(i18n("Exposure timeout. Restarting exposure..."));
ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD);
targetChip->abortExposure();
targetChip->capture(exposureIN->value());
captureTimeout.start(exposureIN->value() * 1000 + FOCUS_TIMEOUT_THRESHOLD);
}
void Focus::syncSettings()
{
QDoubleSpinBox *dsb = nullptr;
QSpinBox *sb = nullptr;
QCheckBox *cb = nullptr;
QComboBox *cbox = nullptr;
if ( (dsb = qobject_cast(sender())))
{
///////////////////////////////////////////////////////////////////////////
/// Focuser Group
///////////////////////////////////////////////////////////////////////////
if (dsb == FocusSettleTime)
Options::setFocusSettleTime(dsb->value());
///////////////////////////////////////////////////////////////////////////
/// CCD & Filter Wheel Group
///////////////////////////////////////////////////////////////////////////
else if (dsb == gainIN)
Options::setFocusGain(dsb->value());
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
else if (dsb == fullFieldInnerRing)
Options::setFocusFullFieldInnerRadius(dsb->value());
else if (dsb == fullFieldOuterRing)
Options::setFocusFullFieldOuterRadius(dsb->value());
else if (dsb == GuideSettleTime)
Options::setGuideSettleTime(dsb->value());
else if (dsb == maxTravelIN)
Options::setFocusMaxTravel(dsb->value());
else if (dsb == toleranceIN)
Options::setFocusTolerance(dsb->value());
else if (dsb == thresholdSpin)
Options::setFocusThreshold(dsb->value());
}
else if ( (sb = qobject_cast(sender())))
{
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
if (sb == focusBoxSize)
Options::setFocusBoxSize(sb->value());
else if (sb == stepIN)
Options::setFocusTicks(sb->value());
else if (sb == focusFramesSpin)
Options::setFocusFramesCount(sb->value());
}
else if ( (cb = qobject_cast(sender())))
{
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
if (cb == useAutoStar)
Options::setFocusAutoStarEnabled(cb->isChecked());
else if (cb == useSubFrame)
Options::setFocusSubFrame(cb->isChecked());
else if (cb == darkFrameCheck)
Options::setUseFocusDarkFrame(cb->isChecked());
else if (cb == useFullField)
Options::setFocusUseFullField(cb->isChecked());
else if (cb == suspendGuideCheck)
Options::setSuspendGuiding(cb->isChecked());
}
else if ( (cbox = qobject_cast(sender())))
{
///////////////////////////////////////////////////////////////////////////
/// CCD & Filter Wheel Group
///////////////////////////////////////////////////////////////////////////
if (cbox == focuserCombo)
Options::setDefaultFocusFocuser(cbox->currentText());
else if (cbox == CCDCaptureCombo)
Options::setDefaultFocusCCD(cbox->currentText());
else if (cbox == binningCombo)
{
activeBin = cbox->currentIndex() + 1;
Options::setFocusXBin(activeBin);
}
else if (cbox == FilterDevicesCombo)
Options::setDefaultFocusFilterWheel(cbox->currentText());
// Filter Effects already taken care of in filterChangeWarning
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
else if (cbox == focusAlgorithmCombo)
Options::setFocusAlgorithm(cbox->currentIndex());
else if (cbox == focusDetectionCombo)
Options::setFocusDetection(cbox->currentIndex());
}
}
void Focus::loadSettings()
{
///////////////////////////////////////////////////////////////////////////
/// Focuser Group
///////////////////////////////////////////////////////////////////////////
// Focus settle time
FocusSettleTime->setValue(Options::focusSettleTime());
///////////////////////////////////////////////////////////////////////////
/// CCD & Filter Wheel Group
///////////////////////////////////////////////////////////////////////////
// Binning
activeBin = Options::focusXBin();
binningCombo->setCurrentIndex(activeBin - 1);
// Gain
gainIN->setValue(Options::focusGain());
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
// Auto Star?
useAutoStar->setChecked(Options::focusAutoStarEnabled());
// Subframe?
useSubFrame->setChecked(Options::focusSubFrame());
// Dark frame?
darkFrameCheck->setChecked(Options::useFocusDarkFrame());
// Use full field?
useFullField->setChecked(Options::focusUseFullField());
// full field inner ring
fullFieldInnerRing->setValue(Options::focusFullFieldInnerRadius());
// full field outer ring
fullFieldOuterRing->setValue(Options::focusFullFieldOuterRadius());
// Suspend guiding?
suspendGuideCheck->setChecked(Options::suspendGuiding());
// Guide Setting time
GuideSettleTime->setValue(Options::guideSettleTime());
// Box Size
focusBoxSize->setValue(Options::focusBoxSize());
// Max Travel
if (Options::focusMaxTravel() > maxTravelIN->maximum())
maxTravelIN->setMaximum(Options::focusMaxTravel());
maxTravelIN->setValue(Options::focusMaxTravel());
// Step
stepIN->setValue(Options::focusTicks());
// Tolerance
toleranceIN->setValue(Options::focusTolerance());
// Threshold spin
thresholdSpin->setValue(Options::focusThreshold());
// Focus Algorithm
focusAlgorithm = static_cast(Options::focusAlgorithm());
focusAlgorithmCombo->setCurrentIndex(focusAlgorithm);
// Frames Count
focusFramesSpin->setValue(Options::focusFramesCount());
// Focus Detection
focusDetection = static_cast(Options::focusDetection());
thresholdSpin->setEnabled(focusDetection == ALGORITHM_THRESHOLD);
focusDetectionCombo->setCurrentIndex(focusDetection);
}
void Focus::initSettingsConnections()
{
///////////////////////////////////////////////////////////////////////////
/// Focuser Group
///////////////////////////////////////////////////////////////////////////
connect(focuserCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
connect(FocusSettleTime, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
///////////////////////////////////////////////////////////////////////////
/// CCD & Filter Wheel Group
///////////////////////////////////////////////////////////////////////////
connect(CCDCaptureCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
connect(binningCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
connect(gainIN, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(FilterDevicesCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
connect(FilterPosCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
///////////////////////////////////////////////////////////////////////////
/// Settings Group
///////////////////////////////////////////////////////////////////////////
connect(useAutoStar, &QCheckBox::toggled, this, &Ekos::Focus::syncSettings);
connect(useSubFrame, &QCheckBox::toggled, this, &Ekos::Focus::syncSettings);
connect(darkFrameCheck, &QCheckBox::toggled, this, &Ekos::Focus::syncSettings);
connect(useFullField, &QCheckBox::toggled, this, &Ekos::Focus::syncSettings);
connect(fullFieldInnerRing, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(fullFieldOuterRing, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(suspendGuideCheck, &QCheckBox::toggled, this, &Ekos::Focus::syncSettings);
connect(GuideSettleTime, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(focusBoxSize, static_cast(&QSpinBox::valueChanged), this, &Focus::syncSettings);
connect(maxTravelIN, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(stepIN, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(toleranceIN, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(thresholdSpin, &QDoubleSpinBox::editingFinished, this, &Focus::syncSettings);
connect(focusAlgorithmCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
connect(focusFramesSpin, static_cast(&QSpinBox::valueChanged), this, &Focus::syncSettings);
connect(focusDetectionCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::syncSettings);
}
void Focus::initPlots()
{
connect(clearDataB, &QPushButton::clicked, this, &Ekos::Focus::clearDataPoints);
profileDialog = new QDialog(this);
profileDialog->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
QVBoxLayout *profileLayout = new QVBoxLayout(profileDialog);
profileDialog->setWindowTitle(i18n("Relative Profile"));
profilePlot = new QCustomPlot(profileDialog);
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);
profileLayout->addWidget(profilePlot);
profileDialog->setLayout(profileLayout);
profileDialog->resize(400, 300);
connect(relativeProfileB, &QPushButton::clicked, profileDialog, &QDialog::show);
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"));
HFRPlot->setInteractions(QCP::iRangeZoom);
HFRPlot->setInteraction(QCP::iRangeDrag, true);
v_graph = HFRPlot->addGraph();
v_graph->setLineStyle(QCPGraph::lsNone);
v_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::red, 3));
}
void Focus::initConnections()
{
// How long do we wait until the user select a star?
waitStarSelectTimer.setInterval(AUTO_STAR_TIMEOUT);
connect(&waitStarSelectTimer, &QTimer::timeout, this, &Ekos::Focus::checkAutoStarTimeout);
connect(liveVideoB, &QPushButton::clicked, this, &Ekos::Focus::toggleVideo);
// Show FITS Image in a new window
showFITSViewerB->setIcon(QIcon::fromTheme("kstars_fitsviewer"));
showFITSViewerB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
connect(showFITSViewerB, &QPushButton::clicked, this, &Ekos::Focus::showFITSViewer);
// Toggle FITS View to full screen
toggleFullScreenB->setIcon(QIcon::fromTheme("view-fullscreen"));
toggleFullScreenB->setShortcut(Qt::Key_F4);
toggleFullScreenB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
connect(toggleFullScreenB, &QPushButton::clicked, this, &Ekos::Focus::toggleFocusingWidgetFullScreen);
// How long do we wait until an exposure times out and needs a retry?
captureTimeout.setSingleShot(true);
connect(&captureTimeout, &QTimer::timeout, this, &Ekos::Focus::processCaptureTimeout);
// Start/Stop focus
connect(startFocusB, &QPushButton::clicked, this, &Ekos::Focus::start);
connect(stopFocusB, &QPushButton::clicked, this, &Ekos::Focus::checkStopFocus);
// Focus IN/OUT
connect(focusOutB, &QPushButton::clicked, [&]()
{
focusOut();
});
connect(focusInB, &QPushButton::clicked, [&]()
{
focusIn();
});
// Capture a single frame
connect(captureB, &QPushButton::clicked, this, &Ekos::Focus::capture);
// Start continuous capture
connect(startLoopB, &QPushButton::clicked, this, &Ekos::Focus::startFraming);
// Use a subframe when capturing
connect(useSubFrame, &QCheckBox::toggled, this, &Ekos::Focus::toggleSubframe);
// Reset frame dimensions to default
connect(resetFrameB, &QPushButton::clicked, this, &Ekos::Focus::resetFrame);
// Sync setting if full field setting is toggled.
connect(useFullField, &QCheckBox::toggled, [&](bool toggled)
{
fullFieldInnerRing->setEnabled(toggled);
fullFieldOuterRing->setEnabled(toggled);
if (toggled)
{
useSubFrame->setChecked(false);
useAutoStar->setChecked(false);
}
else
{
// Disable the overlay
focusView->setStarFilterRange(0, 1);
}
});
// Sync settings if the CCD selection is updated.
connect(CCDCaptureCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::checkCCD);
// Sync settings if the Focuser selection is updated.
connect(focuserCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::checkFocuser);
// Sync settings if the filter selection is updated.
connect(FilterDevicesCombo, static_cast(&QComboBox::activated), this, &Ekos::Focus::checkFilter);
// Set focuser absolute position
connect(startGotoB, &QPushButton::clicked, this, &Ekos::Focus::setAbsoluteFocusTicks);
connect(stopGotoB, &QPushButton::clicked, [this]()
{
if (currentFocuser)
currentFocuser->stop();
});
// Update the focuser box size used to enclose a star
connect(focusBoxSize, static_cast(&QSpinBox::valueChanged), this, &Ekos::Focus::updateBoxSize);
// Update the focuser star detection if the detection algorithm selection changes.
connect(focusDetectionCombo, static_cast(&QComboBox::activated), this, [&](int index)
{
focusDetection = static_cast(index);
thresholdSpin->setEnabled(focusDetection == ALGORITHM_THRESHOLD);
});
// Update the focuser solution algorithm if the selection changes.
connect(focusAlgorithmCombo, static_cast(&QComboBox::activated), this, [&](int index)
{
focusAlgorithm = static_cast(index);
});
// Reset star center on auto star check toggle
connect(useAutoStar, &QCheckBox::toggled, this, [&](bool enabled)
{
if (enabled)
{
starCenter = QVector3D();
starSelected = false;
focusView->setTrackingBox(QRect());
}
});
}
void Focus::initView()
{
focusView = new FITSView(focusingWidget, FITS_FOCUS);
focusView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
focusView->setBaseSize(focusingWidget->size());
focusView->createFloatingToolBar();
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->addWidget(focusView);
focusingWidget->setLayout(vlayout);
connect(focusView, &FITSView::trackingStarSelected, this, &Ekos::Focus::focusStarSelected, Qt::UniqueConnection);
focusView->setStarsEnabled(true);
}
}
diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg
index cbd5e5476..6e481a8f8 100644
--- a/kstars/kstars.kcfg
+++ b/kstars/kstars.kcfg
@@ -1,2431 +1,2435 @@
ksutils.hThe screen coordinates of the Time InfoBox.QPoint(0,0)The screen coordinates of the Focus InfoBox.QPoint(600,0)The screen coordinates of the Geographic Location InfoBox.QPoint(0,600)If true, the Time InfoBox will show only its top line of data.trueIf true, the Focus InfoBox will show only its top line of data.trueIf true, the Geographic Location InfoBox will show only its top line of data.trueToggles display of all three InfoBoxes.trueToggles display of the Time InfoBox.trueToggles display of the Focus InfoBox.trueToggles display of the Geographic Location InfoBox.trueIs the Time InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges.003Is the Focus InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges.103Is the Geographic Location InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges.203Toggle display of the status bar.trueToggle display of the Horizontal coordinates of the mouse cursor in the status bar.trueToggle display of the Equatorial coordinates of the mouse cursor at the current epoch in the status bar.trueToggle display of the Equatorial coordinates of the mouse cursor at the standard epoch in the status bar.falsetrue1024768trueBlack BodyList of the filenames of custom object catalogs.List of integers toggling display of each custom object catalog (any nonzero value indicates the objects in that catalog will be displayed).List of names for which custom catalogs are to be displayed.Names of objects entered into the find dialog are resolved using online services and stored in the database. This option also toggles the display of such resolved objects on the sky map.true800600truetruefalseToggle display of crosshairs centered at telescope's pointed position in the KStars sky map.trueToggle display of INDI messages in the KStars statusbar.true
+
+
+ false
+ Show INDI messages as desktop notifications instead of dialogs.falsetruefalsefalseThe default location of saved FITS filesKSUtils::getDefaultPath("fitsDir")INDI server will attempt to bind with ports starting from this port7624INDI server will attempt to bind with ports ending with this port9000List of the aliases for filter wheel slots.PATH to indiserver binaryKSUtils::getDefaultPath("indiServer")falsePATH to indi drivers directoryKSUtils::getDefaultPath("indiDriversDir")false320240falsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalseThe City name of the current geographic location.GreenwichThe Province name of the current geographic location. This is the name of the state for locations in the U. S.The Country name of the current geographic location.United KingdomThe longitude of the current geographic location, in decimal degrees.0.0The latitude of the current geographic location, in decimal degrees.51.468-10.00.0Two-letter code that determines the dates on which daylight savings time begins and ends (you can view the rules by pressing the "Explain DST Rules" button in the Geographic Location window).--If true, focus changes will cause the sky to visibly spin to the new position. Otherwise, the display will "snap" instantly to the new position.trueIf true, clicking on the skymap will select the closest object and highlights it.falseType of cursor when exploring the sky map.1The names of the currently selected field-of-view indicators. The list of defined FOV indicator names is listed in the "Settings|FOV Symbols" menu.TelradIf true, trails attached to solar system bodies will fade into the background sky color.trueThe right ascension of the initial focus position of the sky map, in decimal hours. This value is volatile; it is reset whenever the program shuts down.180.0The declination of the initial focus position of the sky map, in decimal degrees. This value is volatile; it is reset whenever the program shuts down.45.0The name of the object that should be centered and tracked on startup. If no object should be centered, set to "nothing". This value is volatile; it is reset whenever the program shuts down.nothingTrue if the skymap should track on its initial position on startup. This value is volatile; it is reset whenever the program shuts down.falseToggle whether KStars should hide some objects while the display is moving, for smoother motion.trueToggle whether constellation boundaries are hidden while the display is in motion.trueToggle whether constellation lines are hidden while the display is in motion.falseChoose sky culture.11Toggle whether constellation names are hidden while the display is in motion.falseToggle whether the coordinate grids are hidden while the display is in motion.trueToggle whether the Milky Way contour is hidden while the display is in motion.trueToggle whether IC objects are hidden while the display is in motion.trueToggle whether Messier objects are hidden while the display is in motion.trueToggle whether NGC objects are hidden while the display is in motion.trueToggle whether extra objects are hidden while the display is in motion.trueToggle whether solar system objects are hidden while the display is in motion.falseToggle whether faint stars are hidden while the display is in motion.trueToggle whether name labels are hidden while the display is in motion.trueToggle whether asteroids are drawn in the sky map.trueToggle whether asteroid name labels are drawn in the sky map.falsetrueToggle whether comets are drawn in the sky map.trueToggle whether comet comas are drawn in the sky map.trueToggle whether comet name labels are drawn in the sky map.falseToggle whether supernovae are drawn in the sky map.falseToggle whether supernova name labels are drawn in the sky map.falseSet magnitude limit for supernovae to be shown on the skymap.16Toggle supernova alerts.trueSet magnitude limit for supernovae to be alerted.13Toggle whether constellation boundaries are drawn in the sky map.falseToggle whether constellation boundary containing the central focus point is highlighted in the sky map.falseToggle whether constellation lines are drawn in the sky map.falseToggle whether constellation art drawn in the sky map.falseToggle whether constellation name labels are drawn in the sky map.falseToggle whether deep-sky objects are drawn in the sky map.trueToggle whether the ecliptic line is drawn in the sky map.falseToggle whether the equator line is drawn in the sky map.falseCoordinate grids will automatically change according to active coordinate system.trueToggle whether the equatorial coordinate grid is drawn in the sky map.falseToggle whether the horizontal coordinate grid is drawn in the sky map.falseToggle whether the local meridian line is drawn in the sky map.falseToggle whether the region below the horizon is opaque.trueToggle whether the horizon line is drawn in the sky map.trueToggle whether flags are drawn in the sky map.trueToggle whether IC objects are drawn in the sky map.falseToggle whether NGC objects are drawn in the sky map.trueToggle whether Messier objects are drawn in the sky map.trueToggle whether Messier objects are rendered as images in the sky map.trueToggle whether extra objects are drawn in the sky map.trueToggle whether the Milky Way contour is drawn in the sky map.trueToggle whether the Milky Way contour is filled. When this option is false, the Milky Way is shown as an outline.trueMeta-option to control whether all major planets (and the Sun and Moon) are drawn in the sky map.trueToggle whether major planets (and the Sun and Moon) are rendered as images in the sky map.trueToggle whether major planets (and the Sun and Moon) are labeled in the sky map.trueToggle whether the Sun is drawn in the sky map.trueToggle whether the Moon is drawn in the sky map.trueToggle whether Mercury is drawn in the sky map.trueToggle whether Venus is drawn in the sky map.trueToggle whether Mars is drawn in the sky map.trueToggle whether Jupiter is drawn in the sky map.trueToggle whether Saturn is drawn in the sky map.trueToggle whether Uranus is drawn in the sky map.trueToggle whether Neptune is drawn in the sky map.trueToggle whether Pluto is drawn in the sky map.trueToggle whether stars are drawn in the sky map.trueToggle whether star magnitude (brightness) labels are shown in the sky map.falseToggle whether star name labels are shown in the sky map.trueToggle whether deep-sky object magnitude (brightness) labels are shown in the sky map.falseToggle whether deep-sky object name labels are shown in the sky map.falseThe timescale above which slewing mode is forced on at all times.60The background fill mode for the on-screen information boxes: 0="no BG"; 1="semi-transparent BG"; 2="opaque BG"1Algorithm for the mapping projection.0Use official IAU abbreviations for constellation names.falseUse Latin constellation names.falseUse localized constellation names (if localized names are not available, default to Latin names).trueDisplay the sky with horizontal coordinates (when false, equatorial coordinates will be used).trueToggle whether a centered object automatically gets a name label attached.trueToggle whether a centered solar system object automatically gets a trail attached, as long as it remains centered.trueToggle whether the object under the mouse cursor gets a transient name label.trueToggle whether object positions are corrected for the effects of atmospheric refraction (only applies when horizontal coordinates are used).trueToggle whether corrections due to bending of light around the sun are taken into accountfalseToggle whether the sky is rendered using antialiasing. Lines and shapes are smoother with antialiasing, but rendering the screen will take more time.trueThe zoom level, measured in pixels per radian.250.250.5000000.When zooming in or out, change zoom speed factor by this multiplier.0.20.011.0The faint magnitude limit for drawing asteroids.15.0The maximum magnitude (visibility) to filter the asteroid data download from JPL.12.000Controls the relative number of asteroid name labels drawn in the map.4.0The faint magnitude limit for drawing deep-sky objects, when fully zoomed in.16.0The faint magnitude limit for drawing deep-sky objects, when fully zoomed out.5.0When enabled, objects whose magnitudes are unknown, or not available to KStars, are drawn irrespective of the faint limits set.trueSets the density of stars in the field of view5The faint magnitude limit for drawing stars, when the map is in motion (only applicable if faint stars are set to be hidden while the map is in motion).5.0The relative density for drawing star name and magnitude labels.2.0The relative density for drawing deep-sky object name and magnitude labels.5.0If true, long names (common names) for deep-sky objects are shown in the labels.falseThe maximum solar distance for drawing comets.3.0Use experimental OpenGL backend (deprecated).falseThe state of the clock (running or not)trueObjects in the observing list will be highlighted with a symbol in the map.trueObjects in the observing list will be highlighted with a colored name label in the map.falseThe observing list will prefer DSS imagery while downloading imagery.trueThe observing list will prefer SDSS imagery while downloading imagery.falseCheck this if you use a large Dobsonian telescope. Sorting by percentage current altitude is an easy way of determining what objects are well-placed for observation. However, when using a large Dobsonian telescope, objects close to the zenith are hard to observe. Since tracking there corresponds to a rotation in azimuth, it is both counterintuitive and requires the observer to frequently move the ladder. The region around the zenith where this is particularly frustrating is called the Dobsonian hole. This checkbox makes the observing list consider objects present in the hole as unfit for observation.falseThis specifies the angular radius of the Dobsonian hole, i.e. the region where a large Dobsonian telescope cannot be pointed easily.15.0040.00The name of the color schememoonless-night.colorsThe method for rendering stars: 0="realistic colors"; 1="solid red"; 2="solid black"; 3="solid white"; 4="solid real colors"04The color saturation level of stars (only applicable when using "realistic colors" mode).610The color for the angular-distance measurement ruler.#FFFThe background color of the on-screen information boxes.#000The text color for the on-screen information boxes, when activated by a mouse click.#F00The normal text color of the on-screen information boxes.#FFFThe color for the constellation boundary lines.#222The color for the constellation boundary lines.#222The color for the constellation figure lines.#555The color for the constellation names.#AA7The color for the cardinal compass point labels.#002The color for the ecliptic line.#663The color for the equator line.#FFFThe color for the equatorial coordinate grid lines.#456The color for the horizontal coordinate grid lines.#5A3The color for objects which have extra URL links available.#A00The color for the horizon line and opaque ground.#5A3The color for the local meridian line.#0059b3The color for Messier object symbols.#0F0The color for NGC object symbols.#066The color for IC object symbols.#439The color for the Milky Way contour.#123The color for star name labels.#7AAThe color for deep-sky object name labels.#7AAThe color for solar system object labels.#439The color for solar system object trails.#963The color for the sky background.#002The color for the artificial horizon region.#C82828The color for telescope target symbols.#8B8Color of visible satellites.#00FF00Color of invisible satellites.#FF0000Color of satellites labels.#640000Color of supernova#FFA500The color for user-added object labels.#439The color for RA Guide Error bar in Ekos guide module.#00FF00The color for DEC Guide Error bar in Ekos guide module.#00A5FFThe color for solver FOV box in Ekos alignment module.#FFFF00falseXplanet binary pathKSUtils::getDefaultPath("XplanetPath")Option to use a FIFO file instead of saving to the hard disktrueHow long to wait for XPlanet before giving up in milliseconds1000How long to pause between frames in the XPlanet Animation100Width of xplanet window640Height of xplanet window480If true, display a label in the upper right corner.falseShow local time.trueShow GMT instead of local time.falseSpecify the text of the first line of the label. By default, it says something like "Looking at Earth". Any instances of %t will be replaced by the target name, and any instances of %o will be replaced by the origin name.Specify the point size.12Set the color for the label.#F00Specify the format for the date/time label. This format string is passed to strftime(3). The default is "%c %Z", which shows the date, time, and time zone in the locale’s appropriate date and time representation.%c %ZfalsetruefalsefalseDraw a glare around the sun with a radius of the specified value larger than the Sun. The default value is 28.28Place the observer above a random latitude and longitudefalsePlace the observer above the specified longitude and latitudetrueRender the target body as seen from above the specified latitude (in degrees). The default value is 0.0Place the observer above the specified longitude (in degrees). Longitude is positive going east, negative going west (for the earth and moon), so for example Los Angeles is at -118 or 242. The default value is 0.0The default is no projection. Multiple bodies will not be shown if this option is specified, although shadows will still be drawn.0Use a file as the background image, with the planet to be superimposed upon it. This option is only meaningful with the -projection option. A color may also be supplied.falseUse a file as the background image.falseThe path of the background image.Use a color as the background.trueThe color of the background.#000A star of the specified magnitude will have a pixel brightness of 1. The default value is 10. Stars will be drawn more brightly if this number is larger.10If checked, use an arc file to be plotted against the background stars.falseSpecify an arc file to be plotted against the background stars.If checked, use a config file.falseUse the specified configuration file.If checked, use kstars's FOV.falseIf checked, use the specified marker file.falseSpecify a file containing user-defined marker data to display against the background stars.If checked, write coordinates of the bounding box for each marker in a file.falseWrite coordinates of the bounding box for each marker to this file.If checked, use star map file to draw the background stars.falseStar map file pathThis option is only used when creating JPEG images. The quality can range from 0 to 100. The default value is 80.80Toggle whether satellite tracks are drawn in the sky map.falseToggle whether satellite tracks are drawn in the sky map.falseIf selected, satellites will be draw like stars, otherwise, draw satellites as small colored square.falseToggle whether satellite labels are drawn in the sky map.falseList of selected satellites.Checking this option causes recomputation of current equatorial coordinates from catalog coordinates (i.e. application of precession, nutation and aberration corrections) for every redraw of the map. This makes processing slower when there are many stars to handle, but is more likely to be bug free. There are known bugs in the rendering of stars when this recomputation is avoided.falseThe default size for DSS images downloaded from the Internet.15.0To include parts of the star field, we add some extra padding around DSS images of deep-sky objects. This option configures the total (both sides) padding added to either dimension of the field.10.0Checking this option causes KStars to generate verbose debug information for diagnostic purposes. This may cause slowdown of KStars.falseChecking this option causes KStars to generate regular debug information.trueChecking this option causes KStars to stop generating ANY debug information.falseChecking this option causes KStars log debug messages to the default output used by the platform (e.g. Standard Error).trueChecking this option causes KStars log debug messages to a log file as specified.falseLog FITS Data activity.falseLog INDI devices activity.falseLog Ekos Capture Module activity.falseLog Ekos Focus Module activity.falseLog Ekos Guide Module activity.falseLog Ekos Alignment Module activity.falseLog Ekos Mount Module activity.falseLog Ekos Observatory Module activity.falsetrueDisplay all captured FITS images in a single tab instead of multiple tabs per image.trueDisplay all captured FITS images in a single FITS Viewer window. By default each camera create its own FITS Viewer instancefalseDisplay all opened FITS images in a single FITS Viewer window.trueBring the FITSViewer window to the foreground when receiving a new image.truefalse!KSUtils::isHardwareLimited()false!KSUtils::isHardwareLimited()!KSUtils::isHardwareLimited()KSUtils::isHardwareLimited()4falsefalse40.00600600truefalsetruetruetruefalseSimulatorsfalsetruefalsetrue1Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop.0Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop.90.0falseIf the target hour angle exceeds this value, Ekos will command a meridian flip and if successful it will resume guiding and capture operations.falsetruefalsefalsefalse3:00 AMfalse10If guide deviation exceeds this limit, the exposure will be automatically aborted and only resumed when the deviation is within this limit.2If HFR deviation exceeds this limit, the autofocus routine will be automatically started.0.5falsefalsefalseSets the time interval before forced autofocus attempts during a capture sequence.60falseIf set, Ekos will capture a few flat images to determine the optimal exposure time to achieve the desired ADU value.0Maximum difference between measured and target ADU values to deem the value as acceptable.100000000.10falsefalse2.5truefalse130true!KSUtils::isHardwareLimited()!KSUtils::isHardwareLimited()0KSUtils::getDefaultPath("fitsDir")60falsefalsefalseStep size of the absolute focuser. The step size TICKS should be adjusted so that when the focuser moves TICKS steps, the difference in HFR is more than 0.1 pixels. Lower the value when you are close to optimal focus.100Wait for this many seconds after moving the focuser before capturing the next image during AutoFocus.0Wait for this many seconds after resuming guide.0The tolerance specifies the percentage difference between the current focusing position and the minimum obtained during the focusing run. Adjustment of this value is necessary to prevent the focusing algorithm from oscillating back and forth.1Set the maximum travel distance of an absolute focuser.10000Specifies gain value of CCD when performing focusing if supported by camera.0Set box size to select a focus star.64Set horizontal binning of CCD camera while in focus mode.1Set vertical binning of CCD camera while in focus mode.1truefalseDuring full field focusing, stars which are inside this percentage of the frame are filtered out of HFR calculation (default 0%). Detection algorithms may also have an inherent filter.0.0During full field focusing, stars which are outside this percentage of the frame are filtered out of HFR calculation (default 100%). Detection algorithms may also have an inherent filter.100.0falsetruefalse0150001Specifies exposure value of CCD in seconds when performing plate solving.1Set binning index of CCD camera while in alignment mode. Default values 0-3 corresponding to 1x1 to 4x4 binning. 4 is max binning.4Use rotator when performing load and slew.falseThreshold between measured and FITS position angles in arcminutes to consider the load and slew operation successful.30001truefalsefalse300false1500falsetruetruetruetrue1true2truetruetrue30falsePath to astrometry.net solver location.KSUtils::getDefaultPath("AstrometrySolverBinary")falsePath to astrometry.net wcsinfo location.KSUtils::getDefaultPath("AstrometryWCSInfo")falsePath to astrometry.net file location.KSUtils::getDefaultPath("AstrometryConfFile")truetrueFolder in which the desired python executable or link to be used for astrometry.net resides./usr/local/opt/python/libexec/binKey to access astrometry.net online web services. You must register with astrometry.net to obtain a key.iczikaqstszeptgshttp://nova.astrometry.nettrue180-1true1.000localhost4400localhost5656010002false1falsefalsefalse36010truefalsefalsefalse21014510500falsefalsefalse2truetruetruetruetruetrue133.33133.330000500050001001000.52truetruefalsefalseLog Ekos Scheduler Module activity.falseSort scheduler jobs by priority and altitude.truetruefalsefalsefalsefalsetruefalse2true530300000010false762486243001000NonefalsefalseToggle whether the HIPS sources are drawn in the sky map.falsetruetruefalsefalsetrue600truetruetrue30truetruetrue