File indexing completed on 2024-05-12 07:36:27
0001 /* 0002 SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mount.h" 0008 0009 #include <QQuickView> 0010 #include <QQuickItem> 0011 #include <indicom.h> 0012 0013 #include <KNotifications/KNotification> 0014 #include <KLocalizedContext> 0015 #include <KActionCollection> 0016 0017 #include "Options.h" 0018 0019 #include "ksmessagebox.h" 0020 #include "indi/driverinfo.h" 0021 #include "indi/indicommon.h" 0022 #include "indi/clientmanager.h" 0023 #include "indi/indigps.h" 0024 0025 0026 #include "mountadaptor.h" 0027 0028 #include "ekos/manager.h" 0029 #include "ekos/auxiliary/opticaltrainmanager.h" 0030 #include "ekos/auxiliary/profilesettings.h" 0031 #include "ekos/auxiliary/opticaltrainsettings.h" 0032 #include "ekos/manager/meridianflipstate.h" 0033 #include "ekos/align/polaralignmentassistant.h" 0034 0035 #include "kstars.h" 0036 #include "skymapcomposite.h" 0037 #include "dialogs/finddialog.h" 0038 #include "kstarsdata.h" 0039 0040 #include <basedevice.h> 0041 0042 #include <ekos_mount_debug.h> 0043 0044 extern const char *libindi_strings_context; 0045 0046 #define ABORT_DISPATCH_LIMIT 3 0047 0048 namespace Ekos 0049 { 0050 0051 Mount::Mount() 0052 { 0053 setupUi(this); 0054 0055 new MountAdaptor(this); 0056 QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Mount", this); 0057 // Set up DBus interfaces 0058 QPointer<QDBusInterface> ekosInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos", 0059 QDBusConnection::sessionBus(), this); 0060 qDBusRegisterMetaType<SkyPoint>(); 0061 0062 // Connecting DBus signals 0063 QDBusConnection::sessionBus().connect("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos", "newModule", this, 0064 SLOT(registerNewModule(QString))); 0065 0066 m_Mount = nullptr; 0067 0068 // initialize the state machine 0069 mf_state.reset(new MeridianFlipState()); 0070 // connect to the MF state maichine 0071 getMeridianFlipState()->connectMount(this); 0072 0073 // set the status message in the mount tab and write it to the log 0074 connect(mf_state.get(), &MeridianFlipState::newMeridianFlipMountStatusText, [&](const QString & text) 0075 { 0076 meridianFlipStatusWidget->setStatus(text); 0077 if (mf_state->getMeridianFlipMountState() != MeridianFlipState::MOUNT_FLIP_NONE && 0078 mf_state->getMeridianFlipMountState() != MeridianFlipState::MOUNT_FLIP_PLANNED) 0079 appendLogText(text); 0080 }); 0081 connect(mountToolBoxB, &QPushButton::clicked, this, &Mount::toggleMountToolBox); 0082 0083 connect(clearAlignmentModelB, &QPushButton::clicked, this, &Mount::resetModel); 0084 0085 connect(clearParkingB, &QPushButton::clicked, this, [this]() 0086 { 0087 if (m_Mount) 0088 m_Mount->clearParking(); 0089 0090 }); 0091 0092 connect(purgeConfigB, &QPushButton::clicked, this, [this]() 0093 { 0094 if (m_Mount) 0095 { 0096 if (KMessageBox::questionYesNo(KStars::Instance(), 0097 i18n("Are you sure you want to clear all mount configurations?"), 0098 i18n("Mount Configuration"), KStandardGuiItem::yes(), KStandardGuiItem::no(), 0099 "purge_mount_settings_dialog") == KMessageBox::Yes) 0100 { 0101 resetModel(); 0102 m_Mount->clearParking(); 0103 m_Mount->setConfig(PURGE_CONFIG); 0104 } 0105 } 0106 }); 0107 0108 connect(enableAltitudeLimits, &QCheckBox::toggled, this, [this](bool toggled) 0109 { 0110 m_AltitudeLimitEnabled = toggled; 0111 setAltitudeLimits(toggled); 0112 0113 }); 0114 m_AltitudeLimitEnabled = enableAltitudeLimits->isChecked(); 0115 connect(enableHaLimit, &QCheckBox::toggled, this, &Mount::enableHourAngleLimits); 0116 0117 // meridian flip 0118 connect(mf_state.get(), &MeridianFlipState::newMeridianFlipMountStatusText, meridianFlipStatusWidget, 0119 &MeridianFlipStatusWidget::setStatus); 0120 0121 connect(&autoParkTimer, &QTimer::timeout, this, &Mount::startAutoPark); 0122 connect(startTimerB, &QPushButton::clicked, this, &Mount::startParkTimer); 0123 connect(stopTimerB, &QPushButton::clicked, this, &Mount::stopParkTimer); 0124 0125 stopTimerB->setEnabled(false); 0126 0127 if (parkEveryDay->isChecked()) 0128 startTimerB->animateClick(); 0129 0130 // QML Stuff 0131 m_BaseView = new QQuickView(); 0132 0133 // Must set context *before* loading the QML file. 0134 m_Ctxt = m_BaseView->rootContext(); 0135 ///Use instead of KDeclarative 0136 m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView)); 0137 0138 m_Ctxt->setContextProperty("mount", this); 0139 0140 // Load QML file after setting context 0141 m_BaseView->setSource(QUrl("qrc:/qml/mount/mountbox.qml")); 0142 0143 m_BaseView->setTitle(i18n("Mount Control")); 0144 #ifdef Q_OS_OSX 0145 m_BaseView->setFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0146 #else 0147 m_BaseView->setFlags(Qt::WindowStaysOnTopHint | Qt::WindowCloseButtonHint); 0148 #endif 0149 0150 // Theming? 0151 m_BaseView->setColor(Qt::black); 0152 0153 m_BaseObj = m_BaseView->rootObject(); 0154 0155 m_BaseView->setResizeMode(QQuickView::SizeViewToRootObject); 0156 0157 m_SpeedSlider = m_BaseObj->findChild<QQuickItem *>("speedSliderObject"); 0158 m_SpeedLabel = m_BaseObj->findChild<QQuickItem *>("speedLabelObject"); 0159 m_raValue = m_BaseObj->findChild<QQuickItem *>("raValueObject"); 0160 m_deValue = m_BaseObj->findChild<QQuickItem *>("deValueObject"); 0161 m_azValue = m_BaseObj->findChild<QQuickItem *>("azValueObject"); 0162 m_altValue = m_BaseObj->findChild<QQuickItem *>("altValueObject"); 0163 m_haValue = m_BaseObj->findChild<QQuickItem *>("haValueObject"); 0164 m_zaValue = m_BaseObj->findChild<QQuickItem *>("zaValueObject"); 0165 m_targetText = m_BaseObj->findChild<QQuickItem *>("targetTextObject"); 0166 m_targetRAText = m_BaseObj->findChild<QQuickItem *>("targetRATextObject"); 0167 m_targetDEText = m_BaseObj->findChild<QQuickItem *>("targetDETextObject"); 0168 m_J2000Check = m_BaseObj->findChild<QQuickItem *>("j2000CheckObject"); 0169 m_JNowCheck = m_BaseObj->findChild<QQuickItem *>("jnowCheckObject"); 0170 m_Park = m_BaseObj->findChild<QQuickItem *>("parkButtonObject"); 0171 m_Unpark = m_BaseObj->findChild<QQuickItem *>("unparkButtonObject"); 0172 m_statusText = m_BaseObj->findChild<QQuickItem *>("statusTextObject"); 0173 m_equatorialCheck = m_BaseObj->findChild<QQuickItem *>("equatorialCheckObject"); 0174 m_horizontalCheck = m_BaseObj->findChild<QQuickItem *>("horizontalCheckObject"); 0175 m_haEquatorialCheck = m_BaseObj->findChild<QQuickItem *>("haEquatorialCheckObject"); 0176 m_leftRightCheck = m_BaseObj->findChild<QQuickItem *>("leftRightCheckObject"); 0177 m_upDownCheck = m_BaseObj->findChild<QQuickItem *>("upDownCheckObject"); 0178 0179 m_leftRightCheck->setProperty("checked", Options::leftRightReversed()); 0180 m_upDownCheck->setProperty("checked", Options::upDownReversed()); 0181 0182 //Note: This is to prevent a button from being called the default button 0183 //and then executing when the user hits the enter key such as when on a Text Box 0184 QList<QPushButton *> qButtons = findChildren<QPushButton *>(); 0185 for (auto &button : qButtons) 0186 button->setAutoDefault(false); 0187 0188 loadGlobalSettings(); 0189 connectSettings(); 0190 0191 setupOpticalTrainManager(); 0192 } 0193 0194 Mount::~Mount() 0195 { 0196 autoParkTimer.stop(); 0197 delete(m_Ctxt); 0198 delete(m_BaseObj); 0199 } 0200 0201 void Mount::setupParkUI() 0202 { 0203 if (m_Mount == nullptr) 0204 return; 0205 0206 if (m_Mount->canPark()) 0207 { 0208 switch(m_Mount->parkStatus()) 0209 { 0210 case ISD::PARK_PARKED: 0211 parkingTitle->setTitle("Parked"); 0212 break; 0213 case ISD::PARK_PARKING: 0214 parkingTitle->setTitle("Parking"); 0215 break; 0216 case ISD::PARK_UNPARKING: 0217 parkingTitle->setTitle("Unparking"); 0218 break; 0219 case ISD::PARK_UNPARKED: 0220 parkingTitle->setTitle("Unparked"); 0221 break; 0222 case ISD::PARK_ERROR: 0223 parkingTitle->setTitle("Park Error"); 0224 break; 0225 case ISD::PARK_UNKNOWN: 0226 parkingTitle->setTitle("Park Status Unknown"); 0227 break; 0228 } 0229 parkB->setEnabled(m_Mount->parkStatus() == ISD::PARK_UNPARKED); 0230 unparkB->setEnabled(m_Mount->parkStatus() == ISD::PARK_PARKED); 0231 } 0232 else 0233 { 0234 parkB->setEnabled(false); 0235 unparkB->setEnabled(false); 0236 parkingTitle->setTitle(""); 0237 } 0238 } 0239 0240 bool Mount::setMount(ISD::Mount *device) 0241 { 0242 if (device && device == m_Mount) 0243 { 0244 syncTelescopeInfo(); 0245 return false; 0246 } 0247 0248 if (m_Mount) 0249 m_Mount->disconnect(m_Mount, nullptr, this, nullptr); 0250 0251 m_Mount = device; 0252 0253 if (m_Mount) 0254 { 0255 connect(m_Mount, &ISD::ConcreteDevice::Connected, this, [this]() 0256 { 0257 setEnabled(true); 0258 }); 0259 connect(m_Mount, &ISD::ConcreteDevice::Disconnected, this, [this]() 0260 { 0261 setEnabled(false); 0262 opticalTrainCombo->setEnabled(true); 0263 trainLabel->setEnabled(true); 0264 }); 0265 } 0266 else 0267 return false; 0268 0269 mainLayout->setEnabled(true); 0270 0271 // forward the new mount to the meridian flip state machine 0272 mf_state->setMountConnected(device != nullptr); 0273 0274 if (m_GPS != nullptr) 0275 syncGPS(); 0276 0277 connect(m_Mount, &ISD::Mount::propertyUpdated, this, &Mount::updateProperty); 0278 connect(m_Mount, &ISD::Mount::newTarget, this, &Mount::newTarget); 0279 connect(m_Mount, &ISD::Mount::newTargetName, this, &Mount::newTargetName); 0280 connect(m_Mount, &ISD::Mount::newCoords, this, &Mount::newCoords); 0281 connect(m_Mount, &ISD::Mount::newCoords, this, &Mount::updateTelescopeCoords); 0282 connect(m_Mount, &ISD::Mount::slewRateChanged, this, &Mount::slewRateChanged); 0283 connect(m_Mount, &ISD::Mount::pierSideChanged, this, &Mount::pierSideChanged); 0284 connect(m_Mount, &ISD::Mount::axisReversed, this, &Mount::syncAxisReversed); 0285 connect(m_Mount, &ISD::Mount::Disconnected, this, [this]() 0286 { 0287 m_BaseView->hide(); 0288 }); 0289 connect(m_Mount, &ISD::Mount::newParkStatus, this, [&](ISD::ParkStatus status) 0290 { 0291 m_ParkStatus = status; 0292 emit newParkStatus(status); 0293 0294 setupParkUI(); 0295 0296 // If mount is unparked AND every day auto-paro check is ON 0297 // AND auto park timer is not yet started, we try to initiate it. 0298 if (status == ISD::PARK_UNPARKED && parkEveryDay->isChecked() && autoParkTimer.isActive() == false) 0299 startTimerB->animateClick(); 0300 }); 0301 0302 // If mount is ready then let's set it up. 0303 if (m_Mount->isReady()) 0304 { 0305 if (enableAltitudeLimits->isChecked()) 0306 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value()); 0307 else 0308 m_Mount->setAltLimits(-91, +91); 0309 0310 syncTelescopeInfo(); 0311 0312 // Send initial status 0313 m_Status = m_Mount->status(); 0314 emit newStatus(m_Status); 0315 0316 m_ParkStatus = m_Mount->parkStatus(); 0317 emit newParkStatus(m_ParkStatus); 0318 emit ready(); 0319 } 0320 // Otherwise, let's wait for mount to be ready 0321 else 0322 { 0323 connect(m_Mount, &ISD::Mount::ready, this, [this]() 0324 { 0325 if (enableAltitudeLimits->isChecked()) 0326 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value()); 0327 else 0328 m_Mount->setAltLimits(-91, +91); 0329 0330 syncTelescopeInfo(); 0331 0332 // Send initial status 0333 m_Status = m_Mount->status(); 0334 emit newStatus(m_Status); 0335 0336 m_ParkStatus = m_Mount->parkStatus(); 0337 emit newParkStatus(m_ParkStatus); 0338 emit ready(); 0339 }); 0340 } 0341 0342 return true; 0343 } 0344 0345 bool Mount::addGPS(ISD::GPS * device) 0346 { 0347 // No duplicates 0348 for (auto &oneGPS : m_GPSes) 0349 { 0350 if (oneGPS->getDeviceName() == device->getDeviceName()) 0351 return false; 0352 } 0353 0354 for (auto &oneGPS : m_GPSes) 0355 oneGPS->disconnect(this); 0356 0357 m_GPSes.append(device); 0358 0359 auto executeSetGPS = [this, device]() 0360 { 0361 m_GPS = device; 0362 connect(m_GPS, &ISD::GPS::propertyUpdated, this, &Ekos::Mount::updateProperty, Qt::UniqueConnection); 0363 appendLogText(i18n("GPS driver detected. KStars and mount time and location settings are now synced to the GPS driver.")); 0364 syncGPS(); 0365 }; 0366 0367 if (Options::useGPSSource() == false) 0368 { 0369 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeSetGPS]() 0370 { 0371 KSMessageBox::Instance()->disconnect(this); 0372 Options::setUseKStarsSource(false); 0373 Options::setUseMountSource(false); 0374 Options::setUseGPSSource(true); 0375 executeSetGPS(); 0376 }); 0377 0378 KSMessageBox::Instance()->questionYesNo(i18n("GPS is detected. Do you want to switch time and location source to GPS?"), 0379 i18n("GPS Settings"), 10); 0380 } 0381 else 0382 executeSetGPS(); 0383 0384 return true; 0385 } 0386 0387 void Mount::syncGPS() 0388 { 0389 // We only update when location is OK 0390 auto location = m_GPS->getNumber("GEOGRAPHIC_COORD"); 0391 if (!location || location->getState() != IPS_OK) 0392 return; 0393 0394 // Sync name 0395 if (m_Mount) 0396 { 0397 auto activeDevices = m_Mount->getText("ACTIVE_DEVICES"); 0398 if (activeDevices) 0399 { 0400 auto activeGPS = activeDevices->findWidgetByName("ACTIVE_GPS"); 0401 if (activeGPS) 0402 { 0403 if (activeGPS->getText() != m_GPS->getDeviceName()) 0404 { 0405 activeGPS->setText(m_GPS->getDeviceName().toLatin1().constData()); 0406 m_Mount->sendNewProperty(activeDevices); 0407 } 0408 } 0409 } 0410 } 0411 0412 // GPS Refresh should only be called once automatically. 0413 if (GPSInitialized == false) 0414 { 0415 auto refreshGPS = m_GPS->getSwitch("GPS_REFRESH"); 0416 if (refreshGPS) 0417 { 0418 refreshGPS->at(0)->setState(ISS_ON); 0419 m_GPS->sendNewProperty(refreshGPS); 0420 GPSInitialized = true; 0421 } 0422 } 0423 } 0424 0425 void Mount::removeDevice(const QSharedPointer<ISD::GenericDevice> &device) 0426 { 0427 if (m_Mount && m_Mount->getDeviceName() == device->getDeviceName()) 0428 { 0429 m_Mount->disconnect(this); 0430 m_BaseView->hide(); 0431 qCDebug(KSTARS_EKOS_MOUNT) << "Removing mount driver" << m_Mount->getDeviceName(); 0432 m_Mount = nullptr; 0433 } 0434 0435 for (auto &oneGPS : m_GPSes) 0436 { 0437 if (oneGPS->getDeviceName() == device->getDeviceName()) 0438 { 0439 oneGPS->disconnect(this); 0440 m_GPSes.removeOne(oneGPS); 0441 m_GPS = nullptr; 0442 break; 0443 } 0444 } 0445 } 0446 0447 void Mount::syncTelescopeInfo() 0448 { 0449 if (!m_Mount || m_Mount->isConnected() == false) 0450 return; 0451 0452 auto svp = m_Mount->getSwitch("TELESCOPE_SLEW_RATE"); 0453 0454 if (svp) 0455 { 0456 int index = svp->findOnSwitchIndex(); 0457 0458 // QtQuick 0459 m_SpeedSlider->setEnabled(true); 0460 m_SpeedSlider->setProperty("maximumValue", svp->count() - 1); 0461 m_SpeedSlider->setProperty("value", index); 0462 0463 m_SpeedLabel->setProperty("text", i18nc(libindi_strings_context, svp->at(index)->getLabel())); 0464 m_SpeedLabel->setEnabled(true); 0465 } 0466 else 0467 { 0468 // QtQuick 0469 m_SpeedSlider->setEnabled(false); 0470 m_SpeedLabel->setEnabled(false); 0471 } 0472 0473 if (m_Mount->canPark()) 0474 { 0475 connect(parkB, &QPushButton::clicked, m_Mount, &ISD::Mount::park, Qt::UniqueConnection); 0476 connect(unparkB, &QPushButton::clicked, m_Mount, &ISD::Mount::unpark, Qt::UniqueConnection); 0477 0478 // QtQuick 0479 m_Park->setEnabled(!m_Mount->isParked()); 0480 m_Unpark->setEnabled(m_Mount->isParked()); 0481 } 0482 else 0483 { 0484 disconnect(parkB, &QPushButton::clicked, m_Mount, &ISD::Mount::park); 0485 disconnect(unparkB, &QPushButton::clicked, m_Mount, &ISD::Mount::unpark); 0486 0487 // QtQuick 0488 m_Park->setEnabled(false); 0489 m_Unpark->setEnabled(false); 0490 } 0491 setupParkUI(); 0492 0493 // Tracking State 0494 svp = m_Mount->getSwitch("TELESCOPE_TRACK_STATE"); 0495 if (svp) 0496 { 0497 trackingGroup->setEnabled(true); 0498 trackOnB->disconnect(); 0499 trackOffB->disconnect(); 0500 connect(trackOnB, &QPushButton::clicked, this, [&]() 0501 { 0502 m_Mount->setTrackEnabled(true); 0503 }); 0504 connect(trackOffB, &QPushButton::clicked, this, [&]() 0505 { 0506 if (KMessageBox::questionYesNo(KStars::Instance(), 0507 i18n("Are you sure you want to turn off mount tracking?"), 0508 i18n("Mount Tracking"), KStandardGuiItem::yes(), KStandardGuiItem::no(), 0509 "turn_off_mount_tracking_dialog") == KMessageBox::Yes) 0510 m_Mount->setTrackEnabled(false); 0511 }); 0512 } 0513 else 0514 { 0515 trackOnB->setChecked(false); 0516 trackOffB->setChecked(false); 0517 trackingGroup->setEnabled(false); 0518 } 0519 0520 m_leftRightCheck->setProperty("checked", m_Mount->isReversed(AXIS_RA)); 0521 m_upDownCheck->setProperty("checked", m_Mount->isReversed(AXIS_DE)); 0522 } 0523 0524 void Mount::registerNewModule(const QString &name) 0525 { 0526 if (name == "Capture") 0527 { 0528 hasCaptureInterface = true; 0529 mf_state->setHasCaptureInterface(true); 0530 } 0531 0532 } 0533 0534 void Mount::updateTelescopeCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha) 0535 { 0536 if (m_Mount == nullptr || !m_Mount->isConnected()) 0537 return; 0538 0539 telescopeCoord = position; 0540 0541 // No need to update coords if we are still parked. 0542 if (m_Status == ISD::Mount::MOUNT_PARKED && m_Status == m_Mount->status()) 0543 return; 0544 0545 // Ekos Mount Tab coords are always in JNow 0546 raOUT->setText(telescopeCoord.ra().toHMSString()); 0547 decOUT->setText(telescopeCoord.dec().toDMSString()); 0548 0549 // Mount Control Panel coords depend on the switch 0550 if (m_JNowCheck->property("checked").toBool()) 0551 { 0552 m_raValue->setProperty("text", telescopeCoord.ra().toHMSString()); 0553 m_deValue->setProperty("text", telescopeCoord.dec().toDMSString()); 0554 } 0555 else 0556 { 0557 m_raValue->setProperty("text", telescopeCoord.ra0().toHMSString()); 0558 m_deValue->setProperty("text", telescopeCoord.dec0().toDMSString()); 0559 } 0560 0561 // Get horizontal coords 0562 azOUT->setText(telescopeCoord.az().toDMSString()); 0563 m_azValue->setProperty("text", telescopeCoord.az().toDMSString()); 0564 altOUT->setText(telescopeCoord.alt().toDMSString()); 0565 m_altValue->setProperty("text", telescopeCoord.alt().toDMSString()); 0566 0567 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 0568 dms haSigned(ha); 0569 QChar sgn('+'); 0570 0571 if (haSigned.Hours() > 12.0) 0572 { 0573 haSigned.setH(24.0 - haSigned.Hours()); 0574 sgn = '-'; 0575 } 0576 0577 haOUT->setText(QString("%1%2").arg(sgn).arg(haSigned.toHMSString())); 0578 0579 m_haValue->setProperty("text", haOUT->text()); 0580 lstOUT->setText(lst.toHMSString()); 0581 0582 double currentAlt = telescopeCoord.altRefracted().Degrees(); 0583 0584 m_zaValue->setProperty("text", dms(90 - currentAlt).toDMSString()); 0585 0586 if (minimumAltLimit->isEnabled() && (currentAlt < minimumAltLimit->value() || currentAlt > maximumAltLimit->value())) 0587 { 0588 if (currentAlt < minimumAltLimit->value()) 0589 { 0590 // Only stop if current altitude is less than last altitude indicate worse situation 0591 if (currentAlt < m_LastAltitude && 0592 (m_AbortAltDispatch == -1 || 0593 (m_Mount->isInMotion() /* && ++abortDispatch > ABORT_DISPATCH_LIMIT*/))) 0594 { 0595 appendLogText(i18n("Telescope altitude is below minimum altitude limit of %1. Aborting motion...", 0596 QString::number(minimumAltLimit->value(), 'g', 3))); 0597 m_Mount->abort(); 0598 m_Mount->setTrackEnabled(false); 0599 //KNotification::event( QLatin1String( "OperationFailed" )); 0600 KNotification::beep(); 0601 m_AbortAltDispatch++; 0602 } 0603 } 0604 else 0605 { 0606 // Only stop if current altitude is higher than last altitude indicate worse situation 0607 if (currentAlt > m_LastAltitude && 0608 (m_AbortAltDispatch == -1 || 0609 (m_Mount->isInMotion() /* && ++abortDispatch > ABORT_DISPATCH_LIMIT*/))) 0610 { 0611 appendLogText(i18n("Telescope altitude is above maximum altitude limit of %1. Aborting motion...", 0612 QString::number(maximumAltLimit->value(), 'g', 3))); 0613 m_Mount->abort(); 0614 m_Mount->setTrackEnabled(false); 0615 //KNotification::event( QLatin1String( "OperationFailed" )); 0616 KNotification::beep(); 0617 m_AbortAltDispatch++; 0618 } 0619 } 0620 } 0621 else 0622 m_AbortAltDispatch = -1; 0623 0624 //qCDebug(KSTARS_EKOS_MOUNT) << "MaximumHaLimit " << MaximumHaLimit->isEnabled() << " value " << MaximumHaLimit->value(); 0625 0626 double haHours = rangeHA(ha.Hours()); 0627 // handle Ha limit: 0628 // Telescope must report Pier Side 0629 // MaximumHaLimit must be enabled 0630 // for PierSide West -> East if Ha > MaximumHaLimit stop tracking 0631 // for PierSide East -> West if Ha > MaximumHaLimit - 12 stop Tracking 0632 if (maximumHaLimit->isEnabled()) 0633 { 0634 // get hour angle limit 0635 double haLimit = maximumHaLimit->value(); 0636 bool haLimitReached = false; 0637 switch(pierSide) 0638 { 0639 case ISD::Mount::PierSide::PIER_WEST: 0640 haLimitReached = haHours > haLimit; 0641 break; 0642 case ISD::Mount::PierSide::PIER_EAST: 0643 haLimitReached = rangeHA(haHours + 12.0) > haLimit; 0644 break; 0645 default: 0646 // can't tell so always false 0647 haLimitReached = false; 0648 break; 0649 } 0650 0651 qCDebug(KSTARS_EKOS_MOUNT) << "Ha: " << haHours << 0652 " haLimit " << haLimit << 0653 " " << ISD::Mount::pierSideStateString(m_Mount->pierSide()) << 0654 " haLimitReached " << (haLimitReached ? "true" : "false") << 0655 " lastHa " << m_LastHourAngle; 0656 0657 // compare with last ha to avoid multiple calls 0658 if (haLimitReached && (rangeHA(haHours - m_LastHourAngle) >= 0 ) && 0659 (m_AbortHADispatch == -1 || 0660 m_Mount->isInMotion())) 0661 { 0662 // moved past the limit, so stop 0663 appendLogText(i18n("Telescope hour angle is more than the maximum hour angle of %1. Aborting motion...", 0664 QString::number(maximumHaLimit->value(), 'g', 3))); 0665 m_Mount->abort(); 0666 m_Mount->setTrackEnabled(false); 0667 //KNotification::event( QLatin1String( "OperationFailed" )); 0668 KNotification::beep(); 0669 m_AbortHADispatch++; 0670 // ideally we pause and wait until we have passed the pier flip limit, 0671 // then do a pier flip and try to resume 0672 // this will need changing to use a target position because the current HA has stopped. 0673 } 0674 } 0675 else 0676 m_AbortHADispatch = -1; 0677 0678 m_LastAltitude = currentAlt; 0679 m_LastHourAngle = haHours; 0680 0681 ISD::Mount::Status currentStatus = m_Mount->status(); 0682 if (m_Status != currentStatus) 0683 { 0684 qCDebug(KSTARS_EKOS_MOUNT) << "Mount status changed from " << m_Mount->statusString(m_Status) 0685 << " to " << m_Mount->statusString(currentStatus); 0686 0687 //setScopeStatus(currentStatus); 0688 0689 m_statusText->setProperty("text", m_Mount->statusString(currentStatus)); 0690 m_Status = currentStatus; 0691 // forward 0692 emit newStatus(m_Status); 0693 0694 setupParkUI(); 0695 m_Park->setEnabled(!m_Mount->isParked()); 0696 m_Unpark->setEnabled(m_Mount->isParked()); 0697 0698 QAction *a = KStars::Instance()->actionCollection()->action("telescope_track"); 0699 if (a != nullptr) 0700 a->setChecked(currentStatus == ISD::Mount::MOUNT_TRACKING); 0701 } 0702 0703 bool isTracking = (currentStatus == ISD::Mount::MOUNT_TRACKING); 0704 if (trackingGroup->isEnabled()) 0705 { 0706 trackOnB->setChecked(isTracking); 0707 trackOffB->setChecked(!isTracking); 0708 } 0709 0710 // handle pier side display 0711 pierSideLabel->setText(ISD::Mount::pierSideStateString(m_Mount->pierSide())); 0712 0713 // Auto Park Timer 0714 if (autoParkTimer.isActive()) 0715 { 0716 QTime remainingTime(0, 0, 0); 0717 remainingTime = remainingTime.addMSecs(autoParkTimer.remainingTime()); 0718 countdownLabel->setText(remainingTime.toString("hh:mm:ss")); 0719 emit autoParkCountdownUpdated(countdownLabel->text()); 0720 } 0721 } 0722 0723 void Mount::updateProperty(INDI::Property prop) 0724 { 0725 if (prop.isNameMatch("GEOGRAPHIC_COORD") && 0726 m_GPS != nullptr && 0727 (prop.getDeviceName() == m_GPS->getDeviceName()) && 0728 prop.getState() == IPS_OK) 0729 { 0730 syncGPS(); 0731 } 0732 else if (prop.isNameMatch("EQUATORIAL_EOD_COORD") || prop.isNameMatch("EQUATORIAL_COORD")) 0733 { 0734 auto nvp = prop.getNumber(); 0735 0736 // if the meridian flip state machine is not initialized, return 0737 if (getMeridianFlipState().isNull()) 0738 return; 0739 0740 switch (getMeridianFlipState()->getMeridianFlipStage()) 0741 { 0742 case MeridianFlipState::MF_INITIATED: 0743 if (nvp->s == IPS_BUSY && m_Mount != nullptr && m_Mount->isSlewing()) 0744 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_FLIPPING); 0745 break; 0746 0747 default: 0748 break; 0749 } 0750 } 0751 else if (prop.isNameMatch("TELESCOPE_SLEW_RATE")) 0752 { 0753 auto svp = prop.getSwitch(); 0754 auto index = svp->findOnSwitchIndex(); 0755 m_SpeedSlider->setProperty("value", index); 0756 m_SpeedLabel->setProperty("text", i18nc(libindi_strings_context, svp->at(index)->getLabel())); 0757 } 0758 } 0759 0760 bool Mount::setSlewRate(int index) 0761 { 0762 if (m_Mount) 0763 return m_Mount->setSlewRate(index); 0764 0765 return false; 0766 } 0767 0768 void Mount::setUpDownReversed(bool enabled) 0769 { 0770 Options::setUpDownReversed(enabled); 0771 if (m_Mount) 0772 m_Mount->setReversedEnabled(AXIS_DE, enabled); 0773 } 0774 0775 void Mount::setLeftRightReversed(bool enabled) 0776 { 0777 Options::setLeftRightReversed(enabled); 0778 if (m_Mount) 0779 m_Mount->setReversedEnabled(AXIS_RA, enabled); 0780 } 0781 0782 void Mount::setMeridianFlipValues(bool activate, double degrees) 0783 { 0784 executeMeridianFlip->setChecked(activate); 0785 meridianFlipOffsetDegrees->setValue(degrees); 0786 } 0787 0788 void Mount::paaStageChanged(int stage) 0789 { 0790 // Clear the current target position is necessary due to a bug in some mount drivers 0791 // which report a mount slew instead of a mount motion. For these mounts, ending a slew 0792 // leads to setting the current target position, which is necessary for meridian flips 0793 // Since we want to avoid meridian flips during and after finishing PAA, it needs to 0794 // be set to nullptr. 0795 0796 if (stage != PolarAlignmentAssistant::PAH_IDLE) 0797 mf_state->clearTargetPosition(); 0798 0799 switch (stage) 0800 { 0801 // deactivate the meridian flip when the first capture is taken 0802 case PolarAlignmentAssistant::PAH_FIRST_CAPTURE: 0803 case PolarAlignmentAssistant::PAH_FIRST_SOLVE: 0804 if (mf_state->isEnabled()) 0805 { 0806 appendLogText(i18n("Meridian flip set inactive during polar alignment.")); 0807 mf_state->setEnabled(false); 0808 } 0809 break; 0810 // activate it when the last rotation is finished or stopped 0811 // for safety reasons, we add all stages after the last rotation 0812 case PolarAlignmentAssistant::PAH_THIRD_CAPTURE: 0813 case PolarAlignmentAssistant::PAH_THIRD_SOLVE: 0814 case PolarAlignmentAssistant::PAH_STAR_SELECT: 0815 case PolarAlignmentAssistant::PAH_REFRESH: 0816 case PolarAlignmentAssistant::PAH_POST_REFRESH: 0817 case PolarAlignmentAssistant::PAH_IDLE: 0818 if (executeMeridianFlip->isChecked() && mf_state->isEnabled() == false) 0819 { 0820 appendLogText(i18n("Polar alignment motions finished, meridian flip activated.")); 0821 mf_state->setEnabled(executeMeridianFlip->isChecked()); 0822 } 0823 break; 0824 } 0825 } 0826 0827 void Mount::appendLogText(const QString &text) 0828 { 0829 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", 0830 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text)); 0831 0832 qCInfo(KSTARS_EKOS_MOUNT) << text; 0833 0834 emit newLog(text); 0835 } 0836 0837 void Mount::updateLog(int messageID) 0838 { 0839 if (m_Mount == nullptr) 0840 return; 0841 0842 auto message = m_Mount->getMessage(messageID); 0843 m_LogText.insert(0, i18nc("Message shown in Ekos Mount module", "%1", message)); 0844 0845 emit newLog(message); 0846 } 0847 0848 void Mount::clearLog() 0849 { 0850 m_LogText.clear(); 0851 emit newLog(QString()); 0852 } 0853 0854 void Mount::motionCommand(int command, int NS, int WE) 0855 { 0856 if (m_Mount == nullptr || !m_Mount->isConnected()) 0857 return; 0858 0859 if (NS != -1) 0860 { 0861 m_Mount->MoveNS(static_cast<ISD::Mount::VerticalMotion>(NS), 0862 static_cast<ISD::Mount::MotionCommand>(command)); 0863 } 0864 0865 if (WE != -1) 0866 { 0867 m_Mount->MoveWE(static_cast<ISD::Mount::HorizontalMotion>(WE), 0868 static_cast<ISD::Mount::MotionCommand>(command)); 0869 } 0870 } 0871 0872 0873 void Mount::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs) 0874 { 0875 if (m_Mount == nullptr || !m_Mount->isConnected()) 0876 return; 0877 0878 m_Mount->doPulse(ra_dir, ra_msecs, dec_dir, dec_msecs); 0879 } 0880 0881 void Mount::saveLimits() 0882 { 0883 if (m_Mount == nullptr) 0884 return; 0885 0886 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value()); 0887 } 0888 0889 void Mount::setAltitudeLimits(bool enable) 0890 { 0891 if (enable) 0892 { 0893 minAltLabel->setEnabled(true); 0894 maxAltLabel->setEnabled(true); 0895 0896 minimumAltLimit->setEnabled(true); 0897 maximumAltLimit->setEnabled(true); 0898 0899 if (m_Mount) 0900 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value()); 0901 } 0902 else 0903 { 0904 minAltLabel->setEnabled(false); 0905 maxAltLabel->setEnabled(false); 0906 0907 minimumAltLimit->setEnabled(false); 0908 maximumAltLimit->setEnabled(false); 0909 0910 if (m_Mount) 0911 m_Mount->setAltLimits(-91, +91); 0912 } 0913 } 0914 0915 // Used for meridian flip 0916 void Mount::resumeAltLimits() 0917 { 0918 //Only enable if it was already enabled before and the MinimumAltLimit is currently disabled. 0919 if (m_AltitudeLimitEnabled && minimumAltLimit->isEnabled() == false) 0920 setAltitudeLimits(true); 0921 } 0922 0923 // Used for meridian flip 0924 void Mount::suspendAltLimits() 0925 { 0926 m_AltitudeLimitEnabled = enableAltitudeLimits->isChecked(); 0927 setAltitudeLimits(false); 0928 } 0929 0930 void Mount::enableHourAngleLimits(bool enable) 0931 { 0932 maxHaLabel->setEnabled(enable); 0933 maximumHaLimit->setEnabled(enable); 0934 } 0935 0936 void Mount::enableHaLimits() 0937 { 0938 //Only enable if it was already enabled before and the minHaLimit is currently disabled. 0939 if (m_HourAngleLimitEnabled && maximumHaLimit->isEnabled() == false) 0940 enableHourAngleLimits(true); 0941 } 0942 0943 void Mount::disableHaLimits() 0944 { 0945 m_HourAngleLimitEnabled = enableHaLimit->isChecked(); 0946 0947 enableHourAngleLimits(false); 0948 } 0949 0950 QList<double> Mount::altitudeLimits() 0951 { 0952 QList<double> limits; 0953 0954 limits.append(minimumAltLimit->value()); 0955 limits.append(maximumAltLimit->value()); 0956 0957 return limits; 0958 } 0959 0960 void Mount::setAltitudeLimits(QList<double> limits) 0961 { 0962 minimumAltLimit->setValue(limits[0]); 0963 maximumAltLimit->setValue(limits[1]); 0964 } 0965 0966 void Mount::setAltitudeLimitsEnabled(bool enable) 0967 { 0968 enableAltitudeLimits->setChecked(enable); 0969 } 0970 0971 bool Mount::altitudeLimitsEnabled() 0972 { 0973 return enableAltitudeLimits->isChecked(); 0974 } 0975 0976 double Mount::hourAngleLimit() 0977 { 0978 return maximumHaLimit->value(); 0979 } 0980 0981 void Mount::setHourAngleLimit(double limit) 0982 { 0983 maximumHaLimit->setValue(limit); 0984 } 0985 0986 void Mount::setHourAngleLimitEnabled(bool enable) 0987 { 0988 enableHaLimit->setChecked(enable); 0989 } 0990 0991 bool Mount::hourAngleLimitEnabled() 0992 { 0993 return enableHaLimit->isChecked(); 0994 } 0995 0996 void Mount::setJ2000Enabled(bool enabled) 0997 { 0998 m_J2000Check->setProperty("checked", enabled); 0999 } 1000 1001 bool Mount::gotoTarget(const QString &target) 1002 { 1003 SkyObject *object = KStarsData::Instance()->skyComposite()->findByName(target, false); 1004 1005 if (object != nullptr) 1006 { 1007 object->updateCoordsNow(KStarsData::Instance()->updateNum()); 1008 return slew(object->ra().Hours(), object->dec().Degrees()); 1009 } 1010 1011 return false; 1012 } 1013 1014 bool Mount::gotoTarget(const SkyPoint &target) 1015 { 1016 return slew(target.ra().Hours(), target.dec().Degrees()); 1017 } 1018 1019 bool Mount::syncTarget(const QString &target) 1020 { 1021 SkyObject *object = KStarsData::Instance()->skyComposite()->findByName(target, false); 1022 1023 if (object != nullptr) 1024 { 1025 object->updateCoordsNow(KStarsData::Instance()->updateNum()); 1026 return sync(object->ra().Hours(), object->dec().Degrees()); 1027 } 1028 1029 return false; 1030 } 1031 1032 bool Mount::slew(const QString &RA, const QString &DEC) 1033 { 1034 dms ra, de; 1035 1036 if (m_equatorialCheck->property("checked").toBool()) 1037 { 1038 ra = dms::fromString(RA, false); 1039 de = dms::fromString(DEC, true); 1040 } 1041 1042 if (m_horizontalCheck->property("checked").toBool()) 1043 { 1044 dms az = dms::fromString(RA, true); 1045 dms at = dms::fromString(DEC, true); 1046 SkyPoint target; 1047 target.setAz(az); 1048 target.setAlt(at); 1049 target.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 1050 ra = target.ra(); 1051 de = target.dec(); 1052 } 1053 1054 if (m_haEquatorialCheck->property("checked").toBool()) 1055 { 1056 dms ha = dms::fromString(RA, false); 1057 de = dms::fromString(DEC, true); 1058 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1059 ra = (lst - ha + dms(360.0)).reduce(); 1060 } 1061 1062 // If J2000 was checked and the Mount is _not_ already using native J2000 coordinates 1063 // then we need to convert J2000 to JNow. Otherwise, we send J2000 as is. 1064 if (m_J2000Check->property("checked").toBool() && m_Mount && m_Mount->isJ2000() == false) 1065 { 1066 // J2000 ---> JNow 1067 SkyPoint J2000Coord(ra, de); 1068 J2000Coord.setRA0(ra); 1069 J2000Coord.setDec0(de); 1070 J2000Coord.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd()); 1071 1072 ra = J2000Coord.ra(); 1073 de = J2000Coord.dec(); 1074 } 1075 1076 return slew(ra.Hours(), de.Degrees()); 1077 } 1078 1079 bool Mount::slew(double RA, double DEC) 1080 { 1081 if (m_Mount == nullptr || m_Mount->isConnected() == false) 1082 return false; 1083 1084 // calculate the new target 1085 targetPosition = new SkyPoint(RA, DEC); 1086 SkyPoint J2000Coord(targetPosition->ra(), targetPosition->dec()); 1087 J2000Coord.catalogueCoord(KStarsData::Instance()->ut().djd()); 1088 targetPosition->setRA0(J2000Coord.ra()); 1089 targetPosition->setDec0(J2000Coord.dec()); 1090 1091 mf_state->setTargetPosition(targetPosition); 1092 mf_state->resetMeridianFlip(); 1093 1094 qCDebug(KSTARS_EKOS_MOUNT) << "Slewing to RA=" << 1095 targetPosition->ra().toHMSString() << 1096 "DEC=" << targetPosition->dec().toDMSString(); 1097 qCDebug(KSTARS_EKOS_MOUNT) << "Initial HA " << initialHA() << ", flipDelayHrs " << mf_state->getFlipDelayHrs() << 1098 "MFStatus " << MeridianFlipState::meridianFlipStatusString(mf_state->getMeridianFlipMountState()); 1099 1100 // start the slew 1101 return(m_Mount->Slew(targetPosition)); 1102 } 1103 1104 1105 SkyPoint Mount::currentTarget() 1106 { 1107 if (targetPosition != nullptr) 1108 return *targetPosition; 1109 1110 qCWarning(KSTARS_EKOS_MOUNT) << "No target position defined!"; 1111 // since we need to answer something, we take the current mount position 1112 return telescopeCoord; 1113 } 1114 1115 1116 bool Mount::sync(const QString &RA, const QString &DEC) 1117 { 1118 dms ra, de; 1119 1120 if (m_equatorialCheck->property("checked").toBool()) 1121 { 1122 ra = dms::fromString(RA, false); 1123 de = dms::fromString(DEC, true); 1124 } 1125 1126 if (m_horizontalCheck->property("checked").toBool()) 1127 { 1128 dms az = dms::fromString(RA, true); 1129 dms at = dms::fromString(DEC, true); 1130 SkyPoint target; 1131 target.setAz(az); 1132 target.setAlt(at); 1133 target.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 1134 ra = target.ra(); 1135 de = target.dec(); 1136 } 1137 1138 if (m_haEquatorialCheck->property("checked").toBool()) 1139 { 1140 dms ha = dms::fromString(RA, false); 1141 de = dms::fromString(DEC, true); 1142 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1143 ra = (lst - ha + dms(360.0)).reduce(); 1144 } 1145 1146 if (m_J2000Check->property("checked").toBool()) 1147 { 1148 // J2000 ---> JNow 1149 SkyPoint J2000Coord(ra, de); 1150 J2000Coord.setRA0(ra); 1151 J2000Coord.setDec0(de); 1152 J2000Coord.updateCoordsNow(KStarsData::Instance()->updateNum()); 1153 1154 ra = J2000Coord.ra(); 1155 de = J2000Coord.dec(); 1156 } 1157 1158 return sync(ra.Hours(), de.Degrees()); 1159 } 1160 1161 bool Mount::sync(double RA, double DEC) 1162 { 1163 if (m_Mount == nullptr || m_Mount->isConnected() == false) 1164 return false; 1165 1166 return m_Mount->Sync(RA, DEC); 1167 } 1168 1169 bool Mount::abort() 1170 { 1171 if (m_Mount == nullptr) 1172 return false; 1173 1174 return m_Mount->abort(); 1175 } 1176 1177 IPState Mount::slewStatus() 1178 { 1179 if (m_Mount == nullptr) 1180 return IPS_ALERT; 1181 1182 return m_Mount->getState("EQUATORIAL_EOD_COORD"); 1183 } 1184 1185 QList<double> Mount::equatorialCoords() 1186 { 1187 double ra {0}, dec {0}; 1188 QList<double> coords; 1189 1190 if (m_Mount) 1191 m_Mount->getEqCoords(&ra, &dec); 1192 coords.append(ra); 1193 coords.append(dec); 1194 1195 return coords; 1196 } 1197 1198 QList<double> Mount::horizontalCoords() 1199 { 1200 QList<double> coords; 1201 1202 coords.append(telescopeCoord.az().Degrees()); 1203 coords.append(telescopeCoord.alt().Degrees()); 1204 1205 return coords; 1206 } 1207 1208 /// 1209 /// \brief Mount::hourAngle 1210 /// \return returns the current mount hour angle in hours in the range -12 to +12 1211 /// 1212 double Mount::hourAngle() 1213 { 1214 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1215 dms ha(lst.Degrees() - telescopeCoord.ra().Degrees()); 1216 return rangeHA(ha.Hours()); 1217 } 1218 1219 bool Mount::canPark() 1220 { 1221 if (m_Mount == nullptr) 1222 return false; 1223 1224 return m_Mount->canPark(); 1225 } 1226 1227 bool Mount::park() 1228 { 1229 if (m_Mount == nullptr || m_Mount->canPark() == false) 1230 return false; 1231 1232 return m_Mount->park(); 1233 } 1234 1235 bool Mount::unpark() 1236 { 1237 if (m_Mount == nullptr || m_Mount->canPark() == false) 1238 return false; 1239 1240 return m_Mount->unpark(); 1241 } 1242 1243 1244 void Mount::toggleMountToolBox() 1245 { 1246 if (m_BaseView->isVisible()) 1247 { 1248 m_BaseView->hide(); 1249 QAction *a = KStars::Instance()->actionCollection()->action("show_mount_box"); 1250 if (a) 1251 a->setChecked(false); 1252 } 1253 else 1254 { 1255 m_BaseView->show(); 1256 QAction *a = KStars::Instance()->actionCollection()->action("show_mount_box"); 1257 if (a) 1258 a->setChecked(true); 1259 } 1260 } 1261 1262 void Mount::findTarget() 1263 { 1264 if (FindDialog::Instance()->execWithParent(Ekos::Manager::Instance()) == QDialog::Accepted) 1265 { 1266 SkyObject *object = FindDialog::Instance()->targetObject(); 1267 if (object != nullptr) 1268 { 1269 KStarsData * const data = KStarsData::Instance(); 1270 1271 SkyObject *o = object->clone(); 1272 o->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 1273 1274 m_equatorialCheck->setProperty("checked", true); 1275 1276 m_targetText->setProperty("text", o->name()); 1277 1278 if (m_JNowCheck->property("checked").toBool()) 1279 { 1280 m_targetRAText->setProperty("text", o->ra().toHMSString()); 1281 m_targetDEText->setProperty("text", o->dec().toDMSString()); 1282 } 1283 else 1284 { 1285 m_targetRAText->setProperty("text", o->ra0().toHMSString()); 1286 m_targetDEText->setProperty("text", o->dec0().toDMSString()); 1287 } 1288 } 1289 } 1290 } 1291 1292 //++++ converters for target coordinate display in Mount Control box 1293 1294 bool Mount::raDecToAzAlt(QString qsRA, QString qsDec) 1295 { 1296 dms RA, Dec; 1297 1298 if (!RA.setFromString(qsRA, false) || !Dec.setFromString(qsDec, true)) 1299 return false; 1300 1301 SkyPoint targetCoord(RA, Dec); 1302 1303 targetCoord.EquatorialToHorizontal(KStarsData::Instance()->lst(), 1304 KStarsData::Instance()->geo()->lat()); 1305 1306 m_targetRAText->setProperty("text", targetCoord.az().toDMSString()); 1307 m_targetDEText->setProperty("text", targetCoord.alt().toDMSString()); 1308 1309 return true; 1310 } 1311 1312 bool Mount::raDecToHaDec(QString qsRA) 1313 { 1314 dms RA; 1315 1316 if (!RA.setFromString(qsRA, false)) 1317 return false; 1318 1319 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1320 1321 dms HA = (lst - RA + dms(360.0)).reduce(); 1322 1323 QChar sgn('+'); 1324 if (HA.Hours() > 12.0) 1325 { 1326 HA.setH(24.0 - HA.Hours()); 1327 sgn = '-'; 1328 } 1329 1330 m_targetRAText->setProperty("text", QString("%1%2").arg(sgn).arg(HA.toHMSString())); 1331 1332 return true; 1333 } 1334 1335 bool Mount::azAltToRaDec(QString qsAz, QString qsAlt) 1336 { 1337 dms Az, Alt; 1338 1339 if (!Az.setFromString(qsAz, true) || !Alt.setFromString(qsAlt, true)) 1340 return false; 1341 1342 SkyPoint targetCoord; 1343 targetCoord.setAz(Az); 1344 targetCoord.setAlt(Alt); 1345 1346 targetCoord.HorizontalToEquatorial(KStars::Instance()->data()->lst(), 1347 KStars::Instance()->data()->geo()->lat()); 1348 1349 m_targetRAText->setProperty("text", targetCoord.ra().toHMSString()); 1350 m_targetDEText->setProperty("text", targetCoord.dec().toDMSString()); 1351 1352 return true; 1353 } 1354 1355 bool Mount::azAltToHaDec(QString qsAz, QString qsAlt) 1356 { 1357 dms Az, Alt; 1358 1359 if (!Az.setFromString(qsAz, true) || !Alt.setFromString(qsAlt, true)) 1360 return false; 1361 1362 SkyPoint targetCoord; 1363 targetCoord.setAz(Az); 1364 targetCoord.setAlt(Alt); 1365 1366 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1367 1368 targetCoord.HorizontalToEquatorial(&lst, KStars::Instance()->data()->geo()->lat()); 1369 1370 dms HA = (lst - targetCoord.ra() + dms(360.0)).reduce(); 1371 1372 QChar sgn('+'); 1373 if (HA.Hours() > 12.0) 1374 { 1375 HA.setH(24.0 - HA.Hours()); 1376 sgn = '-'; 1377 } 1378 1379 m_targetRAText->setProperty("text", QString("%1%2").arg(sgn).arg(HA.toHMSString())); 1380 m_targetDEText->setProperty("text", targetCoord.dec().toDMSString()); 1381 1382 1383 return true; 1384 } 1385 1386 bool Mount::haDecToRaDec(QString qsHA) 1387 { 1388 dms HA; 1389 1390 if (!HA.setFromString(qsHA, false)) 1391 return false; 1392 1393 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1394 dms RA = (lst - HA + dms(360.0)).reduce(); 1395 1396 m_targetRAText->setProperty("text", RA.toHMSString()); 1397 1398 return true; 1399 } 1400 1401 bool Mount::haDecToAzAlt(QString qsHA, QString qsDec) 1402 { 1403 dms HA, Dec; 1404 1405 if (!HA.setFromString(qsHA, false) || !Dec.setFromString(qsDec, true)) 1406 return false; 1407 1408 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); 1409 dms RA = (lst - HA + dms(360.0)).reduce(); 1410 1411 SkyPoint targetCoord; 1412 targetCoord.setRA(RA); 1413 targetCoord.setDec(Dec); 1414 1415 targetCoord.EquatorialToHorizontal(&lst, KStars::Instance()->data()->geo()->lat()); 1416 1417 m_targetRAText->setProperty("text", targetCoord.az().toDMSString()); 1418 m_targetDEText->setProperty("text", targetCoord.alt().toDMSString()); 1419 1420 return true; 1421 } 1422 1423 //---- end: converters for target coordinate display in Mount Control box 1424 1425 void Mount::centerMount() 1426 { 1427 if (m_Mount) 1428 m_Mount->find(); 1429 } 1430 1431 bool Mount::resetModel() 1432 { 1433 if (m_Mount == nullptr) 1434 return false; 1435 1436 if (m_Mount->hasAlignmentModel() == false) 1437 return false; 1438 1439 if (m_Mount->clearAlignmentModel()) 1440 { 1441 appendLogText(i18n("Alignment Model cleared.")); 1442 return true; 1443 } 1444 1445 appendLogText(i18n("Failed to clear Alignment Model.")); 1446 return false; 1447 } 1448 1449 1450 void Mount::setScopeStatus(ISD::Mount::Status status) 1451 { 1452 if (m_Status != status) 1453 { 1454 m_statusText->setProperty("text", m_Mount->statusString(status)); 1455 m_Status = status; 1456 // forward 1457 emit newStatus(status); 1458 } 1459 } 1460 1461 1462 1463 void Mount::setTrackEnabled(bool enabled) 1464 { 1465 if (enabled) 1466 trackOnB->click(); 1467 else 1468 trackOffB->click(); 1469 } 1470 1471 int Mount::slewRate() 1472 { 1473 if (m_Mount == nullptr) 1474 return -1; 1475 1476 return m_Mount->getSlewRate(); 1477 } 1478 1479 //QJsonArray Mount::getScopes() const 1480 //{ 1481 // QJsonArray scopes; 1482 // if (currentTelescope == nullptr) 1483 // return scopes; 1484 1485 // QJsonObject primary = 1486 // { 1487 // {"name", "Primary"}, 1488 // {"mount", currentTelescope->getDeviceName()}, 1489 // {"aperture", primaryScopeApertureIN->value()}, 1490 // {"focalLength", primaryScopeFocalIN->value()}, 1491 // }; 1492 1493 // scopes.append(primary); 1494 1495 // QJsonObject guide = 1496 // { 1497 // {"name", "Guide"}, 1498 // {"mount", currentTelescope->getDeviceName()}, 1499 // {"aperture", primaryScopeApertureIN->value()}, 1500 // {"focalLength", primaryScopeFocalIN->value()}, 1501 // }; 1502 1503 // scopes.append(guide); 1504 1505 // return scopes; 1506 //} 1507 1508 bool Mount::autoParkEnabled() 1509 { 1510 return autoParkTimer.isActive(); 1511 } 1512 1513 void Mount::setAutoParkEnabled(bool enable) 1514 { 1515 if (enable) 1516 startParkTimer(); 1517 else 1518 stopParkTimer(); 1519 } 1520 1521 void Mount::setAutoParkDailyEnabled(bool enabled) 1522 { 1523 parkEveryDay->setChecked(enabled); 1524 } 1525 1526 void Mount::setAutoParkStartup(QTime startup) 1527 { 1528 autoParkTime->setTime(startup); 1529 } 1530 1531 bool Mount::meridianFlipEnabled() 1532 { 1533 return executeMeridianFlip->isChecked(); 1534 } 1535 1536 double Mount::meridianFlipValue() 1537 { 1538 return meridianFlipOffsetDegrees->value(); 1539 } 1540 1541 void Mount::stopTimers() 1542 { 1543 autoParkTimer.stop(); 1544 if (m_Mount) 1545 m_Mount->stopTimers(); 1546 } 1547 1548 void Mount::startParkTimer() 1549 { 1550 if (m_Mount == nullptr || m_ParkStatus == ISD::PARK_UNKNOWN) 1551 return; 1552 1553 if (m_Mount->isParked()) 1554 { 1555 appendLogText(i18n("Mount already parked.")); 1556 return; 1557 } 1558 1559 auto parkTime = autoParkTime->time(); 1560 1561 qCDebug(KSTARS_EKOS_MOUNT) << "Parking time is" << parkTime.toString(); 1562 QDateTime currentDateTime = KStarsData::Instance()->lt(); 1563 QDateTime parkDateTime(currentDateTime); 1564 1565 parkDateTime.setTime(parkTime); 1566 qint64 parkMilliSeconds = parkDateTime.msecsTo(currentDateTime); 1567 qCDebug(KSTARS_EKOS_MOUNT) << "Until parking time:" << parkMilliSeconds << "ms or" << parkMilliSeconds / (60 * 60 * 1000) 1568 << "hours"; 1569 if (parkMilliSeconds > 0) 1570 { 1571 qCDebug(KSTARS_EKOS_MOUNT) << "Added a day to parking time..."; 1572 parkDateTime = parkDateTime.addDays(1); 1573 parkMilliSeconds = parkDateTime.msecsTo(currentDateTime); 1574 1575 int hours = static_cast<int>(parkMilliSeconds / (1000 * 60 * 60)); 1576 if (hours > 0) 1577 { 1578 // No need to display warning for every day check 1579 if (parkEveryDay->isChecked() == false) 1580 appendLogText(i18n("Parking time cannot be in the past.")); 1581 return; 1582 } 1583 } 1584 1585 parkMilliSeconds = std::abs(parkMilliSeconds); 1586 1587 if (parkMilliSeconds > 24 * 60 * 60 * 1000) 1588 { 1589 appendLogText(i18n("Parking time must be within 24 hours of current time.")); 1590 return; 1591 } 1592 1593 if (parkMilliSeconds > 12 * 60 * 60 * 1000) 1594 appendLogText(i18n("Warning! Parking time is more than 12 hours away.")); 1595 1596 appendLogText(i18n("Caution: do not use Auto Park while scheduler is active.")); 1597 1598 autoParkTimer.setInterval(static_cast<int>(parkMilliSeconds)); 1599 autoParkTimer.start(); 1600 1601 startTimerB->setEnabled(false); 1602 stopTimerB->setEnabled(true); 1603 } 1604 1605 void Mount::stopParkTimer() 1606 { 1607 autoParkTimer.stop(); 1608 countdownLabel->setText("00:00:00"); 1609 emit autoParkCountdownUpdated("00:00:00"); 1610 stopTimerB->setEnabled(false); 1611 startTimerB->setEnabled(true); 1612 } 1613 1614 void Mount::startAutoPark() 1615 { 1616 appendLogText(i18n("Parking timer is up.")); 1617 autoParkTimer.stop(); 1618 startTimerB->setEnabled(true); 1619 stopTimerB->setEnabled(false); 1620 countdownLabel->setText("00:00:00"); 1621 emit autoParkCountdownUpdated("00:00:00"); 1622 if (m_Mount) 1623 { 1624 if (m_Mount->isParked() == false) 1625 { 1626 appendLogText(i18n("Starting auto park...")); 1627 park(); 1628 } 1629 } 1630 } 1631 1632 void Mount::syncAxisReversed(INDI_EQ_AXIS axis, bool reversed) 1633 { 1634 if (axis == AXIS_RA) 1635 m_leftRightCheck->setProperty("checked", reversed); 1636 else 1637 m_upDownCheck->setProperty("checked", reversed); 1638 } 1639 1640 void Mount::setupOpticalTrainManager() 1641 { 1642 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, this, &Mount::refreshOpticalTrain); 1643 connect(trainB, &QPushButton::clicked, this, [this]() 1644 { 1645 OpticalTrainManager::Instance()->openEditor(opticalTrainCombo->currentText()); 1646 }); 1647 connect(opticalTrainCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) 1648 { 1649 ProfileSettings::Instance()->setOneSetting(ProfileSettings::MountOpticalTrain, 1650 OpticalTrainManager::Instance()->id(opticalTrainCombo->itemText(index))); 1651 refreshOpticalTrain(); 1652 emit trainChanged(); 1653 }); 1654 } 1655 1656 void Mount::refreshOpticalTrain() 1657 { 1658 opticalTrainCombo->blockSignals(true); 1659 opticalTrainCombo->clear(); 1660 opticalTrainCombo->addItems(OpticalTrainManager::Instance()->getTrainNames()); 1661 trainB->setEnabled(true); 1662 1663 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::MountOpticalTrain); 1664 1665 if (trainID.isValid()) 1666 { 1667 auto id = trainID.toUInt(); 1668 1669 // If train not found, select the first one available. 1670 if (OpticalTrainManager::Instance()->exists(id) == false) 1671 { 1672 qCWarning(KSTARS_EKOS_MOUNT) << "Optical train doesn't exist for id" << id; 1673 id = OpticalTrainManager::Instance()->id(opticalTrainCombo->itemText(0)); 1674 } 1675 1676 auto name = OpticalTrainManager::Instance()->name(id); 1677 1678 opticalTrainCombo->setCurrentText(name); 1679 1680 auto mount = OpticalTrainManager::Instance()->getMount(name); 1681 setMount(mount); 1682 1683 auto scope = OpticalTrainManager::Instance()->getScope(name); 1684 opticalTrainCombo->setToolTip(scope["name"].toString()); 1685 1686 // Load train settings 1687 OpticalTrainSettings::Instance()->setOpticalTrainID(id); 1688 auto settings = OpticalTrainSettings::Instance()->getOneSetting(OpticalTrainSettings::Mount); 1689 if (settings.isValid()) 1690 setAllSettings(settings.toJsonObject().toVariantMap()); 1691 else 1692 m_Settings = m_GlobalSettings; 1693 } 1694 1695 opticalTrainCombo->blockSignals(false); 1696 } 1697 1698 /////////////////////////////////////////////////////////////////////////////////////////// 1699 /// 1700 /////////////////////////////////////////////////////////////////////////////////////////// 1701 QVariantMap Mount::getAllSettings() const 1702 { 1703 QVariantMap settings; 1704 1705 // All Combo Boxes 1706 for (auto &oneWidget : findChildren<QComboBox*>()) 1707 settings.insert(oneWidget->objectName(), oneWidget->currentText()); 1708 1709 // All Double Spin Boxes 1710 for (auto &oneWidget : findChildren<QDoubleSpinBox*>()) 1711 settings.insert(oneWidget->objectName(), oneWidget->value()); 1712 1713 // All Spin Boxes 1714 for (auto &oneWidget : findChildren<QSpinBox*>()) 1715 settings.insert(oneWidget->objectName(), oneWidget->value()); 1716 1717 // All Checkboxes 1718 for (auto &oneWidget : findChildren<QCheckBox*>()) 1719 settings.insert(oneWidget->objectName(), oneWidget->isChecked()); 1720 1721 // All Time 1722 for (auto &oneWidget : findChildren<QTimeEdit*>()) 1723 settings.insert(oneWidget->objectName(), oneWidget->time().toString()); 1724 1725 return settings; 1726 } 1727 1728 /////////////////////////////////////////////////////////////////////////////////////////// 1729 /// 1730 /////////////////////////////////////////////////////////////////////////////////////////// 1731 void Mount::setAllSettings(const QVariantMap &settings) 1732 { 1733 // Disconnect settings that we don't end up calling syncSettings while 1734 // performing the changes. 1735 disconnectSyncSettings(); 1736 1737 for (auto &name : settings.keys()) 1738 { 1739 // Combo 1740 auto comboBox = findChild<QComboBox*>(name); 1741 if (comboBox) 1742 { 1743 syncControl(settings, name, comboBox); 1744 continue; 1745 } 1746 1747 // Double spinbox 1748 auto doubleSpinBox = findChild<QDoubleSpinBox*>(name); 1749 if (doubleSpinBox) 1750 { 1751 syncControl(settings, name, doubleSpinBox); 1752 continue; 1753 } 1754 1755 // spinbox 1756 auto spinBox = findChild<QSpinBox*>(name); 1757 if (spinBox) 1758 { 1759 syncControl(settings, name, spinBox); 1760 continue; 1761 } 1762 1763 // checkbox 1764 auto checkbox = findChild<QCheckBox*>(name); 1765 if (checkbox) 1766 { 1767 syncControl(settings, name, checkbox); 1768 continue; 1769 } 1770 1771 // timeEdit 1772 auto timeEdit = findChild<QTimeEdit*>(name); 1773 if (timeEdit) 1774 { 1775 syncControl(settings, name, timeEdit); 1776 continue; 1777 } 1778 } 1779 1780 // Sync to options 1781 for (auto &key : settings.keys()) 1782 { 1783 auto value = settings[key]; 1784 // Save immediately 1785 Options::self()->setProperty(key.toLatin1(), value); 1786 Options::self()->save(); 1787 1788 m_Settings[key] = value; 1789 m_GlobalSettings[key] = value; 1790 } 1791 1792 emit settingsUpdated(getAllSettings()); 1793 1794 // Save to optical train specific settings as well 1795 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->id(opticalTrainCombo->currentText())); 1796 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Mount, m_Settings); 1797 1798 // Restablish connections 1799 connectSyncSettings(); 1800 } 1801 1802 /////////////////////////////////////////////////////////////////////////////////////////// 1803 /// 1804 /////////////////////////////////////////////////////////////////////////////////////////// 1805 bool Mount::syncControl(const QVariantMap &settings, const QString &key, QWidget * widget) 1806 { 1807 QSpinBox *pSB = nullptr; 1808 QDoubleSpinBox *pDSB = nullptr; 1809 QCheckBox *pCB = nullptr; 1810 QComboBox *pComboBox = nullptr; 1811 QTimeEdit *pTimeEdit = nullptr; 1812 bool ok = false; 1813 1814 if ((pSB = qobject_cast<QSpinBox *>(widget))) 1815 { 1816 const int value = settings[key].toInt(&ok); 1817 if (ok) 1818 { 1819 pSB->setValue(value); 1820 return true; 1821 } 1822 } 1823 else if ((pDSB = qobject_cast<QDoubleSpinBox *>(widget))) 1824 { 1825 const double value = settings[key].toDouble(&ok); 1826 if (ok) 1827 { 1828 pDSB->setValue(value); 1829 return true; 1830 } 1831 } 1832 else if ((pCB = qobject_cast<QCheckBox *>(widget))) 1833 { 1834 const bool value = settings[key].toBool(); 1835 pCB->setChecked(value); 1836 return true; 1837 } 1838 // ONLY FOR STRINGS, not INDEX 1839 else if ((pComboBox = qobject_cast<QComboBox *>(widget))) 1840 { 1841 const QString value = settings[key].toString(); 1842 pComboBox->setCurrentText(value); 1843 return true; 1844 } 1845 else if ((pTimeEdit = qobject_cast<QTimeEdit *>(widget))) 1846 { 1847 const QString value = settings[key].toString(); 1848 pTimeEdit->setTime(QTime::fromString(value)); 1849 return true; 1850 } 1851 1852 return false; 1853 }; 1854 1855 /////////////////////////////////////////////////////////////////////////////////////////// 1856 /// 1857 /////////////////////////////////////////////////////////////////////////////////////////// 1858 void Mount::syncSettings() 1859 { 1860 QDoubleSpinBox *dsb = nullptr; 1861 QSpinBox *sb = nullptr; 1862 QCheckBox *cb = nullptr; 1863 QComboBox *cbox = nullptr; 1864 QTimeEdit *timeEdit = nullptr; 1865 1866 QString key; 1867 QVariant value; 1868 1869 if ( (dsb = qobject_cast<QDoubleSpinBox*>(sender()))) 1870 { 1871 key = dsb->objectName(); 1872 value = dsb->value(); 1873 1874 } 1875 else if ( (sb = qobject_cast<QSpinBox*>(sender()))) 1876 { 1877 key = sb->objectName(); 1878 value = sb->value(); 1879 } 1880 else if ( (cb = qobject_cast<QCheckBox*>(sender()))) 1881 { 1882 key = cb->objectName(); 1883 value = cb->isChecked(); 1884 } 1885 else if ( (cbox = qobject_cast<QComboBox*>(sender()))) 1886 { 1887 key = cbox->objectName(); 1888 value = cbox->currentText(); 1889 } 1890 else if ( (timeEdit = qobject_cast<QTimeEdit*>(sender()))) 1891 { 1892 key = timeEdit->objectName(); 1893 value = timeEdit->time().toString(); 1894 } 1895 1896 // Save immediately 1897 Options::self()->setProperty(key.toLatin1(), value); 1898 Options::self()->save(); 1899 1900 m_Settings[key] = value; 1901 m_GlobalSettings[key] = value; 1902 1903 emit settingsUpdated(getAllSettings()); 1904 1905 // Save to optical train specific settings as well 1906 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->id(opticalTrainCombo->currentText())); 1907 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Mount, m_Settings); 1908 } 1909 1910 /////////////////////////////////////////////////////////////////////////////////////////// 1911 /// 1912 /////////////////////////////////////////////////////////////////////////////////////////// 1913 void Mount::loadGlobalSettings() 1914 { 1915 QString key; 1916 QVariant value; 1917 1918 QVariantMap settings; 1919 // All Combo Boxes 1920 for (auto &oneWidget : findChildren<QComboBox*>()) 1921 { 1922 if (oneWidget->objectName() == "opticalTrainCombo") 1923 continue; 1924 1925 key = oneWidget->objectName(); 1926 value = Options::self()->property(key.toLatin1()); 1927 if (value.isValid()) 1928 { 1929 oneWidget->setCurrentText(value.toString()); 1930 settings[key] = value; 1931 } 1932 } 1933 1934 // All Double Spin Boxes 1935 for (auto &oneWidget : findChildren<QDoubleSpinBox*>()) 1936 { 1937 key = oneWidget->objectName(); 1938 value = Options::self()->property(key.toLatin1()); 1939 if (value.isValid()) 1940 { 1941 oneWidget->setValue(value.toDouble()); 1942 settings[key] = value; 1943 } 1944 } 1945 1946 // All Spin Boxes 1947 for (auto &oneWidget : findChildren<QSpinBox*>()) 1948 { 1949 key = oneWidget->objectName(); 1950 value = Options::self()->property(key.toLatin1()); 1951 if (value.isValid()) 1952 { 1953 oneWidget->setValue(value.toInt()); 1954 settings[key] = value; 1955 } 1956 } 1957 1958 // All Checkboxes 1959 for (auto &oneWidget : findChildren<QCheckBox*>()) 1960 { 1961 key = oneWidget->objectName(); 1962 value = Options::self()->property(key.toLatin1()); 1963 if (value.isValid()) 1964 { 1965 oneWidget->setChecked(value.toBool()); 1966 settings[key] = value; 1967 } 1968 } 1969 1970 // initialize meridian flip state machine values 1971 mf_state->setEnabled(Options::executeMeridianFlip()); 1972 mf_state->setOffset(Options::meridianFlipOffsetDegrees()); 1973 1974 m_GlobalSettings = m_Settings = settings; 1975 } 1976 1977 /////////////////////////////////////////////////////////////////////////////////////////// 1978 /// 1979 /////////////////////////////////////////////////////////////////////////////////////////// 1980 void Mount::connectSyncSettings() 1981 { 1982 // All Combo Boxes 1983 for (auto &oneWidget : findChildren<QComboBox*>()) 1984 connect(oneWidget, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings); 1985 1986 // All Double Spin Boxes 1987 for (auto &oneWidget : findChildren<QDoubleSpinBox*>()) 1988 connect(oneWidget, &QDoubleSpinBox::editingFinished, this, &Ekos::Mount::syncSettings); 1989 1990 // All Spin Boxes 1991 for (auto &oneWidget : findChildren<QSpinBox*>()) 1992 connect(oneWidget, &QSpinBox::editingFinished, this, &Ekos::Mount::syncSettings); 1993 1994 // All Checkboxes 1995 for (auto &oneWidget : findChildren<QCheckBox*>()) 1996 connect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings); 1997 1998 // All QDateTimeEdit 1999 for (auto &oneWidget : findChildren<QDateTimeEdit*>()) 2000 connect(oneWidget, &QDateTimeEdit::editingFinished, this, &Ekos::Mount::syncSettings); 2001 } 2002 2003 /////////////////////////////////////////////////////////////////////////////////////////// 2004 /// 2005 /////////////////////////////////////////////////////////////////////////////////////////// 2006 void Mount::disconnectSyncSettings() 2007 { 2008 // All Combo Boxes 2009 for (auto &oneWidget : findChildren<QComboBox*>()) 2010 disconnect(oneWidget, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings); 2011 2012 // All Double Spin Boxes 2013 for (auto &oneWidget : findChildren<QDoubleSpinBox*>()) 2014 disconnect(oneWidget, &QDoubleSpinBox::editingFinished, this, &Ekos::Mount::syncSettings); 2015 2016 // All Spin Boxes 2017 for (auto &oneWidget : findChildren<QSpinBox*>()) 2018 disconnect(oneWidget, &QSpinBox::editingFinished, this, &Ekos::Mount::syncSettings); 2019 2020 // All Checkboxes 2021 for (auto &oneWidget : findChildren<QCheckBox*>()) 2022 disconnect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings); 2023 2024 for (auto &oneWidget : findChildren<QDateTimeEdit*>()) 2025 disconnect(oneWidget, &QDateTimeEdit::editingFinished, this, &Ekos::Mount::syncSettings); 2026 } 2027 2028 /////////////////////////////////////////////////////////////////////////////////////////// 2029 /// 2030 /////////////////////////////////////////////////////////////////////////////////////////// 2031 void Mount::connectSettings() 2032 { 2033 connectSyncSettings(); 2034 2035 // connections to the meridian flip state machine 2036 connect(executeMeridianFlip, &QCheckBox::toggled, mf_state.get(), &MeridianFlipState::setEnabled); 2037 connect(meridianFlipOffsetDegrees, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), 2038 mf_state.get(), &MeridianFlipState::setOffset); 2039 connect(this, &Mount::newParkStatus, mf_state.get(), &MeridianFlipState::setMountParkStatus); 2040 connect(mf_state.get(), &MeridianFlipState::slewTelescope, [&](SkyPoint pos) 2041 { 2042 if (m_Mount) 2043 m_Mount->Slew(&pos, (m_Mount->canFlip() && Options::forcedFlip())); 2044 }); 2045 2046 // Train combo box should NOT be synced. 2047 disconnect(opticalTrainCombo, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings); 2048 } 2049 2050 /////////////////////////////////////////////////////////////////////////////////////////// 2051 /// 2052 /////////////////////////////////////////////////////////////////////////////////////////// 2053 void Mount::disconnectSettings() 2054 { 2055 disconnectSyncSettings(); 2056 2057 // cut connections to the meridian flip state machine 2058 disconnect(executeMeridianFlip, &QCheckBox::toggled, mf_state.get(), &MeridianFlipState::setEnabled); 2059 disconnect(meridianFlipOffsetDegrees, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), 2060 mf_state.get(), &MeridianFlipState::setOffset); 2061 disconnect(this, &Mount::newParkStatus, mf_state.get(), &MeridianFlipState::setMountParkStatus); 2062 disconnect(mf_state.get(), &MeridianFlipState::slewTelescope, nullptr, nullptr); 2063 } 2064 2065 double Mount::initialHA() 2066 { 2067 return mf_state->initialPositionHA(); 2068 } 2069 }