[education/kstars] kstars/ekos: Rename to m_RotatorParityRetried to avoid confusion with rotator own reverse functionality

Jasem Mutlaq <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 5d0543c975045a7661016825abe5315ab6832a7e by Jasem Mutlaq.
Committed on 30/07/2026 at 13:32.
Pushed by mutlaqja into branch 'master'.

Rename to m_RotatorParityRetried to avoid confusion with rotator own reverse functionality

M  +4    -2    kstars/ekos/align/align.h
M  +12   -3    kstars/ekos/align/align_goto.cpp
M  +6    -6    kstars/ekos/align/align_solver.cpp
M  +5    -4    kstars/ekos/auxiliary/rotatorutils.cpp

https://invent.kde.org/education/kstars/-/commit/5d0543c975045a7661016825abe5315ab6832a7e

diff --git a/kstars/ekos/align/align.h b/kstars/ekos/align/align.h
index 56ceb6e896..0211118f2f 100644
--- a/kstars/ekos/align/align.h
+++ b/kstars/ekos/align/align.h
@@ -823,8 +823,10 @@ class Align : public QWidget, public Ui::Align
         ISD::Mount::PierSide m_PAValidPierSide { ISD::Mount::PIER_UNKNOWN };
         /// Previous PA error before the last rotation command (to detect wrong direction)
         double m_PreviousPAError { -1 };
-        /// Auto-reverse rotator direction attempted for current alignment
-        bool m_RotatorAutoReversed { false };
+        /// Whether a software parity-correction retry has already been attempted this alignment
+        /// (limits to one retry, gates persisting the correction, and gates the flip-tolerance
+        /// heuristic in checkIfRotationRequired() from swallowing an in-flight retry).
+        bool m_RotatorParityRetried { false };
         /// Solver iterations count
         uint8_t solverIterations { 0 };
         /// Was solving with scale off used?
diff --git a/kstars/ekos/align/align_goto.cpp b/kstars/ekos/align/align_goto.cpp
index eb213ffd0a..36b0561bc0 100644
--- a/kstars/ekos/align/align_goto.cpp
+++ b/kstars/ekos/align/align_goto.cpp
@@ -60,7 +60,7 @@ void Align::stop(Ekos::AlignState mode)
     m_CaptureTimeoutCounter = 0;
     m_SlewErrorCounter = 0;
     m_PreviousPAError = -1;
-    m_RotatorAutoReversed = false;
+    m_RotatorParityRetried = false;
     m_RemoteAlignTimer.stop();
 
     disconnect(m_Camera, &ISD::Camera::newImage, this, &Ekos::Align::processData);
@@ -333,6 +333,7 @@ void Align::updateProperty(INDI::Property prop)
             appendLogText(i18n("Rotator error detected. Aborting alignment."));
             m_RotatorTimer.invalidate();
             m_PreviousPAError = -1;
+            m_RotatorParityRetried = false;
             setState(ALIGN_FAILED);
             Q_EMIT newStatus(state);
             solveB->setEnabled(true);
@@ -354,7 +355,7 @@ void Align::updateProperty(INDI::Property prop)
                 // NOTE: this "reached" signal only means the raw encoder matches the value
                 // that was *commanded* (computed from the current offset/parity model) — it is
                 // not confirmed by an actual plate solve. Deliberately do NOT clear
-                // m_PreviousPAError/m_RotatorAutoReversed here: doing so would erase the
+                // m_PreviousPAError/m_RotatorParityRetried here: doing so would erase the
                 // baseline the wrong-direction check in align_solver.cpp needs to compare
                 // against once the next (real, solve-verified) measurement comes in. Both
                 // trackers are only cleared once that check has actually run and reached a
@@ -387,6 +388,7 @@ void Align::updateProperty(INDI::Property prop)
                     {
                         appendLogText(i18n("Rotator failed to arrive at the requested position angle (Deviation %1 arcmin).", diff));
                         m_PreviousPAError = -1;
+                        m_RotatorParityRetried = false;
                         setState(ALIGN_FAILED);
                         Q_EMIT newStatus(state);
                         solveB->setEnabled(true);
@@ -637,7 +639,13 @@ bool Align::checkIfRotationRequired()
                     m_TargetPositionAngle = RotatorUtils::Instance()->calcCameraAngle(sRawAngle, true);
                     RotatorUtils::Instance()->setImagePierside(ISD::Mount::PIER_UNKNOWN); // ... once!
                 }
-                else if (Options::astrometryFlipRotationAllowed())
+                // Skip the flip-tolerance guess while a parity-correction retry is in flight
+                // (m_RotatorParityRetried): a wrong-direction rotator produces the exact same
+                // ~180°-off symptom as a genuine optical flip, but here we've already
+                // determined via a more reliable before/after comparison (align_solver.cpp)
+                // that it's a direction problem, not a flip to preserve. Falling through to
+                // the rotation command below is what the retry actually needs to happen.
+                else if (Options::astrometryFlipRotationAllowed() && !m_RotatorParityRetried)
                 {
                     double paDiff = std::abs(KSUtils::rangePA(currentRotatorPA - m_TargetPositionAngle));
                     if (std::abs(paDiff - 180.0) < ROTATOR_FLIP_TOLERANCE)
@@ -719,6 +727,7 @@ void Align::checkRotatorTimeout()
                            Options::captureOperationsTimeout()));
         m_RotatorTimer.invalidate();
         m_PreviousPAError = -1;
+        m_RotatorParityRetried = false;
         setState(ALIGN_FAILED);
         Q_EMIT newStatus(state);
         solveB->setEnabled(true);
diff --git a/kstars/ekos/align/align_solver.cpp b/kstars/ekos/align/align_solver.cpp
index 049109a9cc..a9543c99a6 100644
--- a/kstars/ekos/align/align_solver.cpp
+++ b/kstars/ekos/align/align_solver.cpp
@@ -905,14 +905,14 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
                                                << "Previous PA error:" << m_PreviousPAError
                                                << "New PA error:" << newPAError
                                                << "Raw angle:" << sRawAngle
-                                               << "Auto-reversed:" << m_RotatorAutoReversed;
+                                               << "Parity retried:" << m_RotatorParityRetried;
                     if (newPAError > m_PreviousPAError + 0.5)
                     {
                         // First wrong-direction detection: try correcting via the software-only
                         // per-train parity flag. We never touch the driver's ROTATOR_REVERSE
                         // switch — that would depend on trusting this specific driver's
                         // implementation of it.
-                        if (!m_RotatorAutoReversed)
+                        if (!m_RotatorParityRetried)
                         {
                             appendLogText(i18n("Rotator is moving in the wrong direction. "
                                                "Automatically compensating rotator direction and retrying..."));
@@ -929,7 +929,7 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
                             double correctedOffset = RotatorUtils::Instance()->calcOffsetAngle(sRawAngle, solverPA);
                             RotatorUtils::Instance()->updateOffset(correctedOffset);
 
-                            m_RotatorAutoReversed = true;
+                            m_RotatorParityRetried = true;
                             m_PreviousPAError = -1;
                             // Re-issue the rotation command with the corrected parity
                             checkIfRotationRequired();
@@ -948,7 +948,7 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
                         RotatorUtils::Instance()->revertParity();
                         m_TargetPositionAngle = std::numeric_limits<double>::quiet_NaN();
                         m_PreviousPAError = -1;
-                        m_RotatorAutoReversed = false;
+                        m_RotatorParityRetried = false;
                         setState(ALIGN_FAILED);
                         emit newStatus(state);
                         solveB->setEnabled(true);
@@ -958,12 +958,12 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
                     // PA error did not increase — rotation was in the correct direction.
                     // If this confirms a parity trial, persist it so this train never has to
                     // rediscover its direction again.
-                    if (m_RotatorAutoReversed)
+                    if (m_RotatorParityRetried)
                         RotatorUtils::Instance()->commitParity();
                     // Reset error tracking since the rotation succeeded.
                     qCDebug(KSTARS_EKOS_ALIGN) << "PA error decreased or unchanged — resetting previous error tracker.";
                     m_PreviousPAError = -1;
-                    m_RotatorAutoReversed = false;
+                    m_RotatorParityRetried = false;
                 }
 
                 appendLogText(i18n("Camera position angle is %1 degrees.", solverPA));
diff --git a/kstars/ekos/auxiliary/rotatorutils.cpp b/kstars/ekos/auxiliary/rotatorutils.cpp
index c5b0e3499a..71da885d55 100644
--- a/kstars/ekos/auxiliary/rotatorutils.cpp
+++ b/kstars/ekos/auxiliary/rotatorutils.cpp
@@ -193,10 +193,11 @@ bool RotatorUtils::checkImageFlip()
 
 double RotatorUtils::DiffPA(double diff)
 {
-    if (diff > 180)
-        return (360 - diff);
-    else
-        return diff;
+    // Wrap into (-180,180]. The previous version only unwrapped diff > 180 (e.g. 350 -> 10)
+    // but left diff < -180 untouched, so a raw difference like -340 (current/target PA
+    // straddling the +/-180 boundary, genuinely only 20 degrees apart) was returned as-is,
+    // making callers that do abs(DiffPA(...)) see a bogus ~340 degree deviation instead of 20.
+    return KSUtils::rangePA(diff);
 }
 
 void RotatorUtils::initTimeFrame(const double EndAngle)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.