[education/kstars] kstars/ekos/align: Fix false-positive rotator wrong-direction detection and add PA to solver logs
Jasem Mutlaq <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 55170718f79dd70deb64320e8153a28c6d0fc848 by Jasem Mutlaq.
Committed on 24/07/2026 at 09:27.
Pushed by mutlaqja into branch 'master'.
Fix false-positive rotator wrong-direction detection and add PA to solver logs
Reset m_PreviousPAError at key state transitions to prevent the auto-reverse logic from falsely detecting wrong-direction rotation. Previously, a stale PA error value persisted across successful rotations and mount slews, causing the condition `newPAError > m_PreviousPAError + 0.5` to fire spuriously when the mount slewed to new coordinates (which naturally changes the PA). Now m_PreviousPAError is cleared when:
(a) the rotator successfully reaches the target PA,
(b) the mount begins slewing, and
(c) the PA error decreases after a solve.
Also added the computed position angle (PA) to the solver results info log line and enhanced the rotator direction check debug logging with all decision inputs for easier future diagnostics.
M +13 -0 kstars/ekos/align/align_goto.cpp
M +26 -15 kstars/ekos/align/align_solver.cpp
https://invent.kde.org/education/kstars/-/commit/55170718f79dd70deb64320e8153a28c6d0fc848
diff --git a/kstars/ekos/align/align_goto.cpp b/kstars/ekos/align/align_goto.cpp
index acdbadb8e3..57bafeb2ce 100644
--- a/kstars/ekos/align/align_goto.cpp
+++ b/kstars/ekos/align/align_goto.cpp
@@ -239,6 +239,14 @@ void Align::updateProperty(INDI::Property prop)
case IPS_BUSY:
{
m_wasSlewStarted = true;
+ // Mount is slewing — the camera PA will change relative to the sky,
+ // so any previously stored PA error is no longer valid for comparing
+ // rotation direction. Reset to avoid false-positive wrong-direction detection.
+ if (m_PreviousPAError >= 0)
+ {
+ qCDebug(KSTARS_EKOS_ALIGN) << "Mount slew started. Clearing previous PA error tracker.";
+ m_PreviousPAError = -1;
+ }
handleMountMotion();
}
break;
@@ -304,6 +312,11 @@ void Align::updateProperty(INDI::Property prop)
if (diff <= Options::astrometryRotatorThreshold())
{
appendLogText(i18n("Rotator reached camera position angle."));
+ // Rotation succeeded — clear the previous PA error tracker
+ // so it doesn't cause false-positive wrong-direction detection later.
+ m_PreviousPAError = -1;
+ m_RotatorAutoReversed = false;
+ qCDebug(KSTARS_EKOS_ALIGN) << "Rotator reached target PA. Clearing previous PA error tracker.";
if (m_RotateBeforeSolve)
{
// Rotate-first optimization: skip stale sync, go directly to capture
diff --git a/kstars/ekos/align/align_solver.cpp b/kstars/ekos/align/align_solver.cpp
index f6592b69ca..c8d6a4d4b4 100644
--- a/kstars/ekos/align/align_solver.cpp
+++ b/kstars/ekos/align/align_solver.cpp
@@ -479,7 +479,7 @@ void Align::setCaptureComplete()
QDir dir;
QDateTime now = KStarsData::Instance()->lt();
QString path = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("align/" +
- now.toString("yyyy-MM-dd"));
+ now.toString("yyyy-MM-dd"));
dir.mkpath(path);
QString name = "align_frame_" + now.toString("HH-mm-ss") + ".fits";
QString filename = path + QStringLiteral("/") + name;
@@ -534,7 +534,7 @@ void Align::startSolving()
{
appendLogText(
i18n("No index files were found on your system in the specified index file directories."
- "Please download some index files or add the correct directory to the list."));
+ "Please download some index files or add the correct directory to the list."));
KConfigDialog * alignSettings = KConfigDialog::exists("alignsettings");
if(alignSettings && m_IndexFilesPage)
{
@@ -568,7 +568,7 @@ void Align::startSolving()
if(type == SSolver::SOLVER_LOCALASTROMETRY || type == SSolver::SOLVER_ASTAP || type == SSolver::SOLVER_WATNEYASTROMETRY)
{
QString filename = QDir::tempPath() + QString("/solver%1.fits").arg(QUuid::createUuid().toString().remove(
- QRegularExpression("[-{}]")));
+ QRegularExpression("[-{}]")));
m_AlignView->saveImage(filename);
filenameToUse = filename;
}
@@ -576,7 +576,7 @@ void Align::startSolving()
if(type == SSolver::SOLVER_ONLINEASTROMETRY )
{
QString filename = QDir::tempPath() + QString("/solver%1.fits").arg(QUuid::createUuid().toString().remove(
- QRegularExpression("[-{}]")));
+ QRegularExpression("[-{}]")));
m_AlignView->saveImage(filename);
filenameToUse = filename;
}
@@ -756,14 +756,6 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
ISD::CameraChip *targetChip = m_Camera->getChip(useGuideHead ? ISD::CameraChip::GUIDE_CCD : ISD::CameraChip::PRIMARY_CCD);
targetChip->getBinning(&binx, &biny);
- if (Options::alignmentLogging())
- {
- QString parityString = eastToTheRight ? "neg" : "pos";
- appendLogText(i18n("Solver RA (%1) DEC (%2) Orientation (%3) Pixel Scale (%4) Parity (%5)", QString::number(ra, 'f', 5),
- QString::number(dec, 'f', 5), QString::number(orientation, 'f', 5),
- QString::number(pixscale, 'f', 5), parityString));
- }
-
if (!m_SolveFromFile &&
(isEqual(m_FOVWidth, 0) || m_EffectiveFOVPending || std::abs(pixscale - m_FOVPixelScale) > 0.005) &&
pixscale > 0)
@@ -790,6 +782,17 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
}
double solverPA = KSUtils::rotationToPositionAngle(orientation);
+
+ if (Options::alignmentLogging())
+ {
+ QString parityString = eastToTheRight ? "neg" : "pos";
+ appendLogText(i18n("Solver RA (%1) DEC (%2) Orientation (%3) Pixel Scale (%4) Parity (%5) PA (%6)",
+ QString::number(ra, 'f', 5),
+ QString::number(dec, 'f', 5), QString::number(orientation, 'f', 5),
+ QString::number(pixscale, 'f', 5), parityString,
+ QString::number(solverPA, 'f', 5)));
+ }
+
solverFOV->setCenter(m_AlignCoord);
solverFOV->setPA(solverPA);
solverFOV->setImageDisplay(Options::astrometrySolverOverlay());
@@ -878,7 +881,6 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
{
sRawAngle = absAngle[0].getValue();
double OffsetAngle = RotatorUtils::Instance()->calcOffsetAngle(sRawAngle, solverPA);
- RotatorUtils::Instance()->updateOffset(OffsetAngle);
auto reverseStatus = "Unknown";
auto reverseProperty = m_Rotator->getSwitch("ROTATOR_REVERSE");
if (reverseProperty)
@@ -894,6 +896,12 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
if (std::isnan(m_TargetPositionAngle) == false && m_PreviousPAError >= 0)
{
double newPAError = std::abs(KSUtils::rangePA(solverPA - m_TargetPositionAngle));
+ qCDebug(KSTARS_EKOS_ALIGN) << "Rotator direction check: Target PA:" << m_TargetPositionAngle
+ << "Solver PA:" << solverPA
+ << "Previous PA error:" << m_PreviousPAError
+ << "New PA error:" << newPAError
+ << "Raw angle:" << sRawAngle
+ << "Auto-reversed:" << m_RotatorAutoReversed;
if (newPAError > m_PreviousPAError + 0.5)
{
// First wrong-direction detection: try auto-reversing the rotator
@@ -938,14 +946,17 @@ void Align::solverFinished(double orientation, double ra, double dec, double pix
loadSlewB->setEnabled(true);
return;
}
+ // PA error did not increase — rotation was in the correct direction.
+ // 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;
}
- Q_EMIT newSolverResults(solverPA, ra, dec, pixscale);
- appendLogText(i18n("Camera position angle is %1 degrees.", RotatorUtils::Instance()->calcCameraAngle(sRawAngle, false)));
+ appendLogText(i18n("Camera position angle is %1 degrees.", solverPA));
}
}
+ Q_EMIT newSolverResults(solverPA, ra, dec, pixscale);
}
QJsonObject solution =