diff --git a/kstars/ekos/capture/capture.h b/kstars/ekos/capture/capture.h --- a/kstars/ekos/capture/capture.h +++ b/kstars/ekos/capture/capture.h @@ -13,6 +13,7 @@ #include "customproperties.h" #include "oal/filter.h" #include "ekos/ekos.h" +#include "ekos/mount/mount.h" #include "indi/indiccd.h" #include "indi/indicap.h" #include "indi/indidome.h" @@ -90,7 +91,7 @@ Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) public: - typedef enum { MF_NONE, MF_INITIATED, MF_FLIPPING, MF_SLEWING, MF_ALIGNING, MF_GUIDING } MFStage; + typedef enum { MF_NONE, MF_REQUESTED, MF_READY, MF_INITIATED, MF_FLIPPING, MF_SLEWING, MF_COMPLETED, MF_ALIGNING, MF_GUIDING } MFStage; typedef enum { CAL_NONE, CAL_DUSTCAP_PARKING, @@ -311,8 +312,6 @@ */ void setSettings(const QJsonObject &settings); - SkyPoint getInitialMountCoords() const; - public slots: /** \addtogroup CaptureDBusInterface @@ -512,6 +511,9 @@ // Clear Camera Configuration void clearCameraConfiguration(); + // Meridian flip + void meridianFlipStatusChanged(Mount::MeridianFlipStatus status); + private slots: /** @@ -540,10 +542,6 @@ void resetJobEdit(); void executeJob(); - // Meridian Flip - void checkMeridianFlipTimeout(); - //void checkAlignmentSlewComplete(); - // AutoGuide void checkGuideDeviationTimeout(); @@ -606,6 +604,7 @@ void newExposureProgress(Ekos::SequenceJob *job); void sequenceChanged(const QJsonArray &sequence); void settingsUpdated(const QJsonObject &settings); + void newMeridianFlipStatus(Mount::MeridianFlipStatus status); private: void setBusy(bool enable); @@ -633,17 +632,12 @@ /* Meridian Flip */ bool checkMeridianFlip(); void checkGuidingAfterFlip(); - double getCurrentHA(); - double getInitialHA(); // Remaining Time in seconds int getJobRemainingTime(SequenceJob *job); void resetFrameToZero(); - /* Slewing - true iff start slewing was successful */ - bool slew(const SkyPoint target); - /* Refocus */ void startRefocusTimer(bool forced = false); @@ -798,6 +792,7 @@ SchedulerJob::CapturedFramesMap capturedFramesMap; // Execute the meridian flip - bool executeMeridianFlip(); + void setMeridianFlipStage(MFStage status); + void processFlipCompleted(); }; } diff --git a/kstars/ekos/capture/capture.cpp b/kstars/ekos/capture/capture.cpp --- a/kstars/ekos/capture/capture.cpp +++ b/kstars/ekos/capture/capture.cpp @@ -352,8 +352,8 @@ void Capture::pause() { pauseFunction = nullptr; - m_State = CAPTURE_PAUSED; - emit newStatus(Ekos::CAPTURE_PAUSED); + m_State = CAPTURE_PAUSE_PLANNED; + emit newStatus(Ekos::CAPTURE_PAUSE_PLANNED); appendLogText(i18n("Sequence shall be paused after current exposure is complete.")); pauseB->setEnabled(false); @@ -363,7 +363,7 @@ void Capture::toggleSequence() { - if (m_State == CAPTURE_PAUSED) + if (m_State == CAPTURE_PAUSE_PLANNED || m_State == CAPTURE_PAUSED) { startB->setIcon( QIcon::fromTheme("media-playback-stop")); @@ -391,11 +391,13 @@ void Capture::registerNewModule(const QString &name) { + qCDebug(KSTARS_EKOS_CAPTURE) << "Registering new Module (" << name << ")"; if (name == "Mount") { delete mountInterface; mountInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Mount", "org.kde.kstars.Ekos.Mount", QDBusConnection::sessionBus(), this); + } } @@ -412,8 +414,8 @@ Options::setEnforceAutofocus(autofocusCheck->isChecked()); Options::setEnforceRefocusEveryN(refocusEveryNCheck->isChecked()); - Options::setAutoMeridianFlip(meridianCheck->isChecked()); - Options::setAutoMeridianHours(meridianHours->value()); + setMeridianFlip(meridianCheck->isChecked()); + setMeridianFlipHour(meridianHours->value()); // Reset progress option if there is no captured frame map set at the time of start - fixes the end-user setting the option just before starting ignoreJobProgress = !capturedFramesMap.count() && Options::alwaysResetSequenceWhenStarting(); @@ -476,7 +478,6 @@ // Refocus timer should not be reset on deviation error if (m_DeviationDetected == false && m_State != CAPTURE_SUSPENDED) { - meridianFlipStage = MF_NONE; // start timer to measure time until next forced refocus startRefocusEveryNTimer(); } @@ -593,7 +594,8 @@ lightBox->SetLightEnabled(false); } - secondsLabel->clear(); + if (meridianFlipStage == MF_NONE) + secondsLabel->clear(); disconnect(currentCCD, &ISD::CCD::BLOBUpdated, this, &Ekos::Capture::newFITS); disconnect(currentCCD, &ISD::CCD::newExposureValue, this, &Ekos::Capture::setExposureProgress); disconnect(currentCCD, &ISD::CCD::ready, this, &Ekos::Capture::ready); @@ -627,6 +629,8 @@ seqTimer->stop(); activeJob = nullptr; + // meridian flip may take place if requested + setMeridianFlipStage(MF_READY); } void Capture::sendNewImage(const QString &filename, ISD::CCDChip * myChip) @@ -1236,11 +1240,13 @@ bool Capture::startNextExposure() { - if (m_State == CAPTURE_PAUSED) + if (m_State == CAPTURE_PAUSE_PLANNED) { pauseFunction = &Capture::startNextExposure; appendLogText(i18n("Sequence paused.")); secondsLabel->setText(i18n("Paused...")); + m_State = CAPTURE_PAUSED; + setMeridianFlipStage(MF_READY); return false; } @@ -1360,11 +1366,13 @@ return true; } - if (m_State == CAPTURE_PAUSED) + if (m_State == CAPTURE_PAUSE_PLANNED) { pauseFunction = &Capture::setCaptureComplete; appendLogText(i18n("Sequence paused.")); secondsLabel->setText(i18n("Paused...")); + m_State = CAPTURE_PAUSED; + setMeridianFlipStage(MF_READY); return false; } @@ -2838,7 +2846,8 @@ return; else if (rc == IPS_BUSY) { - secondsLabel->clear(); + if (meridianFlipStage == MF_NONE) + secondsLabel->clear(); QTimer::singleShot(1000, this, &Ekos::Capture::updatePreCaptureCalibrationStatus); return; } @@ -2879,7 +2888,7 @@ appendLogText(i18n("Post meridian flip calibration completed successfully.")); resumeSequence(); // N.B. Set meridian flip stage AFTER resumeSequence() always - meridianFlipStage = MF_NONE; + setMeridianFlipStage(MF_NONE); return; } } @@ -3099,25 +3108,105 @@ HFRPixels->setValue(median + (median * (Options::hFRThresholdPercentage() / 100.0))); } -SkyPoint Capture::getInitialMountCoords() const +void Capture::setMeridianFlipStage(MFStage status) { - QVariant const result = mountInterface->property("currentTarget"); - SkyPoint point = result.value(); - return point; + if (meridianFlipStage != status) + { + switch (status) + { + case MF_NONE: + if (m_State == CAPTURE_PAUSED) + // paused after meridian flip + secondsLabel->setText(i18n("Paused...")); + else + secondsLabel->setText(""); + meridianFlipStage = status; + break; + + case MF_READY: + if (meridianFlipStage == MF_REQUESTED) + { + // we keep the stage on requested until the mount starts the meridian flip + secondsLabel->setText(i18n("Meridian Flip...")); + emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); + } + else if (meridianFlipStage == MF_INITIATED) + { + secondsLabel->setText(i18n("Meridian Flip...")); + } + else if (m_State == CAPTURE_PAUSED) + { + // paused after meridian flip requested + secondsLabel->setText(i18n("Paused...")); + meridianFlipStage = status; + emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); + } + // in any other case, ignore it + break; + + case MF_INITIATED: + meridianFlipStage = MF_INITIATED; + emit meridianFlipStarted(); + secondsLabel->setText(i18n("Meridian Flip...")); + KSNotification::event(QLatin1String("MeridianFlipStarted"), i18n("Meridian flip started"), KSNotification::EVENT_INFO); + break; + + case MF_REQUESTED: + if (m_State == CAPTURE_PAUSED) + // paused before meridian flip requested + emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); + else + emit newMeridianFlipStatus(Mount::FLIP_WAITING); + meridianFlipStage = status; + break; + + case MF_COMPLETED: + secondsLabel->setText(i18n("Flip complete.")); + break; + + default: + meridianFlipStage = status; + break; + } + } } -bool Capture::executeMeridianFlip() -{ - QDBusReply const reply = mountInterface->call("executeMeridianFlip"); +void Capture::meridianFlipStatusChanged(Mount::MeridianFlipStatus status) { + switch (status) + { + case Mount::FLIP_NONE: + // MF_NONE as external signal ignored so that re-alignment and guiding are processed first + if (meridianFlipStage != MF_COMPLETED) + setMeridianFlipStage(MF_NONE); + break; - if (reply.error().type() == QDBusError::NoError) - return reply.value(); + case Mount::FLIP_PLANNED: + // If we are autoguiding, we should resume autoguiding after flip + resumeGuidingAfterFlip = (guideState == GUIDE_GUIDING); - // error occured - qCCritical(KSTARS_EKOS_CAPTURE) << QString("Warning: execute meridian flip request received DBUS error: %1").arg(QDBusError::errorString(reply.error().type())); + if (m_State == CAPTURE_IDLE || m_State == CAPTURE_ABORTED || m_State == CAPTURE_COMPLETE || m_State == CAPTURE_PAUSED) + { + setMeridianFlipStage(MF_INITIATED); + emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); + } + else + setMeridianFlipStage(MF_REQUESTED); + break; - return false; + case Mount::FLIP_RUNNING: + setMeridianFlipStage(MF_INITIATED); + emit newStatus(Ekos::CAPTURE_MERIDIAN_FLIP); + break; + + case Mount::FLIP_COMPLETED: + setMeridianFlipStage(MF_COMPLETED); + processFlipCompleted(); + break; + + default: + break; + } } int Capture::getTotalFramesCount(QString signature) @@ -4152,62 +4241,56 @@ case MF_INITIATED: { if (nvp->s == IPS_BUSY) - meridianFlipStage = MF_FLIPPING; + setMeridianFlipStage(MF_FLIPPING); } break; case MF_FLIPPING: { - double ra, dec; - currentTelescope->getEqCoords(&ra, &dec); - double diffRA = getInitialMountCoords().ra().Hours() - ra; - // If the mount is actually flipping then we should see a difference in RA - // which if it exceeded MF_RA_DIFF_LIMIT (4 hours) then we consider it to be - // undertaking the flip. Otherwise, it's not flipping and let timeout takes care of - // of that - // Are there any mounts that do NOT change RA while flipping? i.e. do it silently? - // Need to investigate that bit - if (fabs(diffRA) > MF_RA_DIFF_LIMIT /* || nvp->s == IPS_OK*/) - meridianFlipStage = MF_SLEWING; + if (currentTelescope != nullptr && currentTelescope->isSlewing()) + setMeridianFlipStage(MF_SLEWING); } break; - case MF_SLEWING: - - if (nvp->s != IPS_OK) - break; + default: + break; + } +} - // If dome is syncing, wait until it stops - if (dome && dome->isMoving()) - break; +void Capture::processFlipCompleted() +{ + // If dome is syncing, wait until it stops + if (dome && dome->isMoving()) + return; - appendLogText(i18n("Telescope completed the meridian flip.")); + appendLogText(i18n("Telescope completed the meridian flip.")); - //KNotification::event(QLatin1String("MeridianFlipCompleted"), i18n("Meridian flip is successfully completed")); - KSNotification::event(QLatin1String("MeridianFlipCompleted"), i18n("Meridian flip is successfully completed"), KSNotification::EVENT_INFO); + //KNotification::event(QLatin1String("MeridianFlipCompleted"), i18n("Meridian flip is successfully completed")); + KSNotification::event(QLatin1String("MeridianFlipCompleted"), i18n("Meridian flip is successfully completed"), KSNotification::EVENT_INFO); - if (resumeAlignmentAfterFlip == true) - { - appendLogText(i18n("Performing post flip re-alignment...")); - secondsLabel->setText(i18n("Aligning...")); - retries = 0; - m_State = CAPTURE_ALIGNING; - emit newStatus(Ekos::CAPTURE_ALIGNING); + // resume only if capturing was running + if (m_State == CAPTURE_IDLE || m_State == CAPTURE_ABORTED || m_State == CAPTURE_COMPLETE) + return; - meridianFlipStage = MF_ALIGNING; - //QTimer::singleShot(Options::settlingTime(), [this]() {emit meridialFlipTracked();}); - //emit meridialFlipTracked(); - return; - } + if (resumeAlignmentAfterFlip == true) + { + appendLogText(i18n("Performing post flip re-alignment...")); + secondsLabel->setText(i18n("Aligning...")); - retries = 0; - checkGuidingAfterFlip(); - break; + retries = 0; + m_State = CAPTURE_ALIGNING; + emit newStatus(Ekos::CAPTURE_ALIGNING); - default: - break; + setMeridianFlipStage(MF_ALIGNING); + //QTimer::singleShot(Options::settlingTime(), [this]() {emit meridialFlipTracked();}); + //emit meridialFlipTracked(); + return; } + + retries = 0; + checkGuidingAfterFlip(); + } void Capture::checkGuidingAfterFlip() @@ -4217,7 +4300,7 @@ { resumeSequence(); // N.B. Set meridian flip stage AFTER resumeSequence() always - meridianFlipStage = MF_NONE; + setMeridianFlipStage(MF_NONE); } else { @@ -4227,119 +4310,37 @@ m_State = CAPTURE_CALIBRATING; emit newStatus(Ekos::CAPTURE_CALIBRATING); - meridianFlipStage = MF_GUIDING; + setMeridianFlipStage(MF_GUIDING); emit meridianFlipCompleted(); } } -double Capture::getCurrentHA() -{ - QVariant HA = mountInterface->property("hourAngle"); - return HA.toDouble(); -} - -double Capture::getInitialHA() -{ - QVariant HA = mountInterface->property("initialHA"); - return HA.toDouble(); -} - -bool Capture::slew(const SkyPoint target) -{ - QList telescopeSlew; - telescopeSlew.append(target.ra().Hours()); - telescopeSlew.append(target.dec().Degrees()); - - QDBusReply const slewModeReply = mountInterface->callWithArgumentList(QDBus::AutoDetect, "slew", telescopeSlew); - - if (slewModeReply.error().type() == QDBusError::NoError) - return true; - - // error occured - qCCritical(KSTARS_EKOS_CAPTURE) << QString("Warning: slew request received DBUS error: %1").arg(QDBusError::errorString(slewModeReply.error().type())); - return false; -} - bool Capture::checkMeridianFlip() { - if (currentTelescope == nullptr || meridianCheck->isChecked() == false || getInitialHA() > 0) + if (currentTelescope == nullptr || meridianCheck->isChecked() == false) return false; // If active job is taking flat field image at a wall source // then do not flip. if (activeJob && activeJob->getFrameType() == FRAME_FLAT && activeJob->getFlatFieldSource() == SOURCE_WALL) return false; - double currentHA = getCurrentHA(); - - //appendLogText(i18n("Current hour angle %1", currentHA)); - - if (currentHA == Ekos::INVALID_VALUE) + if (meridianFlipStage == MF_NONE) return false; - if (currentHA > meridianHours->value()) - { - //NOTE: DO NOT make the follow sentence PLURAL as the value is in double - appendLogText( - i18n("Current hour angle %1 hours exceeds meridian flip limit of %2 hours. Auto meridian flip is initiated.", - QString("%L1").arg(currentHA, 0, 'f', 2), QString("%L1").arg(meridianHours->value(), 0, 'f', 2))); - meridianFlipStage = MF_INITIATED; + // meridian flip requested or already in action - //KNotification::event(QLatin1String("MeridianFlipStarted"), i18n("Meridian flip started")); - KSNotification::event(QLatin1String("MeridianFlipStarted"), i18n("Meridian flip started"), KSNotification::EVENT_INFO); + // Reset frame if we need to do focusing later on + if (isInSequenceFocus || (refocusEveryNCheck->isChecked() && getRefocusEveryNTimerElapsedSec() > 0)) + emit resetFocus(); - // Suspend guiding first before commanding a meridian flip - //if (isAutoGuiding && currentCCD->getChip(ISD::CCDChip::GUIDE_CCD) == guideChip) - // emit suspendGuiding(false); + // signal that meridian flip may take place + if (meridianFlipStage == MF_REQUESTED) + setMeridianFlipStage(MF_READY); - // If we are autoguiding, we should resume autoguiding after flip - resumeGuidingAfterFlip = (guideState == GUIDE_GUIDING); - - emit meridianFlipStarted(); - // Reset frame if we need to do focusing later on - if (isInSequenceFocus || (refocusEveryNCheck->isChecked() && getRefocusEveryNTimerElapsedSec() > 0)) - emit resetFocus(); - - if (executeMeridianFlip()) - { - secondsLabel->setText(i18n("Meridian Flip...")); - - retries = 0; - - m_State = CAPTURE_MERIDIAN_FLIP; - emit newStatus(Ekos::CAPTURE_MERIDIAN_FLIP); - - QTimer::singleShot(MF_TIMER_TIMEOUT, this, &Ekos::Capture::checkMeridianFlipTimeout); - return true; - } - } - - return false; -} - -void Capture::checkMeridianFlipTimeout() -{ - if (meridianFlipStage == MF_NONE) - return; - - if (meridianFlipStage < MF_ALIGNING) - { - appendLogText(i18n("Telescope meridian flip timed out. Please make sure your mount supports meridian flip.")); - - if (++retries == 3) - { - //KNotification::event(QLatin1String("MeridianFlipFailed"), i18n("Meridian flip failed")); - KSNotification::event(QLatin1String("MeridianFlipFailed"), i18n("Meridian flip failed"), KSNotification::EVENT_ALERT); - abort(); - } - else - { - if (executeMeridianFlip()) - appendLogText(i18n("Retrying meridian flip again...")); - } - } + return true; } void Capture::checkGuideDeviationTimeout() @@ -4392,7 +4393,7 @@ this->m_State = CAPTURE_ALIGNING; emit newStatus(Ekos::CAPTURE_ALIGNING); - meridianFlipStage = MF_ALIGNING; + setMeridianFlipStage(MF_ALIGNING); } } break; @@ -4630,12 +4631,21 @@ void Capture::setMeridianFlip(bool enable) { - meridianCheck->setChecked(enable); + Options::setAutoMeridianFlip(enable); + QDBusReply const reply = mountInterface->call("activateMeridianFlip", enable); + + if (reply.error().type() != QDBusError::NoError) + qCCritical(KSTARS_EKOS_CAPTURE) << QString("Warning: setting meridian flip request received DBUS error: %1").arg(QDBusError::errorString(reply.error().type())); } void Capture::setMeridianFlipHour(double hours) { - meridianHours->setValue(hours); + Options::setAutoMeridianHours(hours); + + QDBusReply const reply = mountInterface->call("setMeridianFlipLimit", hours); + + if (reply.error().type() != QDBusError::NoError) + qCCritical(KSTARS_EKOS_CAPTURE) << QString("Warning: setting meridian flip time limit request received DBUS error: %1").arg(QDBusError::errorString(reply.error().type())); } bool Capture::hasCoolerControl() diff --git a/kstars/ekos/ekos.h b/kstars/ekos/ekos.h --- a/kstars/ekos/ekos.h +++ b/kstars/ekos/ekos.h @@ -69,7 +69,7 @@ // Capture States static const QStringList captureStates = { - I18N_NOOP("Idle"), I18N_NOOP("In Progress"), I18N_NOOP("Capturing"), I18N_NOOP("Paused"), + I18N_NOOP("Idle"), I18N_NOOP("In Progress"), I18N_NOOP("Capturing"), I18N_NOOP("Pause Planned"), I18N_NOOP("Paused"), I18N_NOOP("Suspended"), I18N_NOOP("Aborted"), I18N_NOOP("Waiting"), I18N_NOOP("Image Received"), I18N_NOOP("Dithering"),I18N_NOOP("Focusing"), I18N_NOOP("Filter Focus"), I18N_NOOP("Changing Filter"), I18N_NOOP("Setting Temperature"), I18N_NOOP("Setting Rotator"), I18N_NOOP("Aligning"), I18N_NOOP("Calibrating"), @@ -80,6 +80,7 @@ CAPTURE_IDLE, CAPTURE_PROGRESS, CAPTURE_CAPTURING, + CAPTURE_PAUSE_PLANNED, CAPTURE_PAUSED, CAPTURE_SUSPENDED, CAPTURE_ABORTED, diff --git a/kstars/ekos/manager.cpp b/kstars/ekos/manager.cpp --- a/kstars/ekos/manager.cpp +++ b/kstars/ekos/manager.cpp @@ -2968,6 +2968,8 @@ // Meridian Flip states connect(captureProcess.get(), &Ekos::Capture::meridianFlipStarted, mountProcess.get(), &Ekos::Mount::disableAltLimits, Qt::UniqueConnection); connect(captureProcess.get(), &Ekos::Capture::meridianFlipCompleted, mountProcess.get(), &Ekos::Mount::enableAltLimits, Qt::UniqueConnection); + connect(captureProcess.get(), &Ekos::Capture::newMeridianFlipStatus, mountProcess.get(), &Ekos::Mount::meridianFlipStatusChanged, Qt::UniqueConnection); + connect(mountProcess.get(), &Ekos::Mount::newMeridianFlipStatus, captureProcess.get(), &Ekos::Capture::meridianFlipStatusChanged, Qt::UniqueConnection); // Mount Status connect(mountProcess.get(), &Ekos::Mount::newStatus, captureProcess.get(), &Ekos::Capture::setMountStatus, Qt::UniqueConnection); diff --git a/kstars/ekos/mount/mount.h b/kstars/ekos/mount/mount.h --- a/kstars/ekos/mount/mount.h +++ b/kstars/ekos/mount/mount.h @@ -42,9 +42,7 @@ Q_PROPERTY(QList equatorialCoords READ equatorialCoords) Q_PROPERTY(QList horizontalCoords READ horizontalCoords) Q_PROPERTY(QList telescopeInfo READ telescopeInfo WRITE setTelescopeInfo) - Q_PROPERTY(SkyPoint currentTarget READ currentTarget) Q_PROPERTY(double hourAngle READ hourAngle) - Q_PROPERTY(double initialHA READ initialHA) Q_PROPERTY(int slewRate READ slewRate WRITE setSlewRate) Q_PROPERTY(int slewStatus READ slewStatus) Q_PROPERTY(bool canPark READ canPark) @@ -56,6 +54,7 @@ //typedef enum { PARKING_IDLE, PARKING_OK, UNPARKING_OK, PARKING_BUSY, UNPARKING_BUSY, PARKING_ERROR } ParkingStatus; + typedef enum { FLIP_NONE, FLIP_PLANNED, FLIP_WAITING, FLIP_ACCEPTED, FLIP_RUNNING, FLIP_COMPLETED, FLIP_ERROR } MeridianFlipStatus; /** * @brief setTelescope Sets the mount module telescope interface @@ -249,11 +248,30 @@ QJsonArray getScopes() const; /* + * @brief Check if a meridian flip if necessary. + * @param lst current local sideral time + * @return true if a meridian flip is necessary + */ + bool checkMeridianFlip(dms lst); + + /* * @brief Execute a meridian flip if necessary. * @return true if a meridian flip was necessary */ Q_INVOKABLE bool executeMeridianFlip(); + /* + * @brief activate meridian flip function + * @param activate true iff the meridian flip function should be enabled + */ + Q_INVOKABLE Q_NOREPLY void activateMeridianFlip(bool activate); + + /* + * @brief set the time beyond the meridian when the meridian flip + * @param hours time limit + */ + Q_INVOKABLE Q_NOREPLY void setMeridianFlipLimit(double hours); + public slots: /** @@ -329,6 +347,8 @@ void toggleMountToolBox(); + void meridianFlipStatusChanged(MeridianFlipStatus status); + private slots: /** @@ -351,16 +371,19 @@ void pierSideChanged(ISD::Telescope::PierSide side); void slewRateChanged(int index); void ready(); + void newMeridianFlipStatus(MeridianFlipStatus status); - private: +private: void syncGPS(); + MeridianFlipStatus m_MFStatus = FLIP_NONE; + void setMeridianFlipStatus(MeridianFlipStatus status); QPointer captureInterface { nullptr }; ISD::Telescope *currentTelescope = nullptr; ISD::GDInterface *currentGPS = nullptr; QStringList m_LogText; - SkyPoint currentTargetPosition; + SkyPoint *currentTargetPosition = nullptr; SkyPoint telescopeCoord; QString lastNotificationMessage; QTimer updateTimer; @@ -380,7 +403,7 @@ QQuickItem *m_SpeedSlider = nullptr, *m_SpeedLabel = nullptr, *m_raValue = nullptr, *m_deValue = nullptr, *m_azValue = nullptr, *m_altValue = nullptr, *m_haValue = nullptr, *m_zaValue = nullptr, *m_targetText = nullptr, *m_targetRAText = nullptr, *m_targetDEText = nullptr, *m_Park = nullptr, - *m_Unpark = nullptr, *m_statusText = nullptr, *m_J2000Check = nullptr, *m_JNowCheck=nullptr; + *m_Unpark = nullptr, *m_statusText = nullptr, *m_J2000Check = nullptr, *m_JNowCheck=nullptr; }; } diff --git a/kstars/ekos/mount/mount.cpp b/kstars/ekos/mount/mount.cpp --- a/kstars/ekos/mount/mount.cpp +++ b/kstars/ekos/mount/mount.cpp @@ -487,15 +487,16 @@ emit newCoords(raOUT->text(), decOUT->text(), azOUT->text(), altOUT->text()); ISD::Telescope::Status currentStatus = currentTelescope->status(); + bool isTracking = (currentStatus == ISD::Telescope::MOUNT_TRACKING); if (m_Status != currentStatus) { qCDebug(KSTARS_EKOS_MOUNT) << "Mount status changed from " << m_Status << " to " << currentStatus; // If we just finished a slew, let's update initialHA and the current target's position - if (currentStatus == ISD::Telescope::MOUNT_TRACKING) + if (currentStatus == ISD::Telescope::MOUNT_TRACKING && m_Status == ISD::Telescope::MOUNT_SLEWING) { setInitialHA((sgn == '-' ? -1: 1) * ha.Hours()); - currentTargetPosition.setRA(telescopeCoord.ra()); - currentTargetPosition.setDec(telescopeCoord.dec()); + delete currentTargetPosition; + currentTargetPosition = new SkyPoint(telescopeCoord.ra(), telescopeCoord.dec()); } m_Status = currentStatus; @@ -516,7 +517,6 @@ if (trackingGroup->isEnabled()) { - bool isTracking = (currentStatus == ISD::Telescope::MOUNT_TRACKING); trackOnB->setChecked(isTracking); trackOffB->setChecked(!isTracking); } @@ -533,6 +533,9 @@ remainingTime = remainingTime.addMSecs(autoParkTimer.remainingTime()); countdownLabel->setText(remainingTime.toString("hh:mm:ss")); } + + if (isTracking && checkMeridianFlip(lst)) + executeMeridianFlip(); } else updateTimer.stop(); @@ -579,6 +582,26 @@ return false; } +void Mount::activateMeridianFlip(bool activate) +{ + meridianFlipCheckBox->setChecked(activate); +} + +void Mount::setMeridianFlipLimit(double hours) +{ + meridianFlipTimeBox->setValue(hours); +} + +void Mount::setMeridianFlipStatus(MeridianFlipStatus status) +{ + if (m_MFStatus != status) + { + m_MFStatus = status; + meridianFlipStatusChanged(status); + emit newMeridianFlipStatus(status); + } +} + void Mount::updateSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "TELESCOPE_SLEW_RATE")) @@ -827,36 +850,163 @@ if (HA > 12.0) HA -= 24.0; setInitialHA(HA); + // reset the meridian flip status if the slew is not the meridian flip itself + if (m_MFStatus != FLIP_RUNNING) + setMeridianFlipStatus(FLIP_NONE); - currentTargetPosition.setRA(RA); - currentTargetPosition.setDec(DEC); + delete currentTargetPosition; + currentTargetPosition = new SkyPoint(RA, DEC); return currentTelescope->Slew(RA, DEC); } +bool Mount::checkMeridianFlip(dms lst) +{ + + if (currentTelescope == nullptr || currentTelescope->isConnected() == false) + { + meridianFlipStatusText->setText("Status: inactive"); + setMeridianFlipStatus(FLIP_NONE); + return false; + } + + if (currentTelescope->isTracking() == false) + { + meridianFlipStatusText->setText("Status: inactive (not tracking)"); + return false; + } + + + double deltaHA = meridianFlipTimeBox->value() - lst.Hours() + telescopeCoord.ra().Hours(); + int hh = static_cast (deltaHA); + int mm = static_cast ((deltaHA - hh)*60); + int ss = static_cast ((deltaHA - hh - mm/60.0)*3600); + + switch (m_MFStatus) + { + case FLIP_NONE: + + if (initialHA() >= 0 || meridianFlipCheckBox->isChecked() == false) + { + meridianFlipStatusText->setText("Status: inactive"); + return false; + } + + if (deltaHA > 0) + { + QString message = QString("Meridian flip in %1h:%2m:%3s") + .arg(hh, 2, 10, QChar('0')) + .arg(mm, 2, 10, QChar('0')) + .arg(ss, 2, 10, QChar('0')); + meridianFlipStatusText->setText(message); + return false; + } + else if (initialHA() < 0) + { + setMeridianFlipStatus(FLIP_PLANNED); + return false; + } + break; + + case FLIP_PLANNED: + return false; + break; + + case FLIP_ACCEPTED: + return true; + break; + + case FLIP_RUNNING: + if (currentTelescope->isTracking()) + { + // meridian flip completed + setMeridianFlipStatus(FLIP_COMPLETED); + } + break; + + case FLIP_COMPLETED: + setMeridianFlipStatus(FLIP_NONE); + break; + + default: + // do nothing + break; + } + return false; +} + bool Mount::executeMeridianFlip() { if (initialHA() > 0) // no meridian flip necessary return false; + if (currentTelescope->status() != ISD::Telescope::MOUNT_TRACKING) + { + // no meridian flip necessary + return false; + } + dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); - double HA = lst.Hours() - currentTargetPosition.ra().Hours(); + double HA = lst.Hours() - currentTargetPosition->ra().Hours(); if (HA > 12.0) // no meridian flip necessary return false; // execute meridian flip qCDebug(KSTARS_EKOS_MOUNT) << "Meridian flip: slewing to RA=" << - currentTargetPosition.ra().toHMSString() << - "DEC=" << currentTargetPosition.dec().toDMSString(); - slew(currentTargetPosition.ra().Hours(), currentTargetPosition.dec().Degrees()); - return true; + currentTargetPosition->ra().toHMSString() << + "DEC=" << currentTargetPosition->dec().toDMSString(); + setMeridianFlipStatus(FLIP_RUNNING); + + bool result = slew(currentTargetPosition->ra().Hours(), currentTargetPosition->dec().Degrees()); + + if (result == false) + qCWarning(KSTARS_EKOS_MOUNT) << "Meridian flip FAILED: slewing to RA=" << + currentTargetPosition->ra().toHMSString() << + "DEC=" << currentTargetPosition->dec().toDMSString(); + return result; + +} + +void Mount::meridianFlipStatusChanged(Mount::MeridianFlipStatus status) { + + m_MFStatus = status; + + switch (status) + { + case FLIP_NONE: + meridianFlipStatusText->setText("Status: inactive"); + break; + + case FLIP_PLANNED: + meridianFlipStatusText->setText("Meridian flip planned..."); + break; + + case FLIP_WAITING: + meridianFlipStatusText->setText("Meridian flip waiting..."); + break; + + case FLIP_ACCEPTED: + // do nothing, execution of meridian flip initianted in updateTelescopeCoords() + break; + + case FLIP_RUNNING: + meridianFlipStatusText->setText("Meridian flip running..."); + break; + + case FLIP_COMPLETED: + meridianFlipStatusText->setText("Meridian flip completed."); + break; + default: + break; + } + } SkyPoint Mount::currentTarget() { - return currentTargetPosition; + return *currentTargetPosition; } diff --git a/kstars/ekos/mount/mount.ui b/kstars/ekos/mount/mount.ui --- a/kstars/ekos/mount/mount.ui +++ b/kstars/ekos/mount/mount.ui @@ -6,8 +6,8 @@ 0 0 - 498 - 319 + 703 + 464 @@ -348,46 +348,84 @@ - - - false - - - Park + + + Park Telescope + + + + + false + + + Park + + + + + + + false + + + UnPark + + + + - - - false - - - UnPark + + + Meridian Flip + + + + + + + + <html><head/><body><p>Request a meridian flip if the hour angle exceeds the specified value. Capture and Guiding will be suspended and resumed after the flip is complete.</p></body></html> + + + Flip if HA > + + + + + + + h + + + + + + + + + + + - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Reset mount alignment model - - - Clear Alignment Model + + + Alignment Model + + + + + Clear Model + + + + @@ -676,8 +714,8 @@ 3 - - + + false @@ -687,28 +725,34 @@ 0 - - Abort mount if it slews lower than this limit. - - - -10.000000000000000 - 90.000000000000000 + + 90.000000000000000 + - - - - <html><head/><body><p>Abort mount <span style=" font-weight:600;">slew/goto</span> motion if it falls outside the minimum and maximum altitude limits. This does not apply to tracking.</p></body></html> + + + + false - - Enable Limits + + + 0 + 0 + + + + -10.000000000000000 + + + 90.000000000000000 - + false @@ -721,42 +765,27 @@ - - - - false - - - <html><head/><body><p>Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop.</p></body></html> - + + - Max. Alt: + Enable Limits - - + + false - - - 0 - 0 - - - Abort mount if it slews higher than this limit. - - - 90.000000000000000 + <html><head/><body><p>Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop.</p></body></html> - - 90.000000000000000 + + Max. Alt: - + Qt::Vertical @@ -817,9 +846,6 @@ lstOUT minAltLimit maxAltLimit - parkB - unparkB - clearAlignmentModelB enableLimitsCheck diff --git a/kstars/org.kde.kstars.Ekos.Mount.xml b/kstars/org.kde.kstars.Ekos.Mount.xml --- a/kstars/org.kde.kstars.Ekos.Mount.xml +++ b/kstars/org.kde.kstars.Ekos.Mount.xml @@ -7,9 +7,6 @@ - - - @@ -22,7 +19,6 @@ - @@ -60,6 +56,12 @@ + + + + + +