Skip to content

Commit 6c99d6c

Browse files
committed
fix EQ alignment drift by encoding encoder RA instead of HA in sync/goto TDVs
1 parent bb4ff9e commit 6c99d6c

1 file changed

Lines changed: 38 additions & 21 deletions

File tree

drivers/telescope/telescope_simulator.cpp

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,13 @@ void ScopeSim::updateCurrentCoordsFromAxes()
310310
}
311311
else
312312
{
313-
INDI::IEquatorialCoordinates haCoords{ instHA.Hours(), instDec.Degrees() };
313+
// Encode the current encoder RA (= LST − HA), not HA, so the TDV
314+
// fed into TransformTelescopeToCelestial uses the same time-stable
315+
// coordinate that Sync() now stores in the alignment database.
316+
double encoderRA = (alignment.lst() - instHA).Hours();
317+
INDI::IEquatorialCoordinates raCoords{ encoderRA, instDec.Degrees() };
314318
TelescopeDirectionVector tdv =
315-
TelescopeDirectionVectorFromLocalHourAngleDeclination(haCoords);
319+
TelescopeDirectionVectorFromEquatorialCoordinates(raCoords);
316320
corrected = TransformTelescopeToCelestial(tdv, corrRA, corrDec);
317321
}
318322
if (corrected)
@@ -334,7 +338,7 @@ void ScopeSim::setTargetFromAxisPosition(Angle primary, Angle secondary)
334338
bool ScopeSim::ReadScopeStatus()
335339
{
336340
if (m_MountType == Alignment::MOUNT_TYPE::ALTAZ && TrackState == SCOPE_TRACKING &&
337-
TrackModeSP.findOnSwitchIndex() != TRACK_CUSTOM)
341+
TrackModeSP.findOnSwitchIndex() != TRACK_CUSTOM)
338342
{
339343
double dt = getCurrentPollingPeriod() / 1000.0;
340344
double JDnow = ln_get_julian_from_sys();
@@ -351,7 +355,7 @@ bool ScopeSim::ReadScopeStatus()
351355
else if (trackMode == TRACK_LUNAR)
352356
raDriftHrsPerSec = (TRACKRATE_SIDEREAL - TRACKRATE_LUNAR) / 3600.0 / 15.0;
353357

354-
auto getAltAz = [&](double JD, INDI::IHorizontalCoordinates &coords)
358+
auto getAltAz = [&](double JD, INDI::IHorizontalCoordinates & coords)
355359
{
356360
double dtFromNow = (JD - JDnow) * 86400.0;
357361
INDI::IEquatorialCoordinates eq { m_targetRA + raDriftHrsPerSec * dtFromNow, m_targetDEC };
@@ -379,10 +383,14 @@ bool ScopeSim::ReadScopeStatus()
379383

380384
// Unwrap azimuth around the center point to avoid 360/0 discontinuity
381385
// Angle(x).Degrees() normalizes x to (-180, +180], same as the private Angle::range()
382-
auto range180 = [](double x) { return Angle(x).Degrees(); };
386+
auto range180 = [](double x)
387+
{
388+
return Angle(x).Degrees();
389+
};
383390
double pAz[3] = { m_TrackingWindowCoords[0].azimuth,
384391
m_TrackingWindowCoords[1].azimuth,
385-
m_TrackingWindowCoords[2].azimuth };
392+
m_TrackingWindowCoords[2].azimuth
393+
};
386394
pAz[0] = pAz[1] + range180(pAz[0] - pAz[1]);
387395
pAz[2] = pAz[1] + range180(pAz[2] - pAz[1]);
388396

@@ -537,8 +545,9 @@ bool ScopeSim::Goto(double r, double d)
537545
// ALTAZ mounts: the math plugin uses Az/Alt encoding. Decode via SkyToTelescopeAltAz
538546
// and set axis slew targets directly in Az/Alt space.
539547
//
540-
// EQ mounts: the math plugin uses HA/Dec encoding. Decode via LocalHourAngle... and
541-
// convert back through instrumentHaDecToMount inside StartSlew.
548+
// EQ mounts: the math plugin uses RA/Dec encoding. Decode via
549+
// EquatorialCoordinatesFromTelescopeDirectionVector to obtain encoder RA directly,
550+
// then pass it to StartSlew which converts it to HA for the axis motor.
542551
if (GetAlignmentDatabase().size() >= 1 && m_MountType == Alignment::MOUNT_TYPE::ALTAZ)
543552
{
544553
double instrumentAlt, instrumentAz_INDI;
@@ -564,10 +573,12 @@ bool ScopeSim::Goto(double r, double d)
564573
TelescopeDirectionVector tdv;
565574
if (TransformCelestialToTelescope(r, d, 0.0, tdv))
566575
{
567-
INDI::IEquatorialCoordinates haCoords;
568-
LocalHourAngleDeclinationFromTelescopeDirectionVector(tdv, haCoords);
569-
targetRA = (alignment.lst() - Angle(haCoords.rightascension, Angle::HOURS)).Hours();
570-
targetDec = haCoords.declination;
576+
// The TDV now encodes encoder RA (time-stable), so decode with
577+
// EquatorialCoordinatesFromTelescopeDirectionVector — no LST conversion needed.
578+
INDI::IEquatorialCoordinates raCoords;
579+
EquatorialCoordinatesFromTelescopeDirectionVector(tdv, raCoords);
580+
targetRA = raCoords.rightascension;
581+
targetDec = raCoords.declination;
571582
}
572583
}
573584

@@ -579,9 +590,10 @@ bool ScopeSim::Sync(double ra, double dec)
579590
{
580591
// The INDI alignment math plugins encode telescope directions as:
581592
// - ALTAZ (ZENITH) mounts: Az/Alt via TelescopeDirectionVectorFromAltitudeAzimuth
582-
// - EQ mounts: HA/Dec via TelescopeDirectionVectorFromLocalHourAngleDeclination
583-
// Using the wrong encoding causes the correction to be applied in the wrong coordinate
584-
// system, which after Goto's instrumentHaDecToMount re-rotation produces garbage altitudes.
593+
// - EQ mounts: RA/Dec via TelescopeDirectionVectorFromEquatorialCoordinates
594+
// Using encoder RA (= LST − HA, time-stable during sidereal tracking) ensures the
595+
// stored offset sky_RA − encoder_RA remains constant across iterations, so successive
596+
// sync+goto cycles converge rather than diverging.
585597

586598
if (m_MountType == Alignment::MOUNT_TYPE::ALTAZ)
587599
{
@@ -591,14 +603,19 @@ bool ScopeSim::Sync(double ra, double dec)
591603
}
592604
else
593605
{
594-
// EQ mounts: encode the raw encoder HA/Dec as the telescope direction.
606+
// EQ mounts: encode the raw encoder RA/Dec as the telescope direction.
607+
// Use RA (= LST − HA), not HA, so the stored offset is time-stable.
608+
// HA changes at the sidereal rate even during tracking; encoder RA does not.
609+
// Storing HA causes the correction to drift by ~15 arcsec/s, accumulating
610+
// error over successive alignment iterations.
595611
Angle instHA, instDec;
596612
alignment.mountToInstrumentHaDec(axisPrimary.position, axisSecondary.position,
597613
&instHA, &instDec);
598614

599-
INDI::IEquatorialCoordinates haCoords{ instHA.Hours(), instDec.Degrees() };
615+
double encoderRA = (alignment.lst() - instHA).Hours();
616+
INDI::IEquatorialCoordinates raCoords{ encoderRA, instDec.Degrees() };
600617
TelescopeDirectionVector tdv =
601-
TelescopeDirectionVectorFromLocalHourAngleDeclination(haCoords);
618+
TelescopeDirectionVectorFromEquatorialCoordinates(raCoords);
602619

603620
AlignmentDatabaseEntry entry;
604621
entry.ObservationJulianDate = ln_get_julian_from_sys();
@@ -799,7 +816,7 @@ bool ScopeSim::ISNewSwitch(const char *dev, const char *name, ISState *states, c
799816
// Use a full saveConfig() — the selective form (saveConfig(true, name)) only
800817
// patches the named property in the existing XML and skips CURRENT_MATH_PLUGIN.
801818
if (strcmp(name, "ALIGNMENT_SUBSYSTEM_MATH_PLUGINS") == 0 ||
802-
strcmp(name, "ALIGNMENT_SUBSYSTEM_ACTIVE") == 0)
819+
strcmp(name, "ALIGNMENT_SUBSYSTEM_ACTIVE") == 0)
803820
saveConfig();
804821

805822
// Nobody has claimed this, so pass it over
@@ -1209,8 +1226,8 @@ bool ScopeSim::updateMountAndPierSide()
12091226
// Without this, the math plugin always fits an ALTAZ model regardless of mount type.
12101227
SetApproximateMountAlignmentFromMountType(
12111228
alignment.mountType == Alignment::MOUNT_TYPE::ALTAZ
1212-
? INDI::AlignmentSubsystem::MathPluginManagement::ALTAZ
1213-
: INDI::AlignmentSubsystem::MathPluginManagement::EQUATORIAL);
1229+
? INDI::AlignmentSubsystem::MathPluginManagement::ALTAZ
1230+
: INDI::AlignmentSubsystem::MathPluginManagement::EQUATORIAL);
12141231

12151232
// Set the park data type appropriate for the mount geometry.
12161233
if (mountType == static_cast<int>(Alignment::MOUNT_TYPE::ALTAZ))

0 commit comments

Comments
 (0)