11 #include "alignadaptor.h"
12 #include "alignview.h"
13 #include <ekos_align_debug.h>
18 #include "opsprograms.h"
19 #include "opsastrometry.h"
20 #include "opsastrometryindexfiles.h"
23 #include "mountmodel.h"
24 #include "polaralignmentassistant.h"
25 #include "remoteastrometryparser.h"
26 #include "manualrotator.h"
29 #include "fitsviewer/fitsdata.h"
32 #include "auxiliary/QProgressIndicator.h"
33 #include "auxiliary/ksmessagebox.h"
34 #include "ekos/auxiliary/stellarsolverprofileeditor.h"
35 #include "ekos/auxiliary/profilesettings.h"
36 #include "ekos/auxiliary/opticaltrainmanager.h"
37 #include "ekos/auxiliary/opticaltrainsettings.h"
38 #include "ksnotification.h"
42 #include "kstarsdata.h"
43 #include "skymapcomposite.h"
46 #include "ekos/manager.h"
47 #include "indi/clientmanager.h"
48 #include "indi/driverinfo.h"
49 #include "indi/indifilterwheel.h"
50 #include "profileinfo.h"
53 #include <KActionCollection>
54 #include <basedevice.h>
61 #define MAXIMUM_SOLVER_ITERATIONS 10
62 #define CAPTURE_RETRY_DELAY 10000
63 #define PAH_CUTOFF_FOV 10 // Minimum FOV width in arcminutes for PAH to work
64 #define CHECK_PAH(x) \
65 m_PolarAlignmentAssistant && m_PolarAlignmentAssistant->x
67 if (m_PolarAlignmentAssistant) m_PolarAlignmentAssistant->x
74 bool isEqual(
double a,
double b)
76 return std::abs(a - b) < 0.01;
83 qRegisterMetaType<Ekos::AlignState>(
"Ekos::AlignState");
84 qDBusRegisterMetaType<Ekos::AlignState>();
86 new AlignAdaptor(
this);
91 KStarsData::Instance()->clearTransientFOVs();
93 solverFOV.reset(
new FOV());
94 solverFOV->setName(
i18n(
"Solver FOV"));
95 solverFOV->setObjectName(
"solver_fov");
96 solverFOV->setLockCelestialPole(
true);
97 solverFOV->setColor(
KStars::Instance()->data()->colorScheme()->colorNamed(
"SolverFOVColor").name());
98 solverFOV->setProperty(
"visible",
false);
101 sensorFOV.reset(
new FOV());
102 sensorFOV->setObjectName(
"sensor_fov");
103 sensorFOV->setLockCelestialPole(
true);
104 sensorFOV->setProperty(
"visible", Options::showSensorFOV());
111 showFITSViewerB->setIcon(
116 toggleFullScreenB->setIcon(
120 connect(toggleFullScreenB, &
QPushButton::clicked,
this, &Ekos::Align::toggleAlignWidgetFullScreen);
122 m_AlignView.reset(
new AlignView(alignWidget, FITS_ALIGN));
124 m_AlignView->setBaseSize(alignWidget->size());
125 m_AlignView->createFloatingToolBar();
129 alignWidget->setLayout(vlayout);
142 gotoModeButtonGroup->setId(syncR, GOTO_SYNC);
143 gotoModeButtonGroup->setId(slewR, GOTO_SLEW);
144 gotoModeButtonGroup->setId(nothingR, GOTO_NOTHING);
146 m_CurrentGotoMode =
static_cast<GotoMode
>(Options::solverGotoOption());
147 gotoModeButtonGroup->button(m_CurrentGotoMode)->setChecked(
true);
149 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
151 [ = ](
int id,
bool toggled)
154 [ = ](
int id,
bool toggled)
158 this->m_CurrentGotoMode =
static_cast<GotoMode
>(
id);
161 m_CaptureTimer.setSingleShot(
true);
162 m_CaptureTimer.setInterval(CAPTURE_RETRY_DELAY);
163 connect(&m_CaptureTimer, &
QTimer::timeout,
this, &Align::processCaptureTimeout);
164 m_AlignTimer.setSingleShot(
true);
165 m_AlignTimer.setInterval(Options::astrometryTimeout() * 1000);
166 connect(&m_AlignTimer, &
QTimer::timeout,
this, &Ekos::Align::checkAlignmentTimeout);
169 stopLayout->addWidget(pi.get());
171 rememberSolverWCS = Options::astrometrySolverWCS();
172 rememberAutoWCS = Options::autoWCS();
174 solverModeButtonGroup->setId(localSolverR, SOLVER_LOCAL);
175 solverModeButtonGroup->setId(remoteSolverR, SOLVER_REMOTE);
177 setSolverMode(Options::solverMode());
179 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
181 [ = ](
int id,
bool toggled)
184 [ = ](
int id,
bool toggled)
199 connect(
this, &Align::newStatus,
this, [
this](
AlignState state)
208 for (
auto &button : qButtons)
209 button->setAutoDefault(
false);
211 savedOptionsProfiles =
QDir(KSPaths::writableLocation(
213 if(
QFile(savedOptionsProfiles).exists())
214 m_StellarSolverProfiles = StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles);
216 m_StellarSolverProfiles = getDefaultAlignOptionsProfiles();
218 m_StellarSolver.reset(
new StellarSolver());
219 connect(m_StellarSolver.get(), &StellarSolver::logOutput,
this, &Align::appendLogText);
221 setupPolarAlignmentAssistant();
222 setupManualRotator();
223 setuptDarkProcessor();
225 setupSolutionTable();
229 loadGlobalSettings();
232 setupOpticalTrainManager();
237 if (m_StellarSolver.get() !=
nullptr)
238 disconnect(m_StellarSolver.get(), &StellarSolver::logOutput,
this, &Align::appendLogText);
240 if (alignWidget->parent() ==
nullptr)
241 toggleAlignWidgetFullScreen();
248 for (
auto &dirFile : dir.entryList())
251 void Align::selectSolutionTableRow(
int row,
int column)
255 solutionTable->selectRow(row);
256 for (
int i = 0; i < alignPlot->itemCount(); i++)
261 QCPItemText *item = qobject_cast<QCPItemText *>(abstractItem);
280 void Align::handleHorizontalPlotSizeChange()
282 alignPlot->xAxis->setScaleRatio(alignPlot->yAxis, 1.0);
286 void Align::handleVerticalPlotSizeChange()
288 alignPlot->yAxis->setScaleRatio(alignPlot->xAxis, 1.0);
294 if (
event->oldSize().width() != -1)
297 handleHorizontalPlotSizeChange();
299 handleVerticalPlotSizeChange();
312 QCPItemText *label = qobject_cast<QCPItemText *>(item);
315 QString labelText = label->text();
316 int point = labelText.
toInt() - 1;
323 "<th colspan=\"2\">Object %1: %2</th>"
326 "<td>RA:</td><td>%3</td>"
329 "<td>DE:</td><td>%4</td>"
332 "<td>dRA:</td><td>%5</td>"
335 "<td>dDE:</td><td>%6</td>"
339 solutionTable->item(point, 2)->text(),
340 solutionTable->item(point, 0)->text(),
341 solutionTable->item(point, 1)->text(),
342 solutionTable->item(point, 4)->text(),
343 solutionTable->item(point, 5)->text()),
344 alignPlot, alignPlot->rect());
349 void Align::buildTarget()
351 double accuracyRadius = alignAccuracyThreshold->value();
354 concentricRings->
data()->clear();
355 redTarget->
data()->clear();
356 yellowTarget->
data()->clear();
357 centralTarget->
data()->clear();
361 concentricRings =
new QCPCurve(alignPlot->xAxis, alignPlot->yAxis);
362 redTarget =
new QCPCurve(alignPlot->xAxis, alignPlot->yAxis);
363 yellowTarget =
new QCPCurve(alignPlot->xAxis, alignPlot->yAxis);
364 centralTarget =
new QCPCurve(alignPlot->xAxis, alignPlot->yAxis);
366 const int pointCount = 200;
373 int circleRingPt = 0;
374 for (
int i = 0; i < pointCount; i++)
376 double theta = i /
static_cast<double>(pointCount) * 2 * M_PI;
378 for (
double ring = 1; ring < 8; ring++)
380 if (ring != 4 && ring != 6)
382 if (i % (9 -
static_cast<int>(ring)) == 0)
384 circleRings[circleRingPt] =
QCPCurveData(circleRingPt, accuracyRadius * ring * 0.25 * qCos(theta),
385 accuracyRadius * ring * 0.25 * qSin(theta));
391 circleCentral[i] =
QCPCurveData(i, accuracyRadius * qCos(theta), accuracyRadius * qSin(theta));
392 circleYellow[i] =
QCPCurveData(i, accuracyRadius * 1.5 * qCos(theta), accuracyRadius * 1.5 * qSin(theta));
393 circleRed[i] =
QCPCurveData(i, accuracyRadius * 2 * qCos(theta), accuracyRadius * 2 * qSin(theta));
400 concentricRings->
data()->set(circleRings,
true);
401 redTarget->
data()->set(circleRed,
true);
402 yellowTarget->
data()->set(circleYellow,
true);
403 centralTarget->
data()->set(circleCentral,
true);
416 if (alignPlot->size().width() > 0)
420 void Align::slotAutoScaleGraph()
422 double accuracyRadius = alignAccuracyThreshold->value();
423 alignPlot->xAxis->setRange(-accuracyRadius * 3, accuracyRadius * 3);
424 alignPlot->yAxis->setRange(-accuracyRadius * 3, accuracyRadius * 3);
426 alignPlot->xAxis->setScaleRatio(alignPlot->yAxis, 1.0);
432 void Align::slotClearAllSolutionPoints()
434 if (solutionTable->rowCount() == 0)
442 solutionTable->setRowCount(0);
443 alignPlot->graph(0)->data()->clear();
444 alignPlot->clearItems();
447 slotAutoScaleGraph();
451 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to clear all of the solution points?"),
452 i18n(
"Clear Solution Points"), 60);
455 void Align::slotRemoveSolutionPoint()
457 auto abstractItem = alignPlot->item(solutionTable->currentRow());
460 auto item = qobject_cast<QCPItemText *>(abstractItem);
463 double point = item->position->key();
464 alignPlot->graph(0)->data()->remove(point);
468 alignPlot->removeItem(solutionTable->currentRow());
470 for (
int i = 0; i < alignPlot->itemCount(); i++)
472 auto oneItem = alignPlot->item(i);
475 auto item = qobject_cast<QCPItemText *>(oneItem);
480 solutionTable->removeRow(solutionTable->currentRow());
484 void Align::slotMountModel()
488 m_MountModel =
new MountModel(
this);
490 connect(m_MountModel, &Ekos::MountModel::aborted,
this, [
this]()
492 if (m_Mount && m_Mount->isSlewing())
499 m_MountModel->show();
519 void Align::checkAlignmentTimeout()
521 if (m_SolveFromFile || ++solverIterations == MAXIMUM_SOLVER_ITERATIONS)
525 appendLogText(
i18n(
"Solver timed out."));
526 parser->stopSolver();
528 setAlignTableResult(ALIGN_RESULT_FAILED);
536 if (
sender() ==
nullptr && mode >= 0 && mode <= 1)
538 solverModeButtonGroup->button(mode)->setChecked(
true);
541 Options::setSolverMode(mode);
543 if (mode == SOLVER_REMOTE)
545 if (remoteParser.get() !=
nullptr && m_RemoteParserDevice !=
nullptr)
547 parser = remoteParser.get();
553 parser = remoteParser.get();
558 parser->setAlign(
this);
572 return m_Camera->getDeviceName();
603 auto targetChip = m_Camera->getChip(ISD::CameraChip::PRIMARY_CCD);
604 if (targetChip ==
nullptr || (targetChip && targetChip->isCapturing()))
607 if (solverModeButtonGroup->checkedId() == SOLVER_REMOTE && remoteParser.get() !=
nullptr)
617 if (m_Camera && m_Camera == device)
630 connect(m_Camera, &ISD::ConcreteDevice::Connected,
this, [
this]()
632 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
633 controlBox->setEnabled(isConnected);
634 gotoBox->setEnabled(isConnected);
635 plateSolverOptionsGroup->setEnabled(isConnected);
636 tabWidget->setEnabled(isConnected);
638 connect(m_Camera, &ISD::ConcreteDevice::Disconnected,
this, [
this]()
640 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
641 controlBox->setEnabled(isConnected);
642 gotoBox->setEnabled(isConnected);
643 plateSolverOptionsGroup->setEnabled(isConnected);
644 tabWidget->setEnabled(isConnected);
646 opticalTrainCombo->setEnabled(
true);
647 trainLabel->setEnabled(
true);
651 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
652 controlBox->setEnabled(isConnected);
653 gotoBox->setEnabled(isConnected);
654 plateSolverOptionsGroup->setEnabled(isConnected);
655 tabWidget->setEnabled(isConnected);
667 if (m_Mount && m_Mount == device)
680 connect(m_Mount, &ISD::ConcreteDevice::Connected,
this, [
this]()
682 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
683 controlBox->setEnabled(isConnected);
684 gotoBox->setEnabled(isConnected);
685 plateSolverOptionsGroup->setEnabled(isConnected);
686 tabWidget->setEnabled(isConnected);
688 connect(m_Mount, &ISD::ConcreteDevice::Disconnected,
this, [
this]()
690 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
691 controlBox->setEnabled(isConnected);
692 gotoBox->setEnabled(isConnected);
693 plateSolverOptionsGroup->setEnabled(isConnected);
694 tabWidget->setEnabled(isConnected);
696 opticalTrainCombo->setEnabled(
true);
697 trainLabel->setEnabled(
true);
701 auto isConnected = m_Camera && m_Camera->isConnected() && m_Mount && m_Mount->isConnected();
702 controlBox->setEnabled(isConnected);
703 gotoBox->setEnabled(isConnected);
704 plateSolverOptionsGroup->setEnabled(isConnected);
705 tabWidget->setEnabled(isConnected);
710 RUN_PAH(setCurrentTelescope(m_Mount));
713 connect(m_Mount, &ISD::Mount::Disconnected,
this, [
this]()
715 m_isRateSynced =
false;
724 if (m_Dome && m_Dome == device)
741 auto name = device->getDeviceName();
742 device->disconnect(
this);
745 if (m_Mount && m_Mount->getDeviceName() == name)
752 if (m_Dome && m_Dome->getDeviceName() == name)
759 if (m_Rotator && m_Rotator->getDeviceName() == name)
766 if (m_Camera && m_Camera->getDeviceName() == name)
775 if (m_FilterWheel && m_FilterWheel->getDeviceName() == name)
777 m_FilterWheel->disconnect(
this);
778 m_FilterWheel =
nullptr;
787 if (m_Mount ==
nullptr || m_Mount->isConnected() ==
false)
790 if (m_isRateSynced ==
false)
792 auto speed = m_Settings[
"PAHMountSpeed"];
793 auto slewRates = m_Mount->slewRates();
796 RUN_PAH(syncMountSpeed(speed.toString()));
798 else if (!slewRates.isEmpty())
800 RUN_PAH(syncMountSpeed(slewRates.last()));
803 m_isRateSynced = !slewRates.empty();
806 canSync = m_Mount->canSync();
808 if (canSync ==
false && syncR->isEnabled())
810 slewR->setChecked(
true);
811 appendLogText(
i18n(
"Mount does not support syncing."));
814 syncR->setEnabled(canSync);
816 if (m_FocalLength == -1 || m_Aperture == -1)
819 if (m_CameraPixelWidth != -1 && m_CameraPixelHeight != -1)
833 auto targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
834 Q_ASSERT(targetChip);
837 uint8_t bit_depth = 8;
838 targetChip->getImageInfo(m_CameraWidth, m_CameraHeight, m_CameraPixelWidth, m_CameraPixelHeight, bit_depth);
840 setWCSEnabled(Options::astrometrySolverWCS());
842 int binx = 1, biny = 1;
843 alignBinning->setEnabled(targetChip->canBin());
844 if (targetChip->canBin())
846 alignBinning->blockSignals(
true);
848 targetChip->getMaxBin(&binx, &biny);
849 alignBinning->clear();
851 for (
int i = 0; i < binx; i++)
852 alignBinning->addItem(
QString(
"%1x%2").arg(i + 1).arg(i + 1));
854 auto binning = m_Settings[
"alignBinning"];
855 if (binning.isValid())
856 alignBinning->setCurrentText(binning.toString());
858 alignBinning->blockSignals(
false);
863 int roiW = 0, roiH = 0;
864 targetChip->getFrameMinMax(
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &roiW,
nullptr, &roiH);
867 if ( (roiW > 0 && roiW < m_CameraWidth) || (roiH > 0 && roiH < m_CameraHeight))
869 m_CameraWidth = roiW;
870 m_CameraHeight = roiH;
873 if (m_CameraPixelWidth > 0 && m_CameraPixelHeight > 0 && m_FocalLength > 0 && m_Aperture > 0)
881 if (m_Camera ==
nullptr)
884 auto targetChip = m_Camera->getChip(ISD::CameraChip::PRIMARY_CCD);
885 if (targetChip ==
nullptr || (targetChip && targetChip->isCapturing()))
888 auto isoList = targetChip->getISOList();
891 if (isoList.isEmpty())
893 alignISO->setEnabled(
false);
897 alignISO->setEnabled(
true);
898 alignISO->addItems(isoList);
899 alignISO->setCurrentIndex(targetChip->getISOIndex());
903 if (m_Camera->hasGain())
905 double min, max, step, value;
906 m_Camera->getGainMinMaxStep(&min, &max, &step);
909 alignGainSpecialValue = min - step;
910 alignGain->setRange(alignGainSpecialValue, max);
911 alignGain->setSpecialValueText(
i18n(
"--"));
912 alignGain->setEnabled(
true);
913 alignGain->setSingleStep(step);
914 m_Camera->getGain(&value);
916 auto gain = m_Settings[
"alignGain"];
920 TargetCustomGainValue = gain.toDouble();
921 if (TargetCustomGainValue > 0)
922 alignGain->setValue(TargetCustomGainValue);
924 alignGain->setValue(alignGainSpecialValue);
926 alignGain->setReadOnly(m_Camera->getGainPermission() == IP_RO);
930 if (alignGain->value() > alignGainSpecialValue)
931 TargetCustomGainValue = alignGain->value();
935 alignGain->setEnabled(
false);
942 fov_scale = m_FOVPixelScale;
949 result << m_FOVWidth << m_FOVHeight << m_FOVPixelScale;
958 result << m_CameraWidth << m_CameraHeight << m_CameraPixelWidth << m_CameraPixelHeight;
967 result << m_FocalLength << m_Aperture;
976 auto reducedFocalLength = m_Reducer * m_FocalLength;
977 if (m_FocalRatio > 0)
981 fov_w = 3600 * 2 * atan(m_CameraWidth * (m_CameraPixelWidth / 1000.0) / (2 * reducedFocalLength)) /
dms::DegToRad;
982 fov_h = 3600 * 2 * atan(m_CameraHeight * (m_CameraPixelHeight / 1000.0) / (2 * reducedFocalLength)) /
dms::DegToRad;
987 fov_w = 206264.8062470963552 * m_CameraWidth * m_CameraPixelWidth / 1000.0 / reducedFocalLength;
988 fov_h = 206264.8062470963552 * m_CameraHeight * m_CameraPixelHeight / 1000.0 / reducedFocalLength;
992 fov_scale = (fov_w * (alignBinning->currentIndex() + 1)) / m_CameraWidth;
999 void Align::calculateEffectiveFocalLength(
double newFOVW)
1001 if (newFOVW < 0 || isEqual(newFOVW, m_FOVWidth))
1004 auto reducedFocalLength = m_Reducer * m_FocalLength;
1005 double new_focal_length = 0;
1007 if (m_FocalRatio > 0)
1008 new_focal_length = ((m_CameraWidth * m_CameraPixelWidth / 1000.0) / tan(newFOVW / 2)) / 2;
1010 new_focal_length = ((m_CameraWidth * m_CameraPixelWidth / 1000.0) * 206264.8062470963552) / (newFOVW * 60.0);
1011 double focal_diff = std::fabs(new_focal_length - reducedFocalLength);
1015 effectiveFocalLength = new_focal_length / m_Reducer;
1016 appendLogText(
i18n(
"Effective telescope focal length is updated to %1 mm.", effectiveFocalLength));
1020 void Align::calculateFOV()
1022 auto reducedFocalLength = m_Reducer * m_FocalLength;
1023 if (m_FocalRatio > 0)
1027 m_FOVWidth = 3600 * 2 * atan(m_CameraWidth * (m_CameraPixelWidth / 1000.0) / (2 * reducedFocalLength)) /
dms::DegToRad;
1028 m_FOVHeight = 3600 * 2 * atan(m_CameraHeight * (m_CameraPixelHeight / 1000.0) / (2 * reducedFocalLength)) /
dms::DegToRad;
1033 m_FOVWidth = 206264.8062470963552 * m_CameraWidth * m_CameraPixelWidth / 1000.0 / reducedFocalLength;
1034 m_FOVHeight = 206264.8062470963552 * m_CameraHeight * m_CameraPixelHeight / 1000.0 / reducedFocalLength;
1040 m_FOVPixelScale = (m_FOVWidth * (alignBinning->currentIndex() + 1)) / m_CameraWidth;
1044 m_FOVHeight /= 60.0;
1046 double calculated_fov_x = m_FOVWidth;
1047 double calculated_fov_y = m_FOVHeight;
1049 QString calculatedFOV = (
QString(
"%1' x %2'").
arg(m_FOVWidth, 0,
'f', 1).
arg(m_FOVHeight, 0,
'f', 1));
1052 if (m_FOVWidth < 1 || m_FOVWidth > 60 * 180 || m_FOVHeight < 1 || m_FOVHeight > 60 * 180)
1055 i18n(
"Warning! The calculated field of view (%1) is out of bounds. Ensure the telescope focal length and camera pixel size are correct.",
1060 FocalLengthOut->setText(
QString(
"%1 (%2)").arg(m_FocalLength, 0,
'f', 1).
1061 arg(effectiveFocalLength > 0 ? effectiveFocalLength : m_FocalLength, 0,
'f', 1));
1063 if (m_FocalRatio > 0)
1064 FocalRatioOut->setText(
QString(
"%1 (%2)").arg(m_FocalRatio, 0,
'f', 1).
1065 arg(effectiveFocalLength > 0 ? effectiveFocalLength / m_Aperture : m_FocalRatio, 0,
1068 else if (m_Aperture > 0)
1069 FocalRatioOut->setText(
QString(
"%1 (%2)").arg(m_FocalLength / m_Aperture, 0,
'f', 1).
1070 arg(effectiveFocalLength > 0 ? effectiveFocalLength / m_Aperture : m_FocalLength / m_Aperture, 0,
1072 ReducerOut->setText(
QString(
"%1x").arg(m_Reducer, 0,
'f', 2));
1074 if (effectiveFocalLength > 0)
1076 double focal_diff = std::fabs(effectiveFocalLength - m_FocalLength);
1078 FocalLengthOut->setStyleSheet(
"color:green");
1079 else if (focal_diff < 15)
1080 FocalLengthOut->setStyleSheet(
"color:yellow");
1082 FocalLengthOut->setStyleSheet(
"color:red");
1090 if (m_FOVWidth == 0)
1094 i18n(
"<p>Effective field of view size in arcminutes.</p><p>Please capture and solve once to measure the effective FOV or enter the values manually.</p><p>Calculated FOV: %1</p>",
1096 m_FOVWidth = calculated_fov_x;
1097 m_FOVHeight = calculated_fov_y;
1098 m_EffectiveFOVPending =
true;
1102 m_EffectiveFOVPending =
false;
1103 FOVOut->setToolTip(
i18n(
"<p>Effective field of view size in arcminutes.</p>"));
1106 solverFOV->setSize(m_FOVWidth, m_FOVHeight);
1107 sensorFOV->setSize(m_FOVWidth, m_FOVHeight);
1109 sensorFOV->setName(m_Camera->getDeviceName());
1111 FOVOut->setText(
QString(
"%1' x %2'").arg(m_FOVWidth, 0,
'f', 1).arg(m_FOVHeight, 0,
'f', 1));
1114 const bool fovOK = ((m_FOVWidth + m_FOVHeight) / 2.0) > PAH_CUTOFF_FOV;
1115 if (m_PolarAlignmentAssistant !=
nullptr)
1116 m_PolarAlignmentAssistant->setEnabled(fovOK);
1118 if (opsAstrometry->kcfg_AstrometryUseImageScale->isChecked())
1120 int unitType = opsAstrometry->kcfg_AstrometryImageScaleUnits->currentIndex();
1125 double fov_low = qMin(m_FOVWidth / 60, m_FOVHeight / 60);
1126 double fov_high = qMax(m_FOVWidth / 60, m_FOVHeight / 60);
1127 opsAstrometry->kcfg_AstrometryImageScaleLow->setValue(fov_low);
1128 opsAstrometry->kcfg_AstrometryImageScaleHigh->setValue(fov_high);
1130 Options::setAstrometryImageScaleLow(fov_low);
1131 Options::setAstrometryImageScaleHigh(fov_high);
1134 else if (unitType == 1)
1136 double fov_low = qMin(m_FOVWidth, m_FOVHeight);
1137 double fov_high = qMax(m_FOVWidth, m_FOVHeight);
1138 opsAstrometry->kcfg_AstrometryImageScaleLow->setValue(fov_low);
1139 opsAstrometry->kcfg_AstrometryImageScaleHigh->setValue(fov_high);
1141 Options::setAstrometryImageScaleLow(fov_low);
1142 Options::setAstrometryImageScaleHigh(fov_high);
1147 opsAstrometry->kcfg_AstrometryImageScaleLow->setValue(m_FOVPixelScale * 0.9);
1148 opsAstrometry->kcfg_AstrometryImageScaleHigh->setValue(m_FOVPixelScale * 1.1);
1151 Options::setAstrometryImageScaleLow(m_FOVPixelScale * 0.9);
1152 Options::setAstrometryImageScaleHigh(m_FOVPixelScale * 1.1);
1179 if (optionsMap.contains(
"noverify"))
1180 solver_args <<
"--no-verify";
1183 if (optionsMap.contains(
"resort"))
1184 solver_args <<
"--resort";
1187 if (optionsMap.contains(
"nofits2fits"))
1188 solver_args <<
"--no-fits2fits";
1191 if (optionsMap.contains(
"downsample"))
1192 solver_args <<
"--downsample" <<
QString::number(optionsMap.value(
"downsample", 2).toInt());
1195 if (optionsMap.contains(
"scaleL"))
1196 solver_args <<
"-L" <<
QString::number(optionsMap.value(
"scaleL").toDouble());
1199 if (optionsMap.contains(
"scaleH"))
1200 solver_args <<
"-H" <<
QString::number(optionsMap.value(
"scaleH").toDouble());
1203 if (optionsMap.contains(
"scaleUnits"))
1204 solver_args <<
"-u" << optionsMap.
value(
"scaleUnits").toString();
1207 if (optionsMap.contains(
"ra"))
1208 solver_args <<
"-3" <<
QString::number(optionsMap.value(
"ra").toDouble());
1211 if (optionsMap.contains(
"de"))
1212 solver_args <<
"-4" <<
QString::number(optionsMap.value(
"de").toDouble());
1215 if (optionsMap.contains(
"radius"))
1216 solver_args <<
"-5" <<
QString::number(optionsMap.value(
"radius").toDouble());
1219 if (optionsMap.contains(
"custom"))
1220 solver_args << optionsMap.
value(
"custom").toString();
1226 void Align::generateFOVBounds(
double fov_w,
QString &fov_low,
QString &fov_high,
double tolerance)
1230 double lower_boundary = 1.0 - tolerance;
1231 double upper_boundary = 1.0 + tolerance;
1238 double fov_lower = fov_w * lower_boundary;
1239 double fov_upper = fov_w * upper_boundary;
1249 QVariantMap optionsMap;
1262 if (Options::astrometryUseNoVerify())
1263 optionsMap[
"noverify"] =
true;
1265 if (Options::astrometryUseResort())
1266 optionsMap[
"resort"] =
true;
1268 if (Options::astrometryUseNoFITS2FITS())
1269 optionsMap[
"nofits2fits"] =
true;
1271 if (data ==
nullptr)
1273 if (Options::astrometryUseDownsample())
1275 if (Options::astrometryAutoDownsample() && m_CameraWidth && m_CameraHeight)
1277 uint16_t w = m_CameraWidth / (alignBinning->currentIndex() + 1);
1278 optionsMap[
"downsample"] = getSolverDownsample(w);
1281 optionsMap[
"downsample"] = Options::astrometryDownsample();
1285 optionsMap[
"image_width"] = m_CameraWidth / (alignBinning->currentIndex() + 1);
1286 optionsMap[
"image_height"] = m_CameraHeight / (alignBinning->currentIndex() + 1);
1288 if (Options::astrometryUseImageScale() && m_FOVWidth > 0 && m_FOVHeight > 0)
1291 if (Options::astrometryImageScaleUnits() == 1)
1293 else if (Options::astrometryImageScaleUnits() == 2)
1295 if (Options::astrometryAutoUpdateImageScale())
1298 double fov_w = m_FOVWidth;
1301 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
1306 else if (Options::astrometryImageScaleUnits() == SSolver::ARCSEC_PER_PIX)
1308 fov_w = m_FOVPixelScale;
1313 generateFOVBounds(fov_w, fov_low, fov_high, m_EffectiveFOVPending ? 0.3 : 0.05);
1315 optionsMap[
"scaleL"] = fov_low;
1316 optionsMap[
"scaleH"] = fov_high;
1317 optionsMap[
"scaleUnits"] = units;
1321 optionsMap[
"scaleL"] = Options::astrometryImageScaleLow();
1322 optionsMap[
"scaleH"] = Options::astrometryImageScaleHigh();
1323 optionsMap[
"scaleUnits"] = units;
1327 if (Options::astrometryUsePosition() && m_Mount !=
nullptr)
1329 double ra = 0, dec = 0;
1330 m_Mount->getEqCoords(&ra, &dec);
1332 optionsMap[
"ra"] = ra * 15.0;
1333 optionsMap[
"de"] = dec;
1334 optionsMap[
"radius"] = Options::astrometryRadius();
1341 data->getRecordValue(
"NAXIS1",
width);
1342 if (
width.isValid())
1344 optionsMap[
"downsample"] = getSolverDownsample(
width.toInt());
1347 optionsMap[
"downsample"] = Options::astrometryDownsample();
1351 data->getRecordValue(
"SCALE", pixscale);
1354 optionsMap[
"scaleL"] = 0.8 * pixscale.
toDouble();
1355 optionsMap[
"scaleH"] = 1.2 * pixscale.
toDouble();
1356 optionsMap[
"scaleUnits"] =
"app";
1361 data->getRecordValue(
"RA", ra);
1362 data->getRecordValue(
"DEC", de);
1367 optionsMap[
"radius"] = Options::astrometryRadius();
1371 if (Options::astrometryCustomOptions().isEmpty() ==
false)
1372 optionsMap[
"custom"] = Options::astrometryCustomOptions();
1379 m_AlignTimer.
stop();
1380 m_CaptureTimer.
stop();
1382 if (m_Camera ==
nullptr)
1384 appendLogText(
i18n(
"Error: No camera detected."));
1388 if (m_Camera->isConnected() ==
false)
1390 appendLogText(
i18n(
"Error: lost connection to camera."));
1391 KSNotification::event(
QLatin1String(
"AlignFailed"),
i18n(
"Astrometry alignment failed"), KSNotification::Align,
1392 KSNotification::Alert);
1396 if (m_Camera->isBLOBEnabled() ==
false)
1398 m_Camera->setBLOBEnabled(
true);
1404 if (m_FocalLength == -1 || m_Aperture == -1)
1406 KSNotification::error(
1407 i18n(
"Telescope aperture and focal length are missing. Please check your optical train settings and try again."));
1411 if (m_CameraPixelWidth == -1 || m_CameraPixelHeight == -1)
1413 KSNotification::error(
i18n(
"CCD pixel size is missing. Please check your driver settings and try again."));
1417 if (m_FilterWheel !=
nullptr)
1419 if (m_FilterWheel->isConnected() ==
false)
1421 appendLogText(
i18n(
"Error: lost connection to filter wheel."));
1425 int targetPosition = alignFilter->currentIndex() + 1;
1427 if (targetPosition > 0 && targetPosition != currentFilterPosition)
1429 filterPositionPending =
true;
1431 m_FilterManager->setFilterPosition(targetPosition, FilterManager::NO_AUTOFOCUS_POLICY);
1437 if (m_Camera->getDriverInfo()->getClientManager()->getBLOBMode(m_Camera->getDeviceName().
toLatin1().
constData(),
1441 nullptr,
i18n(
"Image transfer is disabled for this camera. Would you like to enable it?")) ==
1444 m_Camera->getDriverInfo()->getClientManager()->setBLOBMode(B_ONLY, m_Camera->getDeviceName().
toLatin1().
constData(),
1446 m_Camera->getDriverInfo()->getClientManager()->setBLOBMode(B_ONLY, m_Camera->getDeviceName().
toLatin1().
constData(),
1455 double seqExpose = alignExposure->value();
1457 ISD::CameraChip *targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
1459 if (m_FocusState >= FOCUS_PROGRESS)
1461 appendLogText(
i18n(
"Cannot capture while focus module is busy. Retrying in %1 seconds...", CAPTURE_RETRY_DELAY / 1000));
1462 m_CaptureTimer.
start(CAPTURE_RETRY_DELAY);
1466 if (targetChip->isCapturing())
1468 appendLogText(
i18n(
"Cannot capture while CCD exposure is in progress. Retrying in %1 seconds...",
1469 CAPTURE_RETRY_DELAY / 1000));
1470 m_CaptureTimer.
start(CAPTURE_RETRY_DELAY);
1474 m_AlignView->setBaseSize(alignWidget->size());
1475 m_AlignView->setProperty(
"suspended", (solverModeButtonGroup->checkedId() == SOLVER_LOCAL
1476 && alignDarkFrame->isChecked()));
1482 if (solverModeButtonGroup->checkedId() == SOLVER_REMOTE && remoteParser.get() !=
nullptr)
1484 if (m_RemoteParserDevice ==
nullptr)
1486 appendLogText(
i18n(
"No remote astrometry driver detected, switching to StellarSolver."));
1492 auto activeDevices = m_RemoteParserDevice->getBaseDevice().getText(
"ACTIVE_DEVICES");
1495 auto activeCCD = activeDevices.findWidgetByName(
"ACTIVE_CCD");
1496 if (
QString(activeCCD->text) != m_Camera->getDeviceName())
1499 m_RemoteParserDevice->getClientManager()->sendNewProperty(activeDevices);
1506 solverTimer.
start();
1512 dir.setNameFilters(
QStringList() <<
"fits*" <<
"tmp.*");
1514 for (
auto &dirFile : dir.entryList())
1517 prepareCapture(targetChip);
1520 if (matchPAHStage(PAA::PAH_REFRESH))
1521 targetChip->capture(m_PolarAlignmentAssistant->getPAHExposureDuration());
1523 targetChip->capture(seqExpose);
1525 solveB->setEnabled(
false);
1526 stopB->setEnabled(
true);
1527 pi->startAnimation();
1529 differentialSlewingActivated =
false;
1532 emit newStatus(state);
1533 solverFOV->setProperty(
"visible",
true);
1536 if (matchPAHStage(PAA::PAH_REFRESH))
1539 appendLogText(
i18n(
"Capturing image..."));
1547 m_Mount->getEqCoords(&ra, &dec);
1548 if (!m_SolveFromFile)
1550 int currentRow = solutionTable->rowCount();
1551 solutionTable->insertRow(currentRow);
1552 for (
int i = 4; i < 6; i++)
1556 solutionTable->setItem(currentRow, i, disabledBox);
1560 RAReport->
setText(ScopeRAOut->text());
1563 solutionTable->setItem(currentRow, 0, RAReport);
1566 DECReport->
setText(ScopeDecOut->text());
1569 solutionTable->setItem(currentRow, 1, DECReport);
1571 double maxrad = 1.0;
1587 solutionTable->setItem(currentRow, 2, ObjNameReport);
1593 solutionTable->setCellWidget(currentRow, 3, alignIndicator);
1605 auto chip = data->property(
"chip");
1606 if (chip.isValid() && chip.toInt() == ISD::CameraChip::GUIDE_CCD)
1614 m_AlignView->loadData(data);
1618 m_ImageData.
reset();
1620 RUN_PAH(setImageData(m_ImageData));
1623 if (matchPAHStage(PAA::PAH_REFRESH))
1625 setCaptureComplete();
1629 appendLogText(
i18n(
"Image received."));
1632 if (solverModeButtonGroup->checkedId() == SOLVER_LOCAL)
1635 if (alignDarkFrame->isChecked())
1637 int x,
y, w, h, binx = 1, biny = 1;
1638 ISD::CameraChip *targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
1639 targetChip->getFrame(&
x, &
y, &w, &h);
1640 targetChip->getBinning(&binx, &biny);
1642 uint16_t offsetX =
x / binx;
1643 uint16_t offsetY =
y / biny;
1645 m_DarkProcessor->denoise(OpticalTrainManager::Instance()->
id(opticalTrainCombo->currentText()),
1646 targetChip, m_ImageData, alignExposure->value(), offsetX, offsetY);
1650 setCaptureComplete();
1656 if (m_Camera->getUploadMode() == ISD::Camera::UPLOAD_LOCAL)
1658 rememberUploadMode = ISD::Camera::UPLOAD_LOCAL;
1659 m_Camera->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
1662 if (m_Camera->isFastExposureEnabled())
1664 m_RememberCameraFastExposure =
true;
1665 m_Camera->setFastExposureEnabled(
false);
1668 m_Camera->setEncodingFormat(
"FITS");
1669 targetChip->resetFrame();
1670 targetChip->setBatchMode(
false);
1671 targetChip->setCaptureMode(FITS_ALIGN);
1672 targetChip->setFrameType(FRAME_LIGHT);
1674 int bin = alignBinning->currentIndex() + 1;
1675 targetChip->setBinning(bin, bin);
1678 if (m_Camera->hasGain() && alignGain->isEnabled() && alignGain->value() > alignGainSpecialValue)
1679 m_Camera->setGain(alignGain->value());
1681 if (alignISO->currentIndex() >= 0)
1682 targetChip->setISOIndex(alignISO->currentIndex());
1686 void Align::setCaptureComplete()
1688 if (matchPAHStage(PAA::PAH_REFRESH))
1690 emit newFrame(m_AlignView);
1691 m_PolarAlignmentAssistant->processPAHRefresh();
1695 emit newImage(m_AlignView);
1697 solverFOV->setImage(m_AlignView->getDisplayImage());
1704 gotoModeButtonGroup->button(mode)->setChecked(
true);
1705 m_CurrentGotoMode =
static_cast<GotoMode
>(mode);
1714 QStringList astrometryDataDirs = KSUtils::getAstrometryDataDirs();
1717 if (solverModeButtonGroup->checkedId() == SOLVER_LOCAL)
1719 if(Options::solverType() != SSolver::SOLVER_ASTAP
1720 && Options::solverType() != SSolver::SOLVER_WATNEYASTROMETRY)
1722 bool foundAnIndex =
false;
1723 for(
auto &dataDir : astrometryDataDirs)
1730 if(indexList.
count() > 0)
1731 foundAnIndex =
true;
1737 i18n(
"No index files were found on your system in the specified index file directories. Please download some index files or add the correct directory to the list."));
1739 if(alignSettings && m_IndexFilesPage)
1742 alignSettings->
show();
1746 if (m_StellarSolver->isRunning())
1747 m_StellarSolver->abort();
1749 m_ImageData = m_AlignView->imageData();
1750 m_StellarSolver->loadNewImageBuffer(m_ImageData->getStatistics(), m_ImageData->getImageBuffer());
1751 m_StellarSolver->setProperty(
"ProcessType", SSolver::SOLVE);
1752 m_StellarSolver->setProperty(
"ExtractorType", Options::solveSextractorType());
1753 m_StellarSolver->setProperty(
"SolverType", Options::solverType());
1754 connect(m_StellarSolver.get(), &StellarSolver::ready,
this, &Align::solverComplete);
1755 m_StellarSolver->setIndexFolderPaths(Options::astrometryIndexFolderList());
1757 auto params = m_StellarSolverProfiles.
at(Options::solveOptionsProfile());
1758 params.partition = Options::stellarSolverPartition();
1759 m_StellarSolver->setParameters(params);
1761 const SSolver::SolverType type =
static_cast<SSolver::SolverType
>(m_StellarSolver->property(
"SolverType").toInt());
1762 if(type == SSolver::SOLVER_LOCALASTROMETRY || type == SSolver::SOLVER_ASTAP || type == SSolver::SOLVER_WATNEYASTROMETRY)
1766 m_AlignView->saveImage(filename);
1767 m_StellarSolver->setProperty(
"FileToProcess", filename);
1768 ExternalProgramPaths externalPaths;
1769 externalPaths.sextractorBinaryPath = Options::sextractorBinary();
1770 externalPaths.solverPath = Options::astrometrySolverBinary();
1771 externalPaths.astapBinaryPath = Options::aSTAPExecutable();
1772 externalPaths.watneyBinaryPath = Options::watneyBinary();
1773 externalPaths.wcsPath = Options::astrometryWCSInfo();
1774 m_StellarSolver->setExternalFilePaths(externalPaths);
1777 m_StellarSolver->setProperty(
"AutoGenerateAstroConfig",
true);
1780 if(type == SSolver::SOLVER_ONLINEASTROMETRY )
1784 m_AlignView->saveImage(filename);
1786 m_StellarSolver->setProperty(
"FileToProcess", filename);
1787 m_StellarSolver->setProperty(
"AstrometryAPIKey", Options::astrometryAPIKey());
1788 m_StellarSolver->setProperty(
"AstrometryAPIURL", Options::astrometryAPIURL());
1791 bool useImageScale = Options::astrometryUseImageScale();
1792 if (useBlindScale == BLIND_ENGAGNED)
1794 useImageScale =
false;
1795 useBlindScale = BLIND_USED;
1796 appendLogText(
i18n(
"Solving with blind image scale..."));
1799 bool useImagePosition = Options::astrometryUsePosition();
1800 if (useBlindPosition == BLIND_ENGAGNED)
1802 useImagePosition =
false;
1803 useBlindPosition = BLIND_USED;
1804 appendLogText(
i18n(
"Solving with blind image position..."));
1807 if (m_SolveFromFile)
1809 FITSImage::Solution solution;
1810 m_ImageData->parseSolution(solution);
1812 if (useImageScale && solution.pixscale > 0)
1813 m_StellarSolver->setSearchScale(solution.pixscale * 0.8,
1814 solution.pixscale * 1.2,
1815 SSolver::ARCSEC_PER_PIX);
1817 m_StellarSolver->setProperty(
"UseScale",
false);
1819 if (useImagePosition && solution.ra > 0)
1820 m_StellarSolver->setSearchPositionInDegrees(solution.ra, solution.dec);
1822 m_StellarSolver->setProperty(
"UsePosition",
false);
1829 SSolver::ScaleUnits units =
static_cast<SSolver::ScaleUnits
>(Options::astrometryImageScaleUnits());
1831 m_StellarSolver->setSearchScale(Options::astrometryImageScaleLow() * 0.8,
1832 Options::astrometryImageScaleHigh() * 1.2,
1836 m_StellarSolver->setProperty(
"UseScale",
false);
1838 if(useImagePosition)
1839 m_StellarSolver->setSearchPositionInDegrees(m_TelescopeCoord.
ra().
Degrees(), m_TelescopeCoord.
dec().
Degrees());
1841 m_StellarSolver->setProperty(
"UsePosition",
false);
1844 if(Options::alignmentLogging())
1849 m_StellarSolver->setLogLevel(SSolver::LOG_NONE);
1850 m_StellarSolver->setSSLogLevel(SSolver::LOG_OFF);
1851 if(Options::astrometryLogToFile())
1853 m_StellarSolver->setProperty(
"LogToFile",
true);
1854 m_StellarSolver->setProperty(
"LogFileName", Options::astrometryLogFilepath());
1859 m_StellarSolver->setLogLevel(SSolver::LOG_NONE);
1860 m_StellarSolver->setSSLogLevel(SSolver::LOG_OFF);
1864 m_StellarSolver->start();
1868 if (m_ImageData.
isNull())
1869 m_ImageData = m_AlignView->imageData();
1872 remoteParser->startSolver(m_ImageData->filename(),
generateRemoteArgs(m_ImageData),
false);
1876 solverTimer.
start();
1879 emit newStatus(state);
1882 void Align::solverComplete()
1884 disconnect(m_StellarSolver.get(), &StellarSolver::ready,
this, &Align::solverComplete);
1885 if(!m_StellarSolver->solvingDone() || m_StellarSolver->failed())
1888 if (CHECK_PAH(processSolverFailure()))
1895 FITSImage::Solution solution = m_StellarSolver->getSolution();
1896 const bool eastToTheRight = solution.parity == FITSImage::POSITIVE ? false :
true;
1897 solverFinished(solution.orientation, solution.ra, solution.dec, solution.pixscale, eastToTheRight);
1903 pi->stopAnimation();
1904 stopB->setEnabled(
false);
1905 solveB->setEnabled(
true);
1907 sOrientation = orientation;
1911 double elapsed = solverTimer.
elapsed() / 1000.0;
1912 appendLogText(
i18n(
"Solver completed after %1 seconds.",
QString::number(elapsed,
'f', 2)));
1914 m_AlignTimer.
stop();
1915 if (solverModeButtonGroup->checkedId() == SOLVER_REMOTE && m_RemoteParserDevice && remoteParser.get())
1922 ISD::CameraChip *targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
1923 targetChip->getBinning(&binx, &biny);
1925 if (Options::alignmentLogging())
1927 QString parityString = eastToTheRight ?
"neg" :
"pos";
1928 appendLogText(
i18n(
"Solver RA (%1) DEC (%2) Orientation (%3) Pixel Scale (%4) Parity (%5)",
QString::number(ra,
'f', 5),
1934 if (!m_SolveFromFile &&
1935 (isEqual(m_FOVWidth, 0) || m_EffectiveFOVPending || std::fabs(pixscale - m_FOVPixelScale) > 0.005) &&
1938 double newFOVW = m_CameraWidth * pixscale / binx / 60.0;
1939 double newFOVH = m_CameraHeight * pixscale / biny / 60.0;
1941 calculateEffectiveFocalLength(newFOVW);
1942 saveNewEffectiveFOV(newFOVW, newFOVH);
1944 m_EffectiveFOVPending =
false;
1947 m_AlignCoord.
setRA0(ra / 15.0);
1953 m_AlignCoord.
EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
1956 if (!m_SolveFromFile)
1959 calculateAlignTargetDiff();
1965 double solverPA = SolverUtils::rotationToPositionAngle(orientation);
1966 solverFOV->setCenter(m_AlignCoord);
1967 solverFOV->setPA(solverPA);
1968 solverFOV->setImageDisplay(Options::astrometrySolverOverlay());
1970 sensorFOV->setPA(solverPA);
1975 getFormattedCoords(m_AlignCoord.
ra().
Hours(), m_AlignCoord.
dec().
Degrees(), ra_dms, dec_dms);
1977 SolverRAOut->setText(ra_dms);
1978 SolverDecOut->setText(dec_dms);
1980 if (Options::astrometrySolverWCS())
1982 auto ccdRotation = m_Camera->getNumber(
"CCD_ROTATION");
1985 auto rotation = ccdRotation->findWidgetByName(
"CCD_ROTATION_VALUE");
1988 ClientManager *clientManager = m_Camera->getDriverInfo()->getClientManager();
1989 rotation->setValue(orientation);
1990 clientManager->sendNewProperty(ccdRotation);
1992 if (m_wcsSynced ==
false)
1995 i18n(
"WCS information updated. Images captured from this point forward shall have valid WCS."));
1998 auto telescopeInfo = m_Mount->getNumber(
"TELESCOPE_INFO");
2000 clientManager->sendNewProperty(telescopeInfo);
2008 m_CaptureErrorCounter = 0;
2009 m_SlewErrorCounter = 0;
2010 m_CaptureTimeoutCounter = 0;
2013 i18n(
"Solution coordinates: RA (%1) DEC (%2) Telescope Coordinates: RA (%3) DEC (%4) Target Coordinates: RA (%5) DEC (%6)",
2021 if (!m_SolveFromFile && m_CurrentGotoMode == GOTO_SLEW)
2023 dms diffDeg(m_TargetDiffTotal / 3600.0);
2024 appendLogText(
i18n(
"Target is within %1 degrees of solution coordinates.", diffDeg.
toDMSString()));
2027 if (rememberUploadMode != m_Camera->getUploadMode())
2028 m_Camera->setUploadMode(rememberUploadMode);
2031 if (m_RememberCameraFastExposure)
2033 m_RememberCameraFastExposure =
false;
2034 m_Camera->setFastExposureEnabled(
true);
2039 int currentRow = solutionTable->rowCount() - 1;
2040 if (!m_SolveFromFile)
2042 stopProgressAnimation();
2043 solutionTable->setCellWidget(currentRow, 3,
new QWidget());
2048 emit newSolverResults(orientation, ra, dec, pixscale);
2051 {
"camera", m_Camera->getDeviceName()},
2052 {
"ra", SolverRAOut->text()},
2053 {
"de", SolverDecOut->text()},
2054 {
"dRA", m_TargetDiffRA},
2055 {
"dDE", m_TargetDiffDE},
2056 {
"targetDiff", m_TargetDiffTotal},
2059 {
"fov", FOVOut->text()},
2064 emit newStatus(state);
2065 solverIterations = 0;
2066 KSNotification::event(
QLatin1String(
"AlignSuccessful"),
i18n(
"Astrometry alignment completed successfully"),
2067 KSNotification::Align);
2069 switch (m_CurrentGotoMode)
2074 if (!m_SolveFromFile)
2076 stopProgressAnimation();
2077 statusReport->setIcon(
QIcon(
":/icons/AlignSuccess.svg"));
2078 solutionTable->setItem(currentRow, 3, statusReport.release());
2084 if (m_SolveFromFile || m_TargetDiffTotal >
static_cast<double>(alignAccuracyThreshold->value()))
2086 if (!m_SolveFromFile && ++solverIterations == MAXIMUM_SOLVER_ITERATIONS)
2088 appendLogText(
i18n(
"Maximum number of iterations reached. Solver failed."));
2090 if (!m_SolveFromFile)
2092 statusReport->setIcon(
QIcon(
":/icons/AlignFailure.svg"));
2093 solutionTable->setItem(currentRow, 3, statusReport.release());
2100 targetAccuracyNotMet =
true;
2102 if (!m_SolveFromFile)
2104 stopProgressAnimation();
2105 statusReport->setIcon(
QIcon(
":/icons/AlignWarning.svg"));
2106 solutionTable->setItem(currentRow, 3, statusReport.release());
2113 stopProgressAnimation();
2114 statusReport->setIcon(
QIcon(
":/icons/AlignSuccess.svg"));
2115 solutionTable->setItem(currentRow, 3, statusReport.release());
2117 appendLogText(
i18n(
"Target is within acceptable range. Astrometric solver is successful."));
2121 if (!m_SolveFromFile)
2123 stopProgressAnimation();
2124 statusReport->setIcon(
QIcon(
":/icons/AlignSuccess.svg"));
2125 solutionTable->setItem(currentRow, 3, statusReport.release());
2130 solverFOV->setProperty(
"visible",
true);
2132 if (!matchPAHStage(PAA::PAH_IDLE))
2133 m_PolarAlignmentAssistant->processPAHStage(orientation, ra, dec, pixscale, eastToTheRight,
2134 m_StellarSolver->getSolutionHealpix(),
2135 m_StellarSolver->getSolutionIndexNumber());
2144 emit newStatus(state);
2146 solveB->setEnabled(
true);
2147 loadSlewB->setEnabled(
true);
2156 if (Options::astrometryUseImageScale() && useBlindScale == BLIND_IDLE)
2158 useBlindScale = BLIND_ENGAGNED;
2159 setAlignTableResult(ALIGN_RESULT_FAILED);
2165 if (Options::astrometryUsePosition() && useBlindPosition == BLIND_IDLE)
2167 useBlindPosition = BLIND_ENGAGNED;
2168 setAlignTableResult(ALIGN_RESULT_FAILED);
2174 appendLogText(
i18n(
"Solver Failed."));
2175 if(!Options::alignmentLogging())
2177 i18n(
"Please check you have sufficient stars in the image, the indicated FOV is correct, and the necessary index files are installed. Enable Alignment Logging in Setup Tab -> Logs to get detailed information on the failure."));
2179 KSNotification::event(
QLatin1String(
"AlignFailed"),
i18n(
"Astrometry alignment failed"),
2180 KSNotification::Align, KSNotification::Alert);
2183 pi->stopAnimation();
2184 stopB->setEnabled(
false);
2185 solveB->setEnabled(
true);
2186 loadSlewB->setEnabled(
true);
2188 m_AlignTimer.
stop();
2190 m_SolveFromFile =
false;
2191 solverIterations = 0;
2192 m_CaptureErrorCounter = 0;
2193 m_CaptureTimeoutCounter = 0;
2194 m_SlewErrorCounter = 0;
2197 emit newStatus(state);
2199 solverFOV->setProperty(
"visible",
false);
2201 setAlignTableResult(ALIGN_RESULT_FAILED);
2207 if (Options::astrometryUseRotator())
2209 if (m_SolveFromFile)
2211 m_TargetPositionAngle = solverFOV->PA();
2213 qCDebug(KSTARS_EKOS_ALIGN) <<
"Solving from file: Setting target PA to:" << m_TargetPositionAngle;
2217 currentRotatorPA = solverFOV->PA();
2220 if (m_Rotator !=
nullptr && m_Rotator->isConnected())
2223 auto absAngle = m_Rotator->getNumber(
"ABS_ROTATOR_ANGLE");
2229 double rawAngle = absAngle->at(0)->getValue();
2230 double offset = range360((rawAngle * Options::pAMultiplier()) - currentRotatorPA);
2232 auto reverseStatus =
"Unknown";
2233 auto reverseProperty = m_Rotator->
getSwitch(
"REVERSE_ROTATOR");
2234 if (reverseProperty)
2236 if (reverseProperty->at(0)->getState() == ISS_ON)
2237 reverseStatus =
"Reversed Direction";
2239 reverseStatus =
"Normal Direction";
2242 qCDebug(KSTARS_EKOS_ALIGN) <<
"Raw Rotator Angle:" << rawAngle <<
"Rotator PA:" << currentRotatorPA
2243 <<
"Rotator Offset:" << offset <<
"Direction:" << reverseStatus;
2246 Options::setPAPierSide(m_Mount->pierSide());
2247 Options::setPAOffset(offset);
2250 if (absAngle && std::isnan(m_TargetPositionAngle) ==
false
2251 && fabs(currentRotatorPA - m_TargetPositionAngle) * 60 > Options::astrometryRotatorThreshold())
2254 double rawAngle = range360((Options::pAOffset() + m_TargetPositionAngle) / Options::pAMultiplier());
2255 absAngle->at(0)->setValue(rawAngle);
2256 ClientManager *clientManager = m_Rotator->getDriverInfo()->getClientManager();
2257 clientManager->sendNewProperty(absAngle);
2258 appendLogText(
i18n(
"Setting position angle to %1 degrees E of N...", m_TargetPositionAngle));
2260 emit newStatus(state);
2264 else if (std::isnan(m_TargetPositionAngle) ==
false)
2266 double current = currentRotatorPA;
2267 double target = m_TargetPositionAngle;
2269 double diff = SolverUtils::rangePA(current - target);
2270 double threshold = Options::astrometryRotatorThreshold() / 60.0;
2272 appendLogText(
i18n(
"Current PA is %1; Target PA is %2; diff: %3", current, target, diff));
2274 emit manualRotatorChanged(current, target, threshold);
2276 m_ManualRotator->setRotatorDiff(current, target, diff);
2277 if (fabs(diff) > threshold)
2279 targetAccuracyNotMet =
true;
2280 m_ManualRotator->show();
2281 m_ManualRotator->raise();
2283 emit newStatus(state);
2288 m_TargetPositionAngle = std::numeric_limits<double>::quiet_NaN();
2289 targetAccuracyNotMet =
false;
2300 m_CaptureTimer.
stop();
2301 if (solverModeButtonGroup->checkedId() == SOLVER_LOCAL)
2302 m_StellarSolver->abort();
2303 else if (solverModeButtonGroup->checkedId() == SOLVER_REMOTE && remoteParser)
2304 remoteParser->stopSolver();
2306 pi->stopAnimation();
2307 stopB->setEnabled(
false);
2308 solveB->setEnabled(
true);
2309 loadSlewB->setEnabled(
true);
2311 m_SolveFromFile =
false;
2312 solverIterations = 0;
2313 m_CaptureErrorCounter = 0;
2314 m_CaptureTimeoutCounter = 0;
2315 m_SlewErrorCounter = 0;
2316 m_AlignTimer.
stop();
2321 if (rememberUploadMode != m_Camera->getUploadMode())
2322 m_Camera->setUploadMode(rememberUploadMode);
2325 if (m_RememberCameraFastExposure)
2327 m_RememberCameraFastExposure =
false;
2328 m_Camera->setFastExposureEnabled(
true);
2331 auto targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
2334 if (matchPAHStage(PAA::PAH_POST_REFRESH))
2336 if (targetChip->isCapturing())
2337 targetChip->abortExposure();
2339 appendLogText(
i18n(
"Refresh is complete."));
2343 if (targetChip->isCapturing())
2345 targetChip->abortExposure();
2346 appendLogText(
i18n(
"Capture aborted."));
2350 double elapsed = solverTimer.
elapsed() / 1000.0;
2352 appendLogText(
i18n(
"Solver aborted after %1 seconds.",
QString::number(elapsed,
'f', 2)));
2357 emit newStatus(state);
2359 setAlignTableResult(ALIGN_RESULT_FAILED);
2364 int currentRow = solutionTable->rowCount() - 1;
2371 QWidget *indicator = solutionTable->cellWidget(currentRow, 3);
2372 if (indicator ==
nullptr)
2377 void Align::stopProgressAnimation()
2380 if (progress_indicator !=
nullptr)
2388 result << sOrientation << sRA << sDEC;
2393 void Align::appendLogText(
const QString &text)
2395 m_LogText.
insert(0,
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
2396 KStarsData::Instance()->lt().toString(
"yyyy-MM-ddThh:mm:ss"), text));
2398 qCInfo(KSTARS_EKOS_ALIGN) << text;
2403 void Align::clearLog()
2411 if (prop.isNameMatch(
"EQUATORIAL_EOD_COORD") || prop.isNameMatch(
"EQUATORIAL_COORD"))
2413 auto nvp = prop.getNumber();
2416 getFormattedCoords(m_TelescopeCoord.
ra().
Hours(), m_TelescopeCoord.
dec().
Degrees(), ra_dms, dec_dms);
2418 ScopeRAOut->setText(ra_dms);
2419 ScopeDecOut->setText(dec_dms);
2425 m_wasSlewStarted =
false;
2434 if (m_wasSlewStarted && Options::astrometryAutoUpdatePosition())
2437 opsAstrometry->estRA->setText(ra_dms);
2438 opsAstrometry->estDec->setText(dec_dms);
2440 Options::setAstrometryPositionRA(nvp->np[0].value * 15);
2441 Options::setAstrometryPositionDE(nvp->np[1].value);
2447 if (m_Dome && m_Dome->isMoving())
2454 if (m_wasSlewStarted && matchPAHStage(PAA::PAH_FIND_CP))
2457 m_wasSlewStarted =
false;
2458 appendLogText(
i18n(
"Mount completed slewing near celestial pole. Capture again to verify."));
2461 m_PolarAlignmentAssistant->setPAHStage(PAA::PAH_FIRST_CAPTURE);
2472 m_wasSlewStarted =
false;
2474 if (m_CurrentGotoMode == GOTO_SLEW)
2481 appendLogText(
i18n(
"Mount is synced to solution coordinates."));
2487 i18n(
"Astrometry alignment completed successfully"), KSNotification::Align);
2489 emit newStatus(state);
2490 solverIterations = 0;
2497 if (!didSlewStart())
2505 m_wasSlewStarted =
false;
2506 if (m_SolveFromFile)
2508 m_SolveFromFile =
false;
2509 m_TargetPositionAngle = solverFOV->PA();
2510 qCDebug(KSTARS_EKOS_ALIGN) <<
"Solving from file: Setting target PA to" << m_TargetPositionAngle;
2513 emit newStatus(state);
2515 if (alignSettlingTime->value() >= DELAY_THRESHOLD_NOTIFY)
2516 appendLogText(
i18n(
"Settling..."));
2517 m_CaptureTimer.
start(alignSettlingTime->value());
2520 else if (differentialSlewingActivated)
2522 appendLogText(
i18n(
"Differential slewing complete."));
2527 KSNotification::event(
QLatin1String(
"AlignSuccessful"),
i18n(
"Astrometry alignment completed successfully"),
2528 KSNotification::Align);
2530 emit newStatus(state);
2531 solverIterations = 0;
2533 else if (m_CurrentGotoMode == GOTO_SLEW || (m_MountModel && m_MountModel->isRunning()))
2535 if (targetAccuracyNotMet)
2536 appendLogText(
i18n(
"Slew complete. Target accuracy is not met, running solver again..."));
2538 appendLogText(
i18n(
"Slew complete. Solving Alignment Point. . ."));
2540 targetAccuracyNotMet =
false;
2543 emit newStatus(state);
2545 if (alignSettlingTime->value() >= DELAY_THRESHOLD_NOTIFY)
2546 appendLogText(
i18n(
"Settling..."));
2547 m_CaptureTimer.
start(alignSettlingTime->value());
2555 m_wasSlewStarted =
false;
2566 m_wasSlewStarted =
true;
2568 handleMountMotion();
2576 m_wasSlewStarted =
false;
2581 appendLogText(
i18n(
"Syncing failed."));
2583 appendLogText(
i18n(
"Slewing failed."));
2585 if (++m_SlewErrorCounter == 3)
2592 if (m_CurrentGotoMode == GOTO_SLEW)
2603 RUN_PAH(processMountRotation(m_TelescopeCoord.
ra(), alignSettlingTime->value()));
2605 else if (prop.isNameMatch(
"ABS_ROTATOR_ANGLE"))
2607 auto nvp = prop.getNumber();
2609 currentRotatorPA = SolverUtils::rangePA( (nvp->np[0].value * Options::pAMultiplier()) - Options::pAOffset());
2610 if (std::isnan(m_TargetPositionAngle) ==
false && nvp->s == IPS_OK)
2612 auto diff = fabs(currentRotatorPA - m_TargetPositionAngle) * 60;
2613 qCDebug(KSTARS_EKOS_ALIGN) <<
"Raw Rotator Angle:" << nvp->np[0].value <<
"Current PA:" << currentRotatorPA
2614 <<
"Target PA:" << m_TargetPositionAngle <<
"Diff (arcmin):" << diff <<
"Offset:" << Options::pAOffset();
2616 if (diff <= Options::astrometryRotatorThreshold())
2618 appendLogText(
i18n(
"Rotator reached target position angle."));
2619 targetAccuracyNotMet =
true;
2620 m_TargetPositionAngle = std::numeric_limits<double>::quiet_NaN();
2624 else if (diff <= Options::astrometryRotatorThreshold() * 2)
2626 appendLogText(
i18n(
"Rotator failed to arrive at the requested position angle. Check power, backlash, or obstructions."));
2632 i18n(
"Rotator failed to arrive at the requested position angle. Try to reverse rotation direction in Rotator Settings."));
2636 else if (prop.isNameMatch(
"DOME_MOTION"))
2639 if (domeReady ==
false && prop.getState() == IPS_OK)
2644 handleMountStatus();
2647 else if (prop.isNameMatch(
"TELESCOPE_MOTION_NS") || prop.isNameMatch(
"TELESCOPE_MOTION_WE"))
2649 switch (prop.getState())
2653 handleMountMotion();
2654 m_wasSlewStarted =
true;
2657 qCDebug(KSTARS_EKOS_ALIGN) <<
"Mount motion finished.";
2658 handleMountStatus();
2664 void Align::handleMountMotion()
2668 if (matchPAHStage(PAA::PAH_IDLE))
2671 appendLogText(
i18n(
"Slew detected, suspend solving..."));
2679 void Align::handleMountStatus()
2681 auto nvp = m_Mount->getNumber(m_Mount->isJ2000() ?
"EQUATORIAL_COORD" :
2682 "EQUATORIAL_EOD_COORD");
2691 if (m_SolveFromFile)
2693 m_TargetCoord = m_AlignCoord;
2695 qCDebug(KSTARS_EKOS_ALIGN) <<
"Solving from file. Setting Target Coordinates align coords. RA:"
2701 else if (m_CurrentGotoMode == GOTO_SYNC)
2703 else if (m_CurrentGotoMode == GOTO_SLEW)
2711 if (m_Mount->Sync(&m_AlignCoord))
2713 emit newStatus(state);
2720 emit newStatus(state);
2721 appendLogText(
i18n(
"Syncing failed."));
2728 emit newStatus(state);
2736 m_Mount->Slew(&m_TargetCoord);
2737 slewStartTimer.
start();
2738 appendLogText(
i18n(
"Slewing to target coordinates: RA (%1) DEC (%2).", m_TargetCoord.
ra().
toHMSString(),
2744 if (canSync && !m_SolveFromFile)
2748 if (Ekos::Manager::Instance()->getCurrentJobName().isEmpty())
2750 KSNotification::event(
QLatin1String(
"EkosSchedulerTelescopeSynced"),
2751 i18n(
"Ekos job (%1) - Telescope synced",
2752 Ekos::Manager::Instance()->getCurrentJobName()));
2757 if (Options::astrometryDifferentialSlewing())
2762 m_TargetCoord.
setRA(m_TargetCoord.
ra() - m_TargetDiffRA);
2763 m_TargetCoord.
setDec(m_TargetCoord.
dec() - m_TargetDiffDE);
2765 differentialSlewingActivated =
true;
2767 qCDebug(KSTARS_EKOS_ALIGN) <<
"Using differential slewing. Setting Target Coordinates to RA:"
2782 void Align::getFormattedCoords(
double ra,
double dec,
QString &ra_str,
QString &dec_str)
2793 dec_str =
QString(
"-%1:%2:%3")
2808 "Images (*.fits *.fits.fz *.fit *.fts *.xisf "
2809 "*.jpg *.jpeg *.png *.gif *.bmp "
2810 "*.cr2 *.cr3 *.crw *.nef *.raf *.dng *.arw *.orf)");
2816 if (fileInfo.
exists() ==
false)
2821 differentialSlewingActivated =
false;
2823 m_SolveFromFile =
true;
2825 if (m_PolarAlignmentAssistant)
2826 m_PolarAlignmentAssistant->stopPAHProcess();
2828 slewR->setChecked(
true);
2829 m_CurrentGotoMode = GOTO_SLEW;
2831 solveB->setEnabled(
false);
2832 stopB->setEnabled(
true);
2833 pi->startAnimation();
2835 if (solverModeButtonGroup->checkedId() == SOLVER_REMOTE && m_RemoteParserDevice ==
nullptr)
2837 appendLogText(
i18n(
"No remote astrometry driver detected, switching to StellarSolver."));
2841 m_ImageData.
clear();
2843 m_AlignView->loadFile(fileURL);
2852 differentialSlewingActivated =
false;
2853 m_SolveFromFile =
true;
2854 RUN_PAH(stopPAHProcess());
2855 slewR->setChecked(
true);
2856 m_CurrentGotoMode = GOTO_SLEW;
2857 solveB->setEnabled(
false);
2858 stopB->setEnabled(
true);
2859 pi->startAnimation();
2863 m_ImageData.
clear();
2866 data->loadFromBuffer(image, extension);
2867 m_AlignView->loadData(data);
2874 alignExposure->setValue(value);
2882 alignBinning->blockSignals(
true);
2883 alignBinning->setCurrentIndex(binIndex);
2884 alignBinning->blockSignals(
false);
2888 if (Options::astrometryImageScaleUnits() == SSolver::ARCSEC_PER_PIX)
2894 if (m_FilterWheel && m_FilterWheel == device)
2901 m_FilterWheel->disconnect(
this);
2903 m_FilterWheel = device;
2907 connect(m_FilterWheel, &ISD::ConcreteDevice::Connected,
this, [
this]()
2909 FilterPosLabel->setEnabled(
true);
2910 alignFilter->setEnabled(
true);
2912 connect(m_FilterWheel, &ISD::ConcreteDevice::Disconnected,
this, [
this]()
2914 FilterPosLabel->setEnabled(
false);
2915 alignFilter->setEnabled(
false);
2919 auto isConnected = m_FilterWheel && m_FilterWheel->isConnected();
2920 FilterPosLabel->setEnabled(isConnected);
2921 alignFilter->setEnabled(isConnected);
2930 return m_FilterWheel->getDeviceName();
2939 alignFilter->setCurrentText(filter);
2949 return alignFilter->currentText();
2954 alignFilter->clear();
2958 FilterPosLabel->setEnabled(
false);
2959 alignFilter->setEnabled(
false);
2963 auto isConnected = m_FilterWheel->isConnected();
2964 FilterPosLabel->setEnabled(isConnected);
2965 alignFilter->setEnabled(isConnected);
2967 setupFilterManager();
2969 alignFilter->addItems(m_FilterManager->getFilterLabels());
2971 currentFilterPosition = m_FilterManager->getFilterPosition();
2974 auto filter = m_Settings[
"alignFilter"];
2975 if (filter.isValid())
2976 alignFilter->setCurrentText(filter.toString());
2979 void Align::setWCSEnabled(
bool enable)
2984 auto wcsControl = m_Camera->
getSwitch(
"WCS_CONTROL");
2989 auto wcs_enable = wcsControl->findWidgetByName(
"WCS_ENABLE");
2990 auto wcs_disable = wcsControl->findWidgetByName(
"WCS_DISABLE");
2992 if (!wcs_enable || !wcs_disable)
2995 if ((wcs_enable->getState() == ISS_ON && enable) || (wcs_disable->getState() == ISS_ON && !enable))
2998 wcsControl->reset();
3001 appendLogText(
i18n(
"World Coordinate System (WCS) is enabled."));
3002 wcs_enable->setState(ISS_ON);
3006 appendLogText(
i18n(
"World Coordinate System (WCS) is disabled."));
3007 wcs_disable->setState(ISS_ON);
3008 m_wcsSynced =
false;
3011 auto clientManager = m_Camera->getDriverInfo()->getClientManager();
3013 clientManager->sendNewProperty(wcsControl);
3018 INDI_UNUSED(targetChip);
3019 INDI_UNUSED(remaining);
3021 if (state == IPS_ALERT)
3023 if (++m_CaptureErrorCounter == 3 && !matchPAHStage(PolarAlignmentAssistant::PAH_REFRESH))
3025 appendLogText(
i18n(
"Capture error. Aborting..."));
3030 appendLogText(
i18n(
"Restarting capture attempt #%1", m_CaptureErrorCounter));
3031 setAlignTableResult(ALIGN_RESULT_FAILED);
3036 void Align::setAlignTableResult(AlignResult result)
3043 if (progress_indicator ==
nullptr || ! progress_indicator->
isAnimated())
3045 stopProgressAnimation();
3050 case ALIGN_RESULT_SUCCESS:
3051 icon =
QIcon(
":/icons/AlignSuccess.svg");
3054 case ALIGN_RESULT_WARNING:
3055 icon =
QIcon(
":/icons/AlignWarning.svg");
3058 case ALIGN_RESULT_FAILED:
3060 icon =
QIcon(
":/icons/AlignFailure.svg");
3063 int currentRow = solutionTable->rowCount() - 1;
3064 solutionTable->setCellWidget(currentRow, 3,
new QWidget());
3068 solutionTable->setItem(currentRow, 3, statusReport);
3071 void Align::setFocusStatus(Ekos::FocusState state)
3073 m_FocusState = state;
3076 uint8_t Align::getSolverDownsample(uint16_t binnedW)
3078 uint8_t downsample = Options::astrometryDownsample();
3080 if (!Options::astrometryAutoDownsample())
3083 while (downsample < 8)
3085 if (binnedW / downsample <= 1024)
3099 if (m_Mount && m_Mount->hasAlignmentModel() && Options::resetMountModelAfterMeridian())
3101 qCDebug(KSTARS_EKOS_ALIGN) <<
"Post meridian flip mount model reset" << (m_Mount->clearAlignmentModel() ?
3102 "successful." :
"failed.");
3105 m_CaptureTimer.
start(alignSettlingTime->value());
3111 if (std::isnan(m_TargetPositionAngle) ==
false)
3112 m_TargetPositionAngle = SolverUtils::rangePA(m_TargetPositionAngle + 180.0);
3118 m_CaptureState = newState;
3121 void Align::showFITSViewer()
3123 static int lastFVTabID = -1;
3130 fv->loadData(m_ImageData, url, &lastFVTabID);
3132 else if (fv->updateData(m_ImageData, url, lastFVTabID, &lastFVTabID) ==
false)
3133 fv->loadData(m_ImageData, url, &lastFVTabID);
3139 void Align::toggleAlignWidgetFullScreen()
3141 if (alignWidget->parent() ==
nullptr)
3143 alignWidget->setParent(
this);
3144 rightLayout->insertWidget(0, alignWidget);
3145 alignWidget->showNormal();
3149 alignWidget->setParent(
nullptr);
3150 alignWidget->setWindowTitle(
i18nc(
"@title:window",
"Align Frame"));
3152 alignWidget->showMaximized();
3153 alignWidget->show();
3157 void Align::setMountStatus(ISD::Mount::Status newState)
3161 case ISD::Mount::MOUNT_PARKING:
3162 case ISD::Mount::MOUNT_SLEWING:
3163 case ISD::Mount::MOUNT_MOVING:
3164 solveB->setEnabled(
false);
3165 loadSlewB->setEnabled(
false);
3171 solveB->setEnabled(
true);
3172 if (matchPAHStage(PAA::PAH_IDLE))
3174 loadSlewB->setEnabled(
true);
3180 RUN_PAH(setMountStatus(newState));
3185 m_RemoteParserDevice = device;
3187 remoteSolverR->setEnabled(
true);
3188 if (remoteParser.get() !=
nullptr)
3190 remoteParser->setAstrometryDevice(m_RemoteParserDevice);
3198 if (device == m_Rotator)
3215 solverFOV->setImageDisplay(Options::astrometrySolverWCS());
3216 m_AlignTimer.
setInterval(Options::astrometryTimeout() * 1000);
3219 void Align::setupOptions()
3227 opsAlign =
new OpsAlign(
this);
3233 opsPrograms =
new OpsPrograms(
this);
3234 page = dialog->
addPage(opsPrograms,
i18n(
"External & Online Programs"));
3237 opsAstrometry =
new OpsAstrometry(
this);
3238 page = dialog->
addPage(opsAstrometry,
i18n(
"Scale & Position"));
3239 page->
setIcon(
QIcon(
":/icons/center_telescope_red.svg"));
3241 optionsProfileEditor =
new StellarSolverProfileEditor(
this, Ekos::AlignProfiles, dialog);
3242 page = dialog->
addPage(optionsProfileEditor,
i18n(
"Align Options Profiles Editor"));
3243 connect(optionsProfileEditor, &StellarSolverProfileEditor::optionsProfilesUpdated,
this, [
this]()
3245 if(
QFile(savedOptionsProfiles).exists())
3246 m_StellarSolverProfiles = StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles);
3248 m_StellarSolverProfiles = getDefaultAlignOptionsProfiles();
3249 opsAlign->reloadOptionsProfiles();
3253 connect(opsAlign, &OpsAlign::needToLoadProfile,
this, [
this, dialog, page](
const QString & profile)
3255 optionsProfileEditor->loadProfile(profile);
3259 opsAstrometryIndexFiles =
new OpsAstrometryIndexFiles(
this);
3260 m_IndexFilesPage = dialog->
addPage(opsAstrometryIndexFiles,
i18n(
"Index Files"));
3264 void Align::setupSolutionTable()
3268 clearAllSolutionsB->setIcon(
3275 exportSolutionsCSV->setIcon(
3290 void Align::setupPlot()
3292 double accuracyRadius = alignAccuracyThreshold->value();
3295 alignPlot->setSelectionTolerance(10);
3306 alignPlot->xAxis->setTickLabelColor(
Qt::white);
3307 alignPlot->yAxis->setTickLabelColor(
Qt::white);
3309 alignPlot->xAxis->setLabelColor(
Qt::white);
3310 alignPlot->yAxis->setLabelColor(
Qt::white);
3312 alignPlot->xAxis->setLabelFont(
QFont(
font().family(), 10));
3313 alignPlot->yAxis->setLabelFont(
QFont(
font().family(), 10));
3315 alignPlot->xAxis->setLabelPadding(2);
3316 alignPlot->yAxis->setLabelPadding(2);
3325 alignPlot->xAxis->setLabel(
i18n(
"dRA (arcsec)"));
3326 alignPlot->yAxis->setLabel(
i18n(
"dDE (arcsec)"));
3328 alignPlot->xAxis->setRange(-accuracyRadius * 3, accuracyRadius * 3);
3329 alignPlot->yAxis->setRange(-accuracyRadius * 3, accuracyRadius * 3);
3334 alignPlot->addGraph();
3344 alignPlot->resize(190, 190);
3345 alignPlot->replot();
3348 void Align::setupFilterManager()
3351 if (m_FilterManager)
3352 m_FilterManager->disconnect(
this);
3355 Ekos::Manager::Instance()->createFilterManager(m_FilterWheel);
3358 Ekos::Manager::Instance()->getFilterManager(m_FilterWheel->getDeviceName(), m_FilterManager);
3360 connect(m_FilterManager.
get(), &FilterManager::ready,
this, [
this]()
3362 if (filterPositionPending)
3364 m_FocusState = FOCUS_IDLE;
3365 filterPositionPending = false;
3370 connect(m_FilterManager.get(), &FilterManager::failed,
this, [
this]()
3372 appendLogText(i18n(
"Filter operation failed."));
3377 connect(m_FilterManager.get(), &FilterManager::newStatus,
this, [
this](Ekos::FilterState filterState)
3379 if (filterPositionPending)
3381 switch (filterState)
3384 appendLogText(i18n(
"Changing focus offset by %1 steps...", m_FilterManager->getTargetFilterOffset()));
3389 const int filterComboIndex = m_FilterManager->getTargetFilterPosition() - 1;
3390 if (filterComboIndex >= 0 && filterComboIndex < alignFilter->count())
3391 appendLogText(i18n(
"Changing filter to %1...", alignFilter->itemText(filterComboIndex)));
3395 case FILTER_AUTOFOCUS:
3396 appendLogText(i18n(
"Auto focus on filter change..."));
3405 connect(m_FilterManager.get(), &FilterManager::labelsChanged,
this, &Align::checkFilter);
3406 connect(m_FilterManager.get(), &FilterManager::positionChanged,
this, &Align::checkFilter);
3409 QVariantMap Align::getEffectiveFOV()
3411 KStarsData::Instance()->
userdb()->GetAllEffectiveFOVs(effectiveFOVs);
3413 m_FOVWidth = m_FOVHeight = 0;
3415 for (
auto &map : effectiveFOVs)
3417 if (map[
"Profile"].
toString() == m_ActiveProfile->name && map[
"Train"].toString() == opticalTrain())
3419 if (isEqual(map[
"Width"].toInt(), m_CameraWidth) &&
3420 isEqual(map[
"Height"].toInt(), m_CameraHeight) &&
3421 isEqual(map[
"PixelW"].toDouble(), m_CameraPixelWidth) &&
3422 isEqual(map[
"PixelH"].toDouble(), m_CameraPixelHeight) &&
3423 isEqual(map[
"FocalLength"].toDouble(), m_FocalLength) &&
3424 isEqual(map[
"FocalRedcuer"].toDouble(), m_Reducer) &&
3425 isEqual(map[
"FocalRatio"].toDouble(), m_FocalRatio))
3427 m_FOVWidth =
map[
"FovW"].toDouble();
3428 m_FOVHeight =
map[
"FovH"].toDouble();
3434 return QVariantMap();
3437 void Align::saveNewEffectiveFOV(
double newFOVW,
double newFOVH)
3439 if (newFOVW < 0 || newFOVH < 0 || (isEqual(newFOVW, m_FOVWidth) && isEqual(newFOVH, m_FOVHeight)))
3442 QVariantMap effectiveMap = getEffectiveFOV();
3445 if (effectiveMap.isEmpty() ==
false)
3446 KStarsData::Instance()->
userdb()->DeleteEffectiveFOV(effectiveMap[
"id"].
toString());
3449 if (newFOVW == 0.0 && newFOVH == 0.0)
3455 effectiveMap[
"Profile"] = m_ActiveProfile->name;
3456 effectiveMap[
"Train"] = opticalTrainCombo->currentText();
3457 effectiveMap[
"Width"] = m_CameraWidth;
3458 effectiveMap[
"Height"] = m_CameraHeight;
3459 effectiveMap[
"PixelW"] = m_CameraPixelWidth;
3460 effectiveMap[
"PixelH"] = m_CameraPixelHeight;
3461 effectiveMap[
"FocalLength"] = m_FocalLength;
3462 effectiveMap[
"FocalReducer"] = m_Reducer;
3463 effectiveMap[
"FocalRatio"] = m_FocalRatio;
3464 effectiveMap[
"FovW"] = newFOVW;
3465 effectiveMap[
"FovH"] = newFOVH;
3467 KStarsData::Instance()->
userdb()->AddEffectiveFOV(effectiveMap);
3473 void Align::zoomAlignView()
3475 m_AlignView->ZoomDefault();
3481 emit newFrame(m_AlignView);
3485 void Align::setAlignZoom(
double scale)
3488 m_AlignView->ZoomIn();
3490 m_AlignView->ZoomOut();
3496 emit newFrame(m_AlignView);
3500 void Align::syncFOV()
3502 QString newFOV = FOVOut->text();
3505 if (
match.hasMatch())
3507 double newFOVW =
match.captured(1).toDouble();
3508 double newFOVH =
match.captured(2).toDouble();
3511 saveNewEffectiveFOV(newFOVW, newFOVH);
3513 FOVOut->setStyleSheet(
QString());
3517 KSNotification::error(
i18n(
"Invalid FOV."));
3518 FOVOut->setStyleSheet(
"background-color:red");
3523 bool Align::didSlewStart()
3525 if (m_wasSlewStarted)
3527 if (slewStartTimer.isValid() && slewStartTimer.elapsed() > MAX_WAIT_FOR_SLEW_START_MSEC)
3529 qCDebug(KSTARS_EKOS_ALIGN) <<
"Slew timed out...waited > 10s, it must have started already.";
3535 void Align::setTargetCoords(
double ra0,
double de0)
3546 m_TargetCoord = targetCoord;
3547 qCInfo(KSTARS_EKOS_ALIGN) <<
"Target coordinates updated to JNow RA:" << m_TargetCoord.
ra().
toHMSString()
3548 <<
"DE:" << m_TargetCoord.dec().toDMSString();
3553 return QList<double>() << m_TargetCoord.ra0().Hours() << m_TargetCoord.dec0().Degrees();
3556 void Align::setTargetPositionAngle(
double value)
3558 m_TargetPositionAngle = value;
3559 qCDebug(KSTARS_EKOS_ALIGN) <<
"Target PA updated to: " << m_TargetPositionAngle;
3562 void Align::calculateAlignTargetDiff()
3564 if (matchPAHStage(PAA::PAH_FIRST_CAPTURE) ||
3565 matchPAHStage(PAA::PAH_SECOND_CAPTURE) ||
3566 matchPAHStage(PAA::PAH_THIRD_CAPTURE) ||
3567 matchPAHStage(PAA::PAH_FIRST_SOLVE) ||
3568 matchPAHStage(PAA::PAH_SECOND_SOLVE) ||
3569 matchPAHStage(PAA::PAH_THIRD_SOLVE))
3571 m_TargetDiffRA = (m_AlignCoord.ra().deltaAngle(m_TargetCoord.ra())).Degrees() * 3600;
3572 m_TargetDiffDE = (m_AlignCoord.dec().deltaAngle(m_TargetCoord.dec())).Degrees() * 3600;
3574 dms RADiff(fabs(m_TargetDiffRA) / 3600.0), DEDiff(m_TargetDiffDE / 3600.0);
3576 m_TargetDiffTotal = sqrt(m_TargetDiffRA * m_TargetDiffRA + m_TargetDiffDE * m_TargetDiffDE);
3578 errOut->setText(
QString(
"%1 arcsec. RA:%2 DE:%3").arg(
3582 if (m_TargetDiffTotal <=
static_cast<double>(alignAccuracyThreshold->value()))
3583 errOut->setStyleSheet(
"color:green");
3584 else if (m_TargetDiffTotal < 1.5 * alignAccuracyThreshold->value())
3585 errOut->setStyleSheet(
"color:yellow");
3587 errOut->setStyleSheet(
"color:red");
3590 int currentRow = solutionTable->rowCount() - 1;
3597 solutionTable->setItem(currentRow, 4, dRAReport);
3606 solutionTable->setItem(currentRow, 5, dDECReport);
3609 double raPlot = m_TargetDiffRA;
3610 double decPlot = m_TargetDiffDE;
3611 alignPlot->graph(0)->addData(raPlot, decPlot);
3617 textLabel->position->
setCoords(raPlot, decPlot);
3625 if (!alignPlot->xAxis->range().contains(m_TargetDiffRA))
3627 alignPlot->graph(0)->rescaleKeyAxis(
true);
3628 alignPlot->yAxis->setScaleRatio(alignPlot->xAxis, 1.0);
3630 if (!alignPlot->yAxis->range().contains(m_TargetDiffDE))
3632 alignPlot->graph(0)->rescaleValueAxis(
true);
3633 alignPlot->xAxis->setScaleRatio(alignPlot->yAxis, 1.0);
3635 alignPlot->replot();
3641 for (
auto ¶m : m_StellarSolverProfiles)
3642 profiles << param.listName;
3647 void Align::exportSolutionPoints()
3649 if (solutionTable->rowCount() == 0)
3654 "CSV File (*.csv)");
3665 i18n(
"A file named \"%1\" already exists. "
3669 if (r == KMessageBox::Cancel)
3676 KSNotification::sorry(
message,
i18n(
"Invalid URL"));
3685 KSNotification::sorry(
message,
i18n(
"Could Not Open File"));
3693 outstream <<
"RA (J" << epoch <<
"),DE (J" << epoch
3694 <<
"),RA (degrees),DE (degrees),Name,RA Error (arcsec),DE Error (arcsec)" <<
Qt::endl;
3696 for (
int i = 0; i < solutionTable->rowCount(); i++)
3704 if (!raCell || !deCell || !objNameCell || !raErrorCell || !deErrorCell)
3706 KSNotification::sorry(
i18n(
"Error in table structure."));
3715 emit newLog(
i18n(
"Solution Points Saved as: %1", path));
3719 void Align::setupPolarAlignmentAssistant()
3722 m_PolarAlignmentAssistant =
new PolarAlignmentAssistant(
this, m_AlignView);
3724 connect(m_PolarAlignmentAssistant, &Ekos::PAA::newAlignTableResult,
this, &Ekos::Align::setAlignTableResult);
3725 connect(m_PolarAlignmentAssistant, &Ekos::PAA::newFrame,
this, &Ekos::Align::newFrame);
3726 connect(m_PolarAlignmentAssistant, &Ekos::PAA::newPAHStage,
this, &Ekos::Align::processPAHStage);
3727 connect(m_PolarAlignmentAssistant, &Ekos::PAA::newLog,
this, &Ekos::Align::appendLogText);
3729 tabWidget->addTab(m_PolarAlignmentAssistant,
i18n(
"Polar Alignment"));
3732 void Align::setupManualRotator()
3734 if (m_ManualRotator)
3737 m_ManualRotator =
new ManualRotator(
this);
3741 connect(m_ManualRotator, &Ekos::ManualRotator::rejected,
this, [
this]()
3743 m_TargetPositionAngle = std::numeric_limits<double>::quiet_NaN();
3747 void Align::setuptDarkProcessor()
3749 if (m_DarkProcessor)
3752 m_DarkProcessor =
new DarkProcessor(
this);
3753 connect(m_DarkProcessor, &DarkProcessor::newLog,
this, &Ekos::Align::appendLogText);
3754 connect(m_DarkProcessor, &DarkProcessor::darkFrameCompleted,
this, [
this](
bool completed)
3756 alignDarkFrame->setChecked(completed);
3757 m_AlignView->setProperty(
"suspended",
false);
3760 m_AlignView->rescale(ZOOM_KEEP_LEVEL);
3761 m_AlignView->updateFrame();
3763 setCaptureComplete();
3767 void Align::processPAHStage(
int stage)
3775 if (m_StellarSolver && m_StellarSolver->isRunning())
3776 m_StellarSolver->abort();
3778 case PAA::PAH_POST_REFRESH:
3780 Options::setAstrometrySolverWCS(rememberSolverWCS);
3781 Options::setAutoWCS(rememberAutoWCS);
3786 case PAA::PAH_FIRST_CAPTURE:
3787 nothingR->setChecked(
true);
3788 m_CurrentGotoMode = GOTO_NOTHING;
3789 loadSlewB->setEnabled(
false);
3791 rememberSolverWCS = Options::astrometrySolverWCS();
3792 rememberAutoWCS = Options::autoWCS();
3794 Options::setAutoWCS(
false);
3795 Options::setAstrometrySolverWCS(
true);
3797 case PAA::PAH_SECOND_CAPTURE:
3798 case PAA::PAH_THIRD_CAPTURE:
3799 if (alignSettlingTime->value() >= DELAY_THRESHOLD_NOTIFY)
3800 emit newLog(
i18n(
"Settling..."));
3801 m_CaptureTimer.start(alignSettlingTime->value());
3808 emit newPAAStage(stage);
3811 bool Align::matchPAHStage(uint32_t stage)
3813 return m_PolarAlignmentAssistant && m_PolarAlignmentAssistant->getPAHStage() == stage;
3816 void Align::toggleManualRotator(
bool toggled)
3820 m_ManualRotator->show();
3821 m_ManualRotator->raise();
3824 m_ManualRotator->close();
3827 void Align::setupOpticalTrainManager()
3829 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated,
this, &Align::refreshOpticalTrain);
3832 OpticalTrainManager::Instance()->openEditor(opticalTrainCombo->currentText());
3836 ProfileSettings::Instance()->setOneSetting(ProfileSettings::AlignOpticalTrain,
3837 OpticalTrainManager::Instance()->
id(opticalTrainCombo->itemText(index)));
3838 refreshOpticalTrain();
3839 emit trainChanged();
3841 refreshOpticalTrain();
3844 void Align::refreshOpticalTrain()
3846 opticalTrainCombo->blockSignals(
true);
3847 opticalTrainCombo->clear();
3848 opticalTrainCombo->addItems(OpticalTrainManager::Instance()->getTrainNames());
3849 trainB->setEnabled(
true);
3851 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::AlignOpticalTrain);
3855 auto id = trainID.
toUInt();
3858 if (OpticalTrainManager::Instance()->exists(
id) ==
false)
3860 id = OpticalTrainManager::Instance()->id(opticalTrainCombo->itemText(0));
3861 ProfileSettings::Instance()->setOneSetting(ProfileSettings::AlignOpticalTrain,
id);
3864 auto name = OpticalTrainManager::Instance()->name(
id);
3866 opticalTrainCombo->setCurrentText(name);
3868 auto scope = OpticalTrainManager::Instance()->getScope(name);
3869 m_FocalLength = scope[
"focal_length"].toDouble(-1);
3870 m_Aperture = scope[
"aperture"].toDouble(-1);
3871 m_FocalRatio = scope[
"focal_ratio"].toDouble(-1);
3872 m_Reducer = OpticalTrainManager::Instance()->getReducer(name);
3875 if (m_Aperture < 0 && m_FocalRatio > 0)
3876 m_Aperture = m_FocalLength * m_FocalRatio;
3878 auto mount = OpticalTrainManager::Instance()->getMount(name);
3881 auto camera = OpticalTrainManager::Instance()->getCamera(name);
3884 camera->setScopeInfo(m_FocalLength * m_Reducer, m_Aperture);
3885 opticalTrainCombo->setToolTip(
QString(
"%1 @ %2").arg(camera->getDeviceName(), scope[
"name"].toString()));
3889 syncTelescopeInfo();
3891 auto filterWheel = OpticalTrainManager::Instance()->getFilterWheel(name);
3892 setFilterWheel(filterWheel);
3894 auto rotator = OpticalTrainManager::Instance()->getRotator(name);
3895 setRotator(rotator);
3898 OpticalTrainSettings::Instance()->setOpticalTrainID(
id);
3899 auto settings = OpticalTrainSettings::Instance()->getOneSetting(OpticalTrainSettings::Align);
3900 if (settings.isValid())
3901 setAllSettings(settings.toJsonObject().toVariantMap());
3903 m_Settings = m_GlobalSettings;
3906 Options::setTelescopeFocalLength(m_FocalLength);
3907 Options::setCameraPixelWidth(m_CameraPixelWidth);
3908 Options::setCameraPixelHeight(m_CameraPixelHeight);
3909 Options::setCameraWidth(m_CameraWidth);
3910 Options::setCameraHeight(m_CameraHeight);
3913 opticalTrainCombo->blockSignals(
false);
3916 void Align::syncSettings()
3927 if ( (dsb = qobject_cast<QDoubleSpinBox*>(sender())))
3930 value = dsb->
value();
3933 else if ( (sb = qobject_cast<QSpinBox*>(sender())))
3936 value = sb->
value();
3938 else if ( (cb = qobject_cast<QCheckBox*>(sender())))
3943 else if ( (cbox = qobject_cast<QComboBox*>(sender())))
3948 else if ( (cradio = qobject_cast<QRadioButton*>(sender())))
3957 if (m_Settings.contains(key))
3959 m_Settings.remove(key);
3960 emit settingsUpdated(getAllSettings());
3961 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->
id(opticalTrainCombo->currentText()));
3962 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Align, m_Settings);
3971 Options::self()->setProperty(key.
toLatin1(), value);
3972 Options::self()->save();
3974 m_Settings[key] = value;
3975 m_GlobalSettings[key] = value;
3977 emit settingsUpdated(getAllSettings());
3980 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->
id(opticalTrainCombo->currentText()));
3981 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Align, m_Settings);
3984 void Align::loadGlobalSettings()
3989 QVariantMap settings;
3991 for (
auto &oneWidget : findChildren<QComboBox*>())
3993 if (oneWidget->objectName() ==
"opticalTrainCombo")
3996 key = oneWidget->objectName();
3997 value = Options::self()->property(key.
toLatin1());
4000 oneWidget->setCurrentText(value.
toString());
4001 settings[key] = value;
4006 for (
auto &oneWidget : findChildren<QDoubleSpinBox*>())
4008 key = oneWidget->objectName();
4009 value = Options::self()->property(key.
toLatin1());
4013 settings[key] = value;
4018 for (
auto &oneWidget : findChildren<QSpinBox*>())
4020 key = oneWidget->objectName();
4021 value = Options::self()->property(key.
toLatin1());
4025 settings[key] = value;
4030 for (
auto &oneWidget : findChildren<QCheckBox*>())
4032 key = oneWidget->objectName();
4033 value = Options::self()->property(key.
toLatin1());
4036 oneWidget->setChecked(value.
toBool());
4037 settings[key] = value;
4041 m_GlobalSettings = m_Settings = settings;
4045 void Align::connectSettings()
4048 for (
auto &oneWidget : findChildren<QComboBox*>())
4049 connect(oneWidget, QOverload<int>::of(&
QComboBox::activated),
this, &Ekos::Align::syncSettings);
4052 for (
auto &oneWidget : findChildren<QDoubleSpinBox*>())
4056 for (
auto &oneWidget : findChildren<QSpinBox*>())
4060 for (
auto &oneWidget : findChildren<QCheckBox*>())
4064 for (
auto &oneWidget : findChildren<QRadioButton*>())
4068 disconnect(opticalTrainCombo, QOverload<int>::of(&
QComboBox::activated),
this, &Ekos::Align::syncSettings);
4071 void Align::disconnectSettings()
4074 for (
auto &oneWidget : findChildren<QComboBox*>())
4075 disconnect(oneWidget, QOverload<int>::of(&
QComboBox::activated),
this, &Ekos::Align::syncSettings);
4078 for (
auto &oneWidget : findChildren<QDoubleSpinBox*>())
4082 for (
auto &oneWidget : findChildren<QSpinBox*>())
4086 for (
auto &oneWidget : findChildren<QCheckBox*>())
4090 for (
auto &oneWidget : findChildren<QRadioButton*>())
4098 QVariantMap Align::getAllSettings()
const
4100 QVariantMap settings;
4103 for (
auto &oneWidget : findChildren<QComboBox*>())
4104 settings.insert(oneWidget->objectName(), oneWidget->currentText());
4107 for (
auto &oneWidget : findChildren<QDoubleSpinBox*>())
4108 settings.insert(oneWidget->objectName(), oneWidget->value());
4111 for (
auto &oneWidget : findChildren<QSpinBox*>())
4112 settings.insert(oneWidget->objectName(), oneWidget->value());
4115 for (
auto &oneWidget : findChildren<QCheckBox*>())
4116 settings.insert(oneWidget->objectName(), oneWidget->isChecked());
4124 void Align::setAllSettings(
const QVariantMap &settings)
4128 disconnectSettings();
4130 for (
auto &name : settings.keys())
4133 auto comboBox = findChild<QComboBox*>(name);
4136 syncControl(settings, name, comboBox);
4141 auto doubleSpinBox = findChild<QDoubleSpinBox*>(name);
4144 syncControl(settings, name, doubleSpinBox);
4149 auto spinBox = findChild<QSpinBox*>(name);
4152 syncControl(settings, name, spinBox);
4157 auto checkbox = findChild<QCheckBox*>(name);
4160 syncControl(settings, name, checkbox);
4165 auto radioButton = findChild<QRadioButton*>(name);
4168 syncControl(settings, name, radioButton);
4174 for (
auto &key : settings.keys())
4176 auto value = settings[key];
4178 Options::self()->setProperty(key.
toLatin1(), value);
4179 Options::self()->save();
4181 m_Settings[key] = value;
4182 m_GlobalSettings[key] = value;
4185 emit settingsUpdated(getAllSettings());
4188 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->
id(opticalTrainCombo->currentText()));
4189 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Align, m_Settings);
4198 bool Align::syncControl(
const QVariantMap &settings,
const QString &key,
QWidget * widget)
4207 if ((pSB = qobject_cast<QSpinBox *>(widget)))
4209 const int value = settings[key].
toInt(&ok);
4216 else if ((pDSB = qobject_cast<QDoubleSpinBox *>(widget)))
4218 const double value = settings[key].toDouble(&ok);
4225 else if ((pCB = qobject_cast<QCheckBox *>(widget)))
4227 const bool value = settings[key].toBool();
4232 else if ((pComboBox = qobject_cast<QComboBox *>(widget)))
4234 const QString value = settings[key].toString();
4238 else if ((pRadioButton = qobject_cast<QRadioButton *>(widget)))
4240 const bool value = settings[key].toBool();
4248 void Align::processCaptureTimeout()
4250 if (m_CaptureTimeoutCounter++ > 3)
4252 appendLogText(
i18n(
"Capture timed out."));
4253 m_CaptureTimer.stop();
4258 ISD::CameraChip *targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
4259 if (targetChip->isCapturing())
4261 appendLogText(
i18n(
"Capturing still running, Retrying in %1 seconds...", m_CaptureTimer.interval() / 500));
4262 targetChip->abortExposure();
4263 m_CaptureTimer.start( m_CaptureTimer.interval() * 2);
4267 setAlignTableResult(ALIGN_RESULT_FAILED);