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 @@ -870,6 +870,8 @@ bool resumeGuidingAfterFlip { false }; MFStage meridianFlipStage { MF_NONE }; + QString MFStageString(MFStage stage); + // Flat field automation QVector ExpRaw, ADURaw; double targetADU { 0 }; @@ -932,7 +934,7 @@ SchedulerJob::CapturedFramesMap capturedFramesMap; // Execute the meridian flip - void setMeridianFlipStage(MFStage status); + void setMeridianFlipStage(MFStage stage); void processFlipCompleted(); // Controls 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 @@ -3523,12 +3523,12 @@ HFRPixels->setValue(median + (median * (Options::hFRThresholdPercentage() / 100.0))); } -void Capture::setMeridianFlipStage(MFStage status) +void Capture::setMeridianFlipStage(MFStage stage) { - qCDebug(KSTARS_EKOS_CAPTURE) << "setMeridianFlipStage: " << status; - if (meridianFlipStage != status) + qCDebug(KSTARS_EKOS_CAPTURE) << "setMeridianFlipStage: " << MFStageString(stage); + if (meridianFlipStage != stage) { - switch (status) + switch (stage) { case MF_NONE: if (m_State == CAPTURE_PAUSED) @@ -3538,7 +3538,7 @@ else secondsLabel->setText(""); */ - meridianFlipStage = status; + meridianFlipStage = stage; break; case MF_READY: @@ -3551,7 +3551,7 @@ { // paused after meridian flip requested secondsLabel->setText(i18n("Paused...")); - meridianFlipStage = status; + meridianFlipStage = stage; emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); } // in any other case, ignore it @@ -3570,24 +3570,24 @@ emit newMeridianFlipStatus(Mount::FLIP_ACCEPTED); else emit newMeridianFlipStatus(Mount::FLIP_WAITING); - meridianFlipStage = status; + meridianFlipStage = stage; break; case MF_COMPLETED: secondsLabel->setText(i18n("Flip complete.")); break; default: - meridianFlipStage = status; + meridianFlipStage = stage; break; } } } void Capture::meridianFlipStatusChanged(Mount::MeridianFlipStatus status) { - qCDebug(KSTARS_EKOS_CAPTURE) << "meridianFlipStatusChanged: " << status; + qCDebug(KSTARS_EKOS_CAPTURE) << "meridianFlipStatusChanged: " << Mount::meridianFlipStatusString(status); switch (status) { case Mount::FLIP_NONE: @@ -6828,4 +6828,30 @@ guideState == GUIDE_DITHERING_SETTLE); } +QString Capture::MFStageString(MFStage stage) +{ + switch(stage) + { + case MF_NONE: + return "MF_NONE"; + case MF_REQUESTED: + return "MF_REQUESTED"; + case MF_READY: + return "MF_READY"; + case MF_INITIATED: + return "MF_INITIATED"; + case MF_FLIPPING: + return "MF_FLIPPING"; + case MF_SLEWING: + return "MF_SLEWING"; + case MF_COMPLETED: + return "MF_COMPLETED"; + case MF_ALIGNING: + return "MF_ALIGNING"; + case MF_GUIDING: + return "MF_GUIDING"; + } + return "MFStage unknown."; +} + } 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 @@ -54,7 +54,22 @@ //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; + // This enum defines the meridian flip state machine, this is implemented in + typedef enum + { + FLIP_NONE, // this is the default state, comparing the hour angle with the next flip position + // it moves to FLIP_PLANNED when a flip is needed. + FLIP_PLANNED, // this signals to the Capture class that a flip is required, the Capture class will + // move to FLIP_ACCEPTED when it has completed everything that needs to be done. + FLIP_WAITING, // Capture seems to set this state to signal that the flip will have to wait + FLIP_ACCEPTED, // Capture signals to the mount that a flip slew can be started + FLIP_RUNNING, // this signals that a flip slew is in progress, when the slew stops the state + // is set to FLIP_COMPLETED + FLIP_COMPLETED, // this checks that the flip was completed successfully or not and after tidying up + // moves to FLIP_NONE to wait for the next flip requirement. + // Capture sees this and resumes. + FLIP_ERROR // errors in the flip process should end up here + } MeridianFlipStatus; /** * @brief setTelescope Sets the mount module telescope interface @@ -184,6 +199,8 @@ double initialPositionHA; /** DBUS interface function. * Get the hour angle of that time the mount has slewed to the current position. + * This is used to manage the meridian flip for mounts which do not report pier side. + * only one attempt to flip is done. */ Q_SCRIPTABLE double initialHA() { @@ -286,6 +303,12 @@ Q_INVOKABLE void setUpDownReversed(bool enabled); Q_INVOKABLE void setLeftRightReversed(bool enabled); + /// + /// \brief meridianFlipStatusString + /// \param status + /// \return return the string for the status + /// + static QString meridianFlipStatusString(MeridianFlipStatus status); public slots: @@ -358,6 +381,22 @@ */ void disableAltLimits(); + /** + * @brief enableHourAngleLimits Enable or disable hour angle limits + * @param enable True to enable, false to disable. + */ + void enableHourAngleLimits(bool enable); + + /** + * @brief enableHaLimits calls enableHourAngleLimits(true). This function is mostly used to enable hour angle limit after a meridian flip is complete. + */ + void enableHaLimits(); + + /** + * @brief disableAltLimits calls enableHourAngleLimits(false). This function is mostly used to disable altitude limit once a meridial flip process is started. + */ + void disableHaLimits(); + bool setScopeConfig(int index); void toggleMountToolBox(); @@ -371,7 +410,7 @@ */ void setMeridianFlipValues(bool activate, double hours); - private slots: +private slots: /** * @brief registerNewModule Register an Ekos module as it arrives via DBus @@ -401,6 +440,15 @@ void syncGPS(); MeridianFlipStatus m_MFStatus = FLIP_NONE; void setMeridianFlipStatus(MeridianFlipStatus status); + QString pierSideStateString(); + + // A meridian flip requires a slew of 180 degrees in the hour angle axis so will take at least + // the time for that, currently set to 20 seconds + // not reliable for pointing state change detection but reported if the pier side is unknown + QDateTime minMeridianFlipEndTime; + int minMeridianFlipDurationSecs = 20; + + double flipDelayHrs = 0.0; // delays the next flip attempt if it fails QPointer captureInterface { nullptr }; @@ -415,6 +463,8 @@ double lastAlt; int abortDispatch; bool altLimitEnabled; + bool haLimitEnabled; + double lastHa; bool GPSInitialized = {false}; ISD::Telescope::Status m_Status = ISD::Telescope::MOUNT_IDLE; 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 @@ -1,4 +1,4 @@ -/* Ekos Mount Module +/* Ekos Mount Module Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or @@ -71,9 +71,11 @@ minAltLimit->setValue(Options::minimumAltLimit()); maxAltLimit->setValue(Options::maximumAltLimit()); + maxHaLimit->setValue(Options::maximumHaLimit()); connect(minAltLimit, SIGNAL(editingFinished()), this, SLOT(saveLimits())); connect(maxAltLimit, SIGNAL(editingFinished()), this, SLOT(saveLimits())); + connect(maxHaLimit, SIGNAL(editingFinished()), this, SLOT(saveLimits())); connect(mountToolBoxB, SIGNAL(clicked()), this, SLOT(toggleMountToolBox())); @@ -110,6 +112,9 @@ connect(enableLimitsCheck, SIGNAL(toggled(bool)), this, SLOT(enableAltitudeLimits(bool))); enableLimitsCheck->setChecked(Options::enableAltitudeLimits()); altLimitEnabled = enableLimitsCheck->isChecked(); + connect(enableHaLimitCheck, SIGNAL(toggled(bool)), this, SLOT(enableHourAngleLimits(bool))); + enableHaLimitCheck->setChecked(Options::enableHaLimit()); + //haLimitEnabled = enableHaLimitCheck->isChecked(); // meridian flip meridianFlipCheckBox->setChecked(Options::executeMeridianFlip()); @@ -318,6 +323,7 @@ currentGPS = nullptr; } } + void Mount::syncTelescopeInfo() { if (currentTelescope == nullptr || currentTelescope->isConnected() == false) @@ -530,11 +536,13 @@ dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); dms ha(lst - telescopeCoord.ra()); QChar sgn('+'); + if (ha.Hours() > 12.0) { ha.setH(24.0 - ha.Hours()); sgn = '-'; } + haOUT->setText(QString("%1%2").arg(sgn).arg(ha.toHMSString())); m_haValue->setProperty("text", haOUT->text()); @@ -582,22 +590,79 @@ else abortDispatch = -1; + //qCDebug(KSTARS_EKOS_MOUNT) << "maxHaLimit " << maxHaLimit->isEnabled() << " value " << maxHaLimit->value(); + + // handle Ha limit: + // Telescope must report Pier Side + // maxHaLimit must be enabled + // for PierSide West -> East if Ha > maxHaLimit stop tracking + // for PierSide East -> West if Ha > maxHaLimit - 12 stop Tracking + if (maxHaLimit->isEnabled()) + { + // get hour angle limit + double haLimit = maxHaLimit->value(); + bool haLimitReached = false; + switch(currentTelescope->pierSide()) + { + case ISD::Telescope::PierSide::PIER_WEST: + haLimitReached = hourAngle() > haLimit; + break; + case ISD::Telescope::PierSide::PIER_EAST: + haLimitReached = rangeHA(hourAngle() + 12.0) > haLimit; + break; + default: + // can't tell so always false + haLimitReached = false; + break; + } + + qCDebug(KSTARS_EKOS_MOUNT) << "Ha: " << hourAngle() << + " haLimit " << haLimit << + " " << pierSideStateString() << + " haLimitReached " << (haLimitReached ? "true" : "false") << + " lastHa " << lastHa; + // compare with last ha to avoid multiple calls + if (haLimitReached && (rangeHA(hourAngle() - lastHa) >= 0 ) && + (abortDispatch == -1 || + currentTelescope->isInMotion())) + { + // moved past the limit, so stop + appendLogText(i18n("Telescope hour angle is more than the maximum hour angle of %1. Aborting motion...", + QString::number(maxHaLimit->value(), 'g', 3))); + currentTelescope->Abort(); + currentTelescope->setTrackEnabled(false); + //KNotification::event( QLatin1String( "OperationFailed" )); + KNotification::beep(); + abortDispatch++; + // ideally we pause and wait until we have passed the pier flip limit, + // then do a pier flip and try to resume + // this will need changing to use a target position because the current HA has stopped. + } + } + else + abortDispatch = -1; + lastAlt = currentAlt; + lastHa = hourAngle(); 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 " << currentTelescope->getStatusString(m_Status) << " to " << currentTelescope->getStatusString(currentStatus); // If we just finished a slew, let's update initialHA and the current target's position if (currentStatus == ISD::Telescope::MOUNT_TRACKING && m_Status == ISD::Telescope::MOUNT_SLEWING) { + if (m_MFStatus == FLIP_NONE) + { + flipDelayHrs = 0; + } setInitialHA((sgn == '-' ? -1 : 1) * ha.Hours()); delete currentTargetPosition; currentTargetPosition = new SkyPoint(telescopeCoord.ra(), telescopeCoord.dec()); + qCDebug(KSTARS_EKOS_MOUNT) << "Slew finished, MFStatus " << meridianFlipStatusString(m_MFStatus); } m_Status = currentStatus; @@ -616,12 +681,16 @@ emit newStatus(m_Status); } + bool isTracking = (currentStatus == ISD::Telescope::MOUNT_TRACKING); if (trackingGroup->isEnabled()) { trackOnB->setChecked(isTracking); trackOffB->setChecked(!isTracking); } + // handle pier side display + pierSideLabel->setText(pierSideStateString()); + if (currentTelescope->isConnected() == false) updateTimer.stop(); else if (updateTimer.isActive() == false) @@ -649,7 +718,7 @@ if (nvp->s == IPS_ALERT) { QString newMessage; - if (primaryScopeApertureIN->value() == 1 || primaryScopeFocalIN->value() == 1) + if (primaryScopeApertureIN->value() <= 1 || primaryScopeFocalIN->value() <= 1) newMessage = i18n("Error syncing telescope info. Please fill telescope aperture and focal length."); else newMessage = i18n("Error syncing telescope info. Check INDI control panel for more details."); @@ -725,13 +794,12 @@ Options::setMeridianFlipOffset(offset); } - void Mount::setMeridianFlipStatus(MeridianFlipStatus status) { if (m_MFStatus != status) { m_MFStatus = status; - qCDebug (KSTARS_EKOS_MOUNT) << "Setting meridian flip status to " << status; + qCDebug (KSTARS_EKOS_MOUNT) << "Setting meridian flip status to " << meridianFlipStatusString(status); meridianFlipStatusChanged(status); emit newMeridianFlipStatus(status); @@ -838,11 +906,11 @@ np = IUFindNumber(nvp, "GUIDER_APERTURE"); if (np) np->value = - guideScopeApertureIN->value() == 1 ? primaryScopeApertureIN->value() : guideScopeApertureIN->value(); + guideScopeApertureIN->value() <= 1 ? primaryScopeApertureIN->value() : guideScopeApertureIN->value(); np = IUFindNumber(nvp, "GUIDER_FOCAL_LENGTH"); if (np) - np->value = guideScopeFocalIN->value() == 1 ? primaryScopeFocalIN->value() : guideScopeFocalIN->value(); + np->value = guideScopeFocalIN->value() <= 1 ? primaryScopeFocalIN->value() : guideScopeFocalIN->value(); ClientManager *clientManager = currentTelescope->getDriverInfo()->getClientManager(); @@ -861,6 +929,8 @@ Options::setMinimumAltLimit(minAltLimit->value()); Options::setMaximumAltLimit(maxAltLimit->value()); currentTelescope->setAltLimits(minAltLimit->value(), maxAltLimit->value()); + + Options::setMaximumHaLimit(maxHaLimit->value()); } void Mount::enableAltitudeLimits(bool enable) @@ -905,6 +975,28 @@ enableAltitudeLimits(false); } +void Mount::enableHourAngleLimits(bool enable) +{ + //Options::setEnableHaLimit(enable); + + maxHaLabel->setEnabled(enable); + maxHaLimit->setEnabled(enable); +} + +void Mount::enableHaLimits() +{ + //Only enable if it was already enabled before and the minHaLimit is currently disabled. + if (haLimitEnabled && maxHaLimit->isEnabled() == false) + enableHourAngleLimits(true); +} + +void Mount::disableHaLimits() +{ + haLimitEnabled = enableHaLimitCheck->isChecked(); + + enableHourAngleLimits(false); +} + QList Mount::altitudeLimits() { QList limits; @@ -1008,145 +1100,267 @@ setInitialHA(HA); // reset the meridian flip status if the slew is not the meridian flip itself if (m_MFStatus != FLIP_RUNNING) + { setMeridianFlipStatus(FLIP_NONE); + flipDelayHrs = 0; + qCDebug(KSTARS_EKOS_MOUNT) << "flipDelayHrs set to zero in slew, m_MFStatus=" << + meridianFlipStatusString(m_MFStatus); + } delete currentTargetPosition; currentTargetPosition = new SkyPoint(RA, DEC); qCDebug(KSTARS_EKOS_MOUNT) << "Slewing to RA=" << currentTargetPosition->ra().toHMSString() << "DEC=" << currentTargetPosition->dec().toDMSString(); + qCDebug(KSTARS_EKOS_MOUNT) << "Initial HA " << initialHA() << ", flipDelayHrs " << flipDelayHrs << + "MFStatus " << meridianFlipStatusString(m_MFStatus); - return currentTelescope->Slew(currentTargetPosition); + // a slew to the current position can return so quickly that the status isn't updated + if (currentTelescope->Slew(currentTargetPosition)) + { + m_Status = ISD::Telescope::Status::MOUNT_SLEWING; + return true; + } + + return false; } +/// +/// \brief Mount::checkMeridianFlip This updates the Meridian Flip Status state machine using the LST supplied, +/// the mount target position and the pier side if available. +/// \param lst +/// \return true if a flip slew can be started, false otherwise +/// bool Mount::checkMeridianFlip(dms lst) { - + // checks if a flip is possible if (currentTelescope == nullptr || currentTelescope->isConnected() == false) { - meridianFlipStatusText->setText("Status: inactive"); + meridianFlipStatusText->setText("Status: inactive (no scope connected)"); setMeridianFlipStatus(FLIP_NONE); return false; } - if (currentTelescope->isTracking() == false) + if (meridianFlipCheckBox->isChecked() == false) + { + meridianFlipStatusText->setText("Status: inactive (flip not requested)"); + return false; + } + + if (currentTelescope->isParked()) + { + meridianFlipStatusText->setText("Status: inactive (parked)"); + return false; + } + + if (currentTargetPosition == nullptr) { - meridianFlipStatusText->setText("Status: inactive (not tracking)"); + meridianFlipStatusText->setText("Status: inactive (no Target set)"); return false; } + // get the time after the meridian that the flip is called for double offset = meridianFlipTimeBox->value(); // Degrees --> Hours if (meridianFlipDegreesR->isChecked()) offset = rangeHA(offset / 15.0); - double deltaHA = offset - lst.Hours() + telescopeCoord.ra().Hours(); - deltaHA = rangeHA(deltaHA); - int hh = static_cast (deltaHA); - int mm = static_cast ((deltaHA - hh) * 60); - int ss = static_cast ((deltaHA - hh - mm / 60.0) * 3600); + double hrsToFlip = 0; // time to go to the next flip - hours -ve means a flip is required - switch (m_MFStatus) - { - case FLIP_NONE: + double ha = rangeHA(lst.Hours() - telescopeCoord.ra().Hours()); // -12 to 0 to +12 - if (initialHA() >= 0 || meridianFlipCheckBox->isChecked() == false) - { - meridianFlipStatusText->setText("Status: inactive"); - return false; - } + // calculate time to next flip attempt. This uses the current hour angle, the pier side if available + // and the meridian flip offset to get the time to the flip + // + // *** should it use the target position so it will continue to track the target even if the mount is not tracking? + // + // Note: the PierSide code relies on the mount reporting the pier side correctly - if (deltaHA > 0) + static ISD::Telescope::PierSide initialPierSide; // used when the flip has completed to determine if the flip was successful + + switch (currentTelescope->pierSide()) + { + case ISD::Telescope::PierSide::PIER_WEST: + // this is the normal case, tracking from East to West, flip is near Ha 0. + hrsToFlip = offset + flipDelayHrs - ha; // lst.Hours() + telescopeCoord.ra().Hours(); + break; + case ISD::Telescope::PierSide::PIER_EAST: + // this is the below the pole case, tracking West to East, flip is near Ha 12. + hrsToFlip = offset + flipDelayHrs - rangeHA(ha + 12); // lst.Hours() + telescopeCoord.ra().Hours()); + break; + default: + // This is the case where the PierSide is not available, make one attempt only + hrsToFlip = offset - ha; //lst.Hours() + telescopeCoord.ra().Hours(); + // we can only attempt a flip if the mount started before the meridian, assumed in the unflipped state + if (initialHA() >= 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; + meridianFlipStatusText->setText("Status: inactive (slew after meridian)"); + if (m_MFStatus == FLIP_NONE) + return false; } - else if (initialHA() < 0) - { - double offset = meridianFlipTimeBox->value(); - // Degrees --> Hours - if (meridianFlipDegreesR->isChecked()) - offset = rangeHA(offset / 15.0); + break; + } - qCDebug(KSTARS_EKOS_MOUNT) << "Meridian flip planned with LST=" << - lst.toHMSString() << - " scope RA=" << telescopeCoord.ra().toHMSString() << - " and meridian diff=" << offset; + int hh = static_cast (hrsToFlip); + int mm = static_cast ((hrsToFlip - hh) * 60); + int ss = static_cast ((hrsToFlip - hh - mm / 60.0) * 3600); + 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')); + // handle the meridian flip state machine + switch (m_MFStatus) + { + case FLIP_NONE: + meridianFlipStatusText->setText(message); + + if (hrsToFlip <= 0) + { + // signal that a flip can be done + qCDebug(KSTARS_EKOS_MOUNT) << "Meridian flip planned with LST=" << + lst.toHMSString() << + " scope RA=" << telescopeCoord.ra().toHMSString() << + ", meridian diff=" << offset << + ", hrstoFlip=" << hrsToFlip << + ", flipDelayHrs=" << flipDelayHrs << + ", " << pierSideStateString(); + + initialPierSide = currentTelescope->pierSide(); setMeridianFlipStatus(FLIP_PLANNED); - return false; } break; case FLIP_PLANNED: + // handle the case where there is no Capture module + if (captureInterface == nullptr) + { + qCDebug(KSTARS_EKOS_MOUNT) << "no capture interface, starting flip slew."; + setMeridianFlipStatus(FLIP_ACCEPTED); + return true; + } return false; case FLIP_ACCEPTED: + // set by the Capture module when it's ready return true; case FLIP_RUNNING: if (currentTelescope->isTracking()) { - // meridian flip completed - setMeridianFlipStatus(FLIP_COMPLETED); + // meridian flip slew completed, did it work? + bool flipFailed = false; + + // pointing state change check only for mounts that report pier side + if (currentTelescope->pierSide() == ISD::Telescope::PIER_UNKNOWN) + { + // check how long it took + if (minMeridianFlipEndTime > QDateTime::currentDateTimeUtc()) + { + // don't fail, we have tried but we don't know where the mount was when it started + appendLogText(i18n("Meridian flip failed - time too short, pier side unknown.")); + // signal that capture can resume + setMeridianFlipStatus(FLIP_COMPLETED); + return false; + } + } + else if (currentTelescope->pierSide() == initialPierSide) + { + flipFailed = true; + qCWarning(KSTARS_EKOS_MOUNT) << "Meridian flip failed, pier side not changed"; + } + + if (flipFailed) + { + if (flipDelayHrs <= 1.0) + { + flipDelayHrs += (4.0 / 60.0); // add 4 minutes delay + + // check to stop an infinite loop, 1.0 hrs for now but should use the Ha limit + appendLogText(i18n("meridian flip failed, retrying in 4 minutes")); + } + else + { + appendLogText(i18n("No successful Meridian Flip done, delay too long")); + } + setMeridianFlipStatus(FLIP_COMPLETED); // this will resume imaging and try again after the extra delay + } + else + { + flipDelayHrs = 0; + appendLogText(i18n("Meridian flip completed OK.")); + // signal that capture can resume + setMeridianFlipStatus(FLIP_COMPLETED); + } } break; case FLIP_COMPLETED: setMeridianFlipStatus(FLIP_NONE); break; default: - // do nothing break; } return false; } bool Mount::executeMeridianFlip() { - if (initialHA() > 0 || currentTargetPosition == nullptr) + if (/*initialHA() > 0 || */ currentTargetPosition == nullptr) + { // no meridian flip necessary + qCDebug(KSTARS_EKOS_MOUNT) << "No meridian flip: currentTargetPosition is null"; return false; + } if (currentTelescope->status() != ISD::Telescope::MOUNT_TRACKING) { // no meridian flip necessary + qCDebug(KSTARS_EKOS_MOUNT) << "No meridian flip: mount not tracking"; return false; } dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); double HA = lst.Hours() - currentTargetPosition->ra().Hours(); if (HA > 12.0) + { // no meridian flip necessary + qCInfo(KSTARS_EKOS_MOUNT) << "No Meridian flip: HA=" << + dms(HA).toHMSString(); return false; + } // execute meridian flip qCInfo(KSTARS_EKOS_MOUNT) << "Meridian flip: slewing to RA=" << currentTargetPosition->ra().toHMSString() << - "DEC=" << currentTargetPosition->dec().toDMSString(); + "DEC=" << currentTargetPosition->dec().toDMSString() << + " Hour Angle " << dms(HA).toHMSString(); setMeridianFlipStatus(FLIP_RUNNING); - bool result = slew(currentTargetPosition->ra().Hours(), currentTargetPosition->dec().Degrees()); + minMeridianFlipEndTime = KStarsData::Instance()->clock()->utc().addSecs(minMeridianFlipDurationSecs); - if (result == false) + if (slew(currentTargetPosition->ra().Hours(), currentTargetPosition->dec().Degrees())) + { + appendLogText(i18n("Meridian flip slew started...")); + return true; + } + else + { qCWarning(KSTARS_EKOS_MOUNT) << "Meridian flip FAILED: slewing to RA=" << currentTargetPosition->ra().toHMSString() << "DEC=" << currentTargetPosition->dec().toDMSString(); - return result; - + return false; + } } void Mount::meridianFlipStatusChanged(Mount::MeridianFlipStatus status) { - m_MFStatus = status; + qCDebug(KSTARS_EKOS_MOUNT) << "meridianFlipStatusChanged " << meridianFlipStatusString(status); + switch (status) { case FLIP_NONE: @@ -1175,20 +1389,18 @@ case FLIP_COMPLETED: meridianFlipStatusText->setText("Meridian flip completed."); break; + default: break; } - - } SkyPoint Mount::currentTarget() { return *currentTargetPosition; } - bool Mount::sync(const QString &RA, const QString &DEC) { dms ra, de; @@ -1254,6 +1466,10 @@ return coords; } +/// +/// \brief Mount::hourAngle +/// \return returns the current mount hour angle in hours in the range -12 to +12 +/// double Mount::hourAngle() { dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst()); @@ -1566,7 +1782,7 @@ if (currentTelescope->isParked()) { - appendLogText("Mount already parked."); + appendLogText(i18n("Mount already parked.")); return; } @@ -1586,7 +1802,7 @@ parkDateTime = parkDateTime.addDays(1); parkMilliSeconds = parkDateTime.msecsTo(currentDateTime); - int hours = parkMilliSeconds / (1000 * 60 * 60); + int hours = static_cast(parkMilliSeconds / (1000 * 60 * 60)); if (hours > 0) { // No need to display warning for every day check @@ -1614,7 +1830,7 @@ appendLogText(i18n("Caution: do not use Auto Park while scheduler is active.")); - autoParkTimer.setInterval(parkMilliSeconds); + autoParkTimer.setInterval(static_cast(parkMilliSeconds)); autoParkTimer.start(); startTimerB->setEnabled(false); @@ -1646,4 +1862,39 @@ } } +QString Mount::meridianFlipStatusString(MeridianFlipStatus status) +{ + switch (status) + { + case FLIP_NONE: + return "FLIP_NONE"; + case FLIP_PLANNED: + return "FLIP_PLANNED"; + case FLIP_WAITING: + return "FLIP_WAITING"; + case FLIP_ACCEPTED: + return "FLIP_ACCEPTED"; + case FLIP_RUNNING: + return "FLIP_RUNNING"; + case FLIP_COMPLETED: + return "FLIP_COMPLETED"; + case FLIP_ERROR: + return "FLIP_ERROR"; + } + return "not possible"; +} + +QString Mount::pierSideStateString() +{ + switch (currentTelescope->pierSide()) + { + case ISD::Telescope::PierSide::PIER_EAST: + return "Pier Side: East (pointing West)"; + case ISD::Telescope::PierSide::PIER_WEST: + return "Pier Side: West (pointing East)"; + default: + return "Pier Side: Unknown"; + } +} + } 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 @@ -1,1019 +1,1070 @@ - - - Mount - - - - 0 - 0 - 577 - 419 - - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - 3 - - - - - 3 - - - - - - - Primary Telescope - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Aperture (mm) - - - - - - - 1.000000000000000 - - - 100000.000000000000000 - - - 10.000000000000000 - - - - - - - Focal Length (mm) - - - - - - - 1.000000000000000 - - - 100000.000000000000000 - - - 10.000000000000000 - - - - - - - - - - Guide Telescope - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Focal Length (mm) - - - - - - - 1.000000000000000 - - - 100000.000000000000000 - - - 10.000000000000000 - - - - - - - 1.000000000000000 - - - 100000.000000000000000 - - - 10.000000000000000 - - - - - - - Aperture (mm) - - - - - - - - - - - - - - Configurations - - - - - - - <html><head/><body><p>Scope configuration index. You can define up to 6 different combinations of primary and secondary scopes.</p></body></html> - - - - - - - Configuration label - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Save telescope information in configuration file - - - Save Telescope Info - - - - - - - - - Coordinates - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Right Ascension - - - RA - - - - - - - true - - - - - - - Declination - - - DEC - - - - - - - - - - true - - - - - - - Azimuth - - - AZ - - - - - - - true - - - - - - - Altitude - - - ALT - - - - - - - true - - - - - - - Hour Angle - - - HA - - - - - - - true - - - - - - - Local Sidereal TIme - - - LST - - - - - - - true - - - - - - - - - - - - Meridian Flip - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - - - - - - - <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 > - - - - - - - <html><head/><body><p>Set Hour Angle unit to Degrees. Add 1 degree to the mount meridian flip setting. For example, if the mount meridian flip limit is set to 5 degrees, set the value here to be <span style=" font-weight:600;">at least</span> 6 degrees to ensure a successful meridian flip.</p></body></html> - - - Degrees - - - meridianFlipUnit - - - - - - - 0.100000000000000 - - - - - - - Set Hour Angle unit to Hours - - - Hours - - - meridianFlipUnit - - - - - - - - - - Reset - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Clear Parking - - - - .. - - - - 24 - 24 - - - - - - - - Deletes all mount alignment points - - - Clear Model - - - - .. - - - - 24 - 24 - - - - - - - - Purge all configuration - - - - .. - - - - 24 - 24 - - - - - - - - - - - - - Auto Park - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Park At: - - - - - - - - - - - - - - Automatically start the park timer on startup - - - Every day - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Arial - 26 - 75 - false - true - - - - QFrame::Box - - - 2 - - - 00:00:00 - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - - - - .. - - - - 32 - 32 - - - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - - - - .. - - - - 32 - 32 - - - - - - - - - - - - - 3 - - - - - - 75 - true - - - - Mount Control - - - - .. - - - - 32 - 32 - - - - Ctrl+S - - - true - - - true - - - - - - - false - - - Tracking - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - 48 - 48 - - - - - 48 - 48 - - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - ON - - - true - - - false - - - - - - - - 48 - 48 - - - - - 48 - 48 - - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - OFF - - - true - - - - - - - - - - Parking - - - - - - false - - - - 48 - 48 - - - - - 48 - 48 - - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - Park - - - - - - - false - - - - 48 - 48 - - - - - 48 - 48 - - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - UnPark - - - - - - - - - - <html><head/><body><p><br/></p></body></html> - - - Limits - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - false - - - - 0 - 0 - - - - <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 - - - 90.000000000000000 - - - - - - - false - - - - 0 - 0 - - - - <html><head/><body><p>Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop.</p></body></html> - - - -10.000000000000000 - - - 90.000000000000000 - - - - - - - false - - - Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop. - - - Min. Alt: - - - - - - - <html><head/><body><p>Enable or Disable the mount travel range limits. Once enabled, Ekos monitors the mount's altitude while slewing or tracking. If the mount slews/tracks below or above the limits, it shall be commanded to stop and tracking will be turned off.</p></body></html> - - - Enable Limits - - - - - - - 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: - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - Qt::Vertical - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - primaryScopeApertureIN - primaryScopeFocalIN - guideScopeApertureIN - guideScopeFocalIN - scopeConfigCombo - scopeConfigNameEdit - saveB - mountToolBoxB - trackOnB - trackOffB - raOUT - decOUT - azOUT - altOUT - haOUT - lstOUT - minAltLimit - maxAltLimit - enableLimitsCheck - - - - - - - + + + Mount + + + + 0 + 0 + 743 + 519 + + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 3 + + + + + 3 + + + + + + + Primary Telescope + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Aperture (mm) + + + + + + + 1.000000000000000 + + + 100000.000000000000000 + + + 10.000000000000000 + + + + + + + Focal Length (mm) + + + + + + + 1.000000000000000 + + + 100000.000000000000000 + + + 10.000000000000000 + + + + + + + + + + Guide Telescope + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Focal Length (mm) + + + + + + + 1.000000000000000 + + + 100000.000000000000000 + + + 10.000000000000000 + + + + + + + 1.000000000000000 + + + 100000.000000000000000 + + + 10.000000000000000 + + + + + + + Aperture (mm) + + + + + + + + + + + + + + Configurations + + + + + + + <html><head/><body><p>Scope configuration index. You can define up to 6 different combinations of primary and secondary scopes.</p></body></html> + + + + + + + Configuration label + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save telescope information in configuration file + + + Save Telescope Info + + + + + + + + + Coordinates + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Right Ascension + + + RA + + + + + + + true + + + + + + + Declination + + + DEC + + + + + + + + + + true + + + + + + + Azimuth + + + AZ + + + + + + + true + + + + + + + Altitude + + + ALT + + + + + + + true + + + + + + + Hour Angle + + + HA + + + + + + + true + + + + + + + Local Sidereal TIme + + + LST + + + + + + + true + + + + + + + + + + + + Meridian Flip + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + <html><head/><body><p>Set Hour Angle unit to Degrees. Add 1 degree to the mount meridian flip setting. For example, if the mount meridian flip limit is set to 5 degrees, set the value here to be <span style=" font-weight:600;">at least</span> 6 degrees to ensure a successful meridian flip.</p></body></html> + + + Degrees + + + meridianFlipUnit + + + + + + + Set Hour Angle unit to Hours + + + Hours + + + meridianFlipUnit + + + + + + + + + + + + + + <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 > + + + + + + + 0.100000000000000 + + + + + + + pier side label + + + + + + + + + + Reset + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Clear Parking + + + + .. + + + + 24 + 24 + + + + + + + + Deletes all mount alignment points + + + Clear Model + + + + .. + + + + 24 + 24 + + + + + + + + Purge all configuration + + + + .. + + + + 24 + 24 + + + + + + + + + + + + + Auto Park + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Park At: + + + + + + + + + + + + + + Automatically start the park timer on startup + + + Every day + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Arial + 26 + 75 + false + true + + + + QFrame::Box + + + 2 + + + 00:00:00 + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + .. + + + + 32 + 32 + + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + .. + + + + 32 + 32 + + + + + + + + + + + + + 3 + + + + + + 75 + true + + + + Mount Control + + + + .. + + + + 32 + 32 + + + + Ctrl+S + + + true + + + true + + + + + + + false + + + Tracking + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 48 + 48 + + + + + 48 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + ON + + + true + + + false + + + + + + + + 48 + 48 + + + + + 48 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + OFF + + + true + + + + + + + + + + Parking + + + + + + false + + + + 64 + 48 + + + + + 64 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Park + + + + + + + false + + + + 64 + 48 + + + + + 64 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + UnPark + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + <html><head/><body><p><br/></p></body></html> + + + Limits + + + + + + 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: + + + + + + + false + + + + 0 + 0 + + + + <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 + + + 90.000000000000000 + + + + + + + <html><head/><body><p>Enable or Disable the mount travel range limits. Once enabled, Ekos monitors the mount's altitude while slewing or tracking. If the mount slews/tracks below or above the limits, it shall be commanded to stop and tracking will be turned off.</p></body></html> + + + Enable Alt Limits + + + + + + + false + + + <html><head/><body><p>Maximum Hour Angle limit if the mount has not flipped. If the telescope is above this limit, it will be commanded to stop.</p></body></html> + + + Max. Ha (hrs): + + + + + + + false + + + + 0 + 0 + + + + <html><head/><body><p>Maximum Hour Angle limit if the mount has not flipped. If the telescope is above this limit, it will be commanded to stop.</p></body></html> + + + -2.000000000000000 + + + 2.000000000000000 + + + 0.100000000000000 + + + 0.000000000000000 + + + + + + + false + + + Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop. + + + Min. Alt: + + + + + + + false + + + + 0 + 0 + + + + <html><head/><body><p>Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop.</p></body></html> + + + -10.000000000000000 + + + 90.000000000000000 + + + + + + + <html><head/><body><p>Enable or Disable the mount Hour Angle limit. Once enabled, Ekos monitors the mount's hour angle while slewing or tracking. If the mount slews/tracks past the limit while the Pier Side is in the state that does not allow that, it shall be commanded to stop and tracking will be turned off.Requires a mount that reports the pier side correctly.</p></body></html> + + + Enable Ha Limits + + + + + + + + + + + + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + primaryScopeApertureIN + primaryScopeFocalIN + guideScopeApertureIN + guideScopeFocalIN + scopeConfigCombo + scopeConfigNameEdit + saveB + mountToolBoxB + trackOnB + trackOffB + raOUT + decOUT + azOUT + altOUT + haOUT + lstOUT + minAltLimit + maxAltLimit + + + + + + + diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg --- a/kstars/kstars.kcfg +++ b/kstars/kstars.kcfg @@ -1556,6 +1556,15 @@ If the target hour angle exceeds this value, Ekos will command a meridian flip and if successful it will resume guiding and capture operations. + + + Maximum limit for the hour angle of the telescope. If the hour angle of the telescope is above this limit, a meridian flip will be forced. + 90.0 + + + + false + false