diff --git a/kstars/ekos/focus/focus.h b/kstars/ekos/focus/focus.h --- a/kstars/ekos/focus/focus.h +++ b/kstars/ekos/focus/focus.h @@ -418,6 +418,17 @@ // Move the focuser in (negative) or out (positive amount). bool changeFocus(int amount); + // Start up capture, or occasionally move focuser again, after current focus-move accomplished. + void autoFocusProcessPositionChange(IPState state); + + // For the Linear algorithm, which always scans in (from higher position to lower position) + // if we notice the new position is higher than the current position (that is, it is the start + // of a new scan), we adjust the new position to be several steps further out than requested + // and set focuserAdditionalMovement to the extra motion, so that after this motion completes + // we will then scan back in (back to the originally requested position). This "dance" is done + // to reduce backlash on such movement changes and so that we've always focused in before capture. + int adjustLinearPosition(int position, int newPosition); + /** * @brief syncTrackingBoxPosition Sync the tracking box to the current selected star center */ @@ -610,5 +621,6 @@ // Experimental linear focuser. std::unique_ptr linearFocuser; + int focuserAdditionalMovement { 0 }; }; } diff --git a/kstars/ekos/focus/focus.cpp b/kstars/ekos/focus/focus.cpp --- a/kstars/ekos/focus/focus.cpp +++ b/kstars/ekos/focus/focus.cpp @@ -600,6 +600,7 @@ } inAutoFocus = true; + focuserAdditionalMovement = 0; HFRFrames.clear(); resetButtons(); @@ -668,7 +669,7 @@ maxTravelIN->value(), stepIN->value(), position, absMotionMin, absMotionMax, MAXIMUM_ABS_ITERATIONS, toleranceIN->value() / 100.0); linearFocuser.reset(MakeLinearFocuser(params)); - const int newPosition = linearFocuser->initialPosition(); + const int newPosition = adjustLinearPosition(position, linearFocuser->initialPosition()); if (newPosition != position) { if (!changeFocus(newPosition - position)) { @@ -682,6 +683,23 @@ capture(); } +int Focus::adjustLinearPosition(int position, int newPosition) +{ + if (newPosition > position) + { + constexpr int extraMotionSteps = 5; + int adjustment = extraMotionSteps * stepIN->value(); + if (newPosition + adjustment > absMotionMax) + adjustment = static_cast(absMotionMax) - newPosition; + + focuserAdditionalMovement = adjustment; + qCDebug(KSTARS_EKOS_FOCUS) << QString("LinearFocuser: extending outward movement by %1").arg(adjustment); + + return newPosition + adjustment; + } + return newPosition; +} + void Focus::checkStopFocus() { if (inSequenceFocus == true) @@ -715,6 +733,7 @@ ISD::CCDChip *targetChip = currentCCD->getChip(ISD::CCDChip::PRIMARY_CCD); inAutoFocus = false; + focuserAdditionalMovement = 0; inFocusLoop = false; // Why starSelected is set to false below? We should retain star selection status under: // 1. Autostar is off, or @@ -1667,7 +1686,9 @@ drawHFRPlot(); if (focusAlgorithm == FOCUS_LINEAR) { - const int nextPosition = linearFocuser->newMeasurement(currentPosition, currentHFR); + const int nextPosition = adjustLinearPosition( + static_cast(currentPosition), + linearFocuser->newMeasurement(currentPosition, currentHFR)); if (nextPosition == -1) { if (linearFocuser->isDone() && linearFocuser->solution() != -1) @@ -2143,6 +2164,40 @@ } }*/ +void Focus::autoFocusProcessPositionChange(IPState state) +{ + if (state == IPS_OK && captureInProgress == false) + { + // Normally, if we are auto-focusing, after we move the focuser we capture an image. + // However, the Linear algorithm, at the start of its passes, requires two + // consecutive focuser moves--the first out further than we want, and a second + // move back in, so that we eliminate backlash and are always moving in before a capture. + if (focuserAdditionalMovement > 0) + { + int temp = focuserAdditionalMovement; + focuserAdditionalMovement = 0; + qCDebug(KSTARS_EKOS_FOCUS) << QString("LinearFocuser: un-doing extension. Moving back in by %1").arg(temp); + + if (!focusIn(temp)) + { + appendLogText(i18n("Focuser error, check INDI panel.")); + abort(); + setAutoFocusResult(false); + } + } + else + { + QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture); + } + } + else if (state == IPS_ALERT) + { + appendLogText(i18n("Focuser error, check INDI panel.")); + abort(); + setAutoFocusResult(false); + } +} + void Focus::processFocusNumber(INumberVectorProperty *nvp) { // Return if it is not our current focuser @@ -2182,15 +2237,7 @@ if (canAbsMove && inAutoFocus) { - if (nvp->s == IPS_OK && captureInProgress == false) - QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture); - //capture(); - else if (nvp->s == IPS_ALERT) - { - appendLogText(i18n("Focuser error, check INDI panel.")); - abort(); - setAutoFocusResult(false); - } + autoFocusProcessPositionChange(nvp->s); } else if (nvp->s == IPS_ALERT) appendLogText(i18n("Focuser error, check INDI panel.")); @@ -2227,14 +2274,7 @@ if (canRelMove && inAutoFocus) { - if (nvp->s == IPS_OK && captureInProgress == false) - QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture); - else if (nvp->s == IPS_ALERT) - { - appendLogText(i18n("Focuser error, check INDI panel.")); - abort(); - setAutoFocusResult(false); - } + autoFocusProcessPositionChange(nvp->s); } else if (nvp->s == IPS_ALERT) appendLogText(i18n("Focuser error, check INDI panel.")); @@ -2256,14 +2296,7 @@ if (canAbsMove == false && canRelMove == false && inAutoFocus) { - if (nvp->s == IPS_OK && captureInProgress == false) - QTimer::singleShot(FocusSettleTime->value() * 1000, this, &Ekos::Focus::capture); - else if (nvp->s == IPS_ALERT) - { - appendLogText(i18n("Focuser error, check INDI panel.")); - abort(); - setAutoFocusResult(false); - } + autoFocusProcessPositionChange(nvp->s); } else if (nvp->s == IPS_ALERT) appendLogText(i18n("Focuser error, check INDI panel.")); diff --git a/kstars/ekos/focus/focusalgorithms.cpp b/kstars/ekos/focus/focusalgorithms.cpp --- a/kstars/ekos/focus/focusalgorithms.cpp +++ b/kstars/ekos/focus/focusalgorithms.cpp @@ -114,16 +114,15 @@ { const int position = params.currentPosition; int start, end; - int halfMaxTravel = params.maxTravel / 2; // If the bounds allow, set the focus to half-travel above the current position // and sample focusing in down-to half-travel below the current position. - if (position + halfMaxTravel <= maxPositionLimit && position - halfMaxTravel >= minPositionLimit) + if (position + params.maxTravel <= maxPositionLimit && position - params.maxTravel >= minPositionLimit) { - start = position + halfMaxTravel; - end = position - halfMaxTravel; + start = position + params.maxTravel; + end = position - params.maxTravel; } - else if (position + halfMaxTravel > maxPositionLimit) + else if (position + params.maxTravel > maxPositionLimit) { // If the above hits the focus-out bound, start from the highest focus position possible // and sample down the travel amount.