29#include <libnova/julian_day.h>
30#include <libnova/sidereal_time.h>
34namespace AlignmentSubsystem
37#ifndef NO_PLUGIN_HOOKS
42 return new SPKMathPlugin;
52 return "SPK Math Plugin";
60 memset(&m_Obs, 0,
sizeof(m_Obs));
61 memset(&m_PM, 0,
sizeof(m_PM));
62 memset(&m_Ast, 0,
sizeof(m_Ast));
63 memset(&m_Opt, 0,
sizeof(m_Opt));
64 memset(&m_PO, 0,
sizeof(m_PO));
69 memset(m_NE_A, 0,
sizeof(m_NE_A));
70 memset(m_NE_v, 0,
sizeof(m_NE_v));
85 if (!UpdateObsConfig())
return true;
88 int n =
static_cast<int>(syncPoints.size());
89 char mountChar = (m_Obs.
mount ==
ALTAZ) ?
'A' :
'E';
91 if (n < 1)
return true;
93 ASSDEBUGF(
"SPK Initialise: n=%d mount=%c NE_valid=%d NE_n=%d",
94 n, mountChar, m_NE_valid, m_NE_n);
100 mountChar == m_NE_mountChar)
102 ASSDEBUGF(
"SPK Initialise: hot path — incremental NE update for point %d", n);
103 double oblon, oblat, rdem, pdem;
104 if (ExtractObsRow(syncPoints.back(), oblon, oblat, rdem, pdem) &&
105 AccumulateObsRow(oblon, oblat, rdem, pdem))
108 if (SolveNormalEquations(pmv))
111 ParsePmfitCoefficients(pmv, 6);
116 ASSDEBUG(
"SPK hot path failed - falling through to cold path");
121 std::vector<double> obsData = BuildObservationData(syncPoints, nt);
123 double pmv[6] = {}, pms[6] = {}, skysig = 0;
124 int js =
Pmfit(m_Obs.
slat, mountChar, n, obsData.data(), nt, pmv, pms, &skysig);
127 ASSDEBUGF(
"SPK Pmfit failed: js=%d", js);
132 ParsePmfitCoefficients(pmv, nt);
138 m_NE_mountChar = mountChar;
139 memset(m_NE_A, 0,
sizeof(m_NE_A));
140 memset(m_NE_v, 0,
sizeof(m_NE_v));
143 for (
const auto &point : syncPoints)
145 double oblon, oblat, rdem, pdem;
146 if (!ExtractObsRow(point, oblon, oblat, rdem, pdem) ||
147 !AccumulateObsRow(oblon, oblat, rdem, pdem))
153 m_NE_n = m_NE_valid ? n : 0;
168 ln_get_julian_from_sys() + JulianOffset,
169 ApparentTelescopeDirectionVector);
176 if (!UpdateObsConfig())
return false;
177 UpdateAstrometry(JulianDate);
190 double tara, tare, tarr, tarp, soln[5];
196 int status =
spkVtel(
AXES, &m_Obs, &m_Opt, &m_PM, &m_Ast, &ax3, &tar, &m_PO,
197 &tara, &tare, &tarr, &tarp, soln);
203 status =
spkVtel(
AXES, &m_Obs, &m_Opt, &m_PM, &m_Ast, &ax3, &tar, &m_PO,
204 &tara, &tare, &tarr, &tarp, soln);
209 double roll = soln[0];
210 double pitch = soln[1];
211 ApparentTelescopeDirectionVector = RollPitchToDirectionVector(roll, pitch);
214 ASSDEBUGF(
"SPK C->T failed: RA=%.4fh Dec=%.4fdeg status=%d", RightAscension, Declination, status);
219 double &RightAscension,
double &Declination,
double JulianOffset)
222 ln_get_julian_from_sys() + JulianOffset);
226 double &RightAscension,
double &Declination,
double JulianDate)
228 if (!UpdateObsConfig())
return false;
229 UpdateAstrometry(JulianDate);
233 DirectionVectorToRollPitch(ApparentTelescopeDirectionVector, roll, pitch);
244 double tara, tare, tarr, tarp, soln[5];
245 int status =
spkVtel(
TARG, &m_Obs, &m_Opt, &m_PM, &m_Ast, &ax3, &tar, &m_PO,
246 &tara, &tare, &tarr, &tarp, soln);
253 ASSDEBUGF(
"SPK T->C: roll=%.4fdeg pitch=%.4fdeg => RA=%.4fh Dec=%.4fdeg",
257 ASSDEBUGF(
"SPK T->C failed: roll=%.4fdeg pitch=%.4fdeg status=%d",
262bool SPKMathPlugin::UpdateObsConfig()
275void SPKMathPlugin::UpdateAstrometry(
double JD)
279 ln_get_date(JD, &date);
283 utc.
mo = date.months;
286 utc.
mi = date.minutes;
287 utc.
sec = date.seconds;
291 double gmst_hrs = ln_get_apparent_sidereal_time(JD);
299 spkAIR air = {0.0, 10.0, 0.5};
302 memset(&m_Ast, 0,
sizeof(m_Ast));
303 int astrStatus =
spkAstr(1, utc, 0.0, &eop, &m_Obs, &air, &m_Opt, &m_Ast);
306 ASSDEBUGF(
"SPK UpdateAstrometry: spkAstr failed (status=%d), transforms will be inaccurate", astrStatus);
320bool SPKMathPlugin::ExtractObsRow(
const AlignmentDatabaseEntry &point,
321 double &oblon,
double &oblat,
322 double &rdem,
double &pdem)
324 UpdateAstrometry(point.ObservationJulianDate);
326 double gmst_hrs = ln_get_apparent_sidereal_time(point.ObservationJulianDate);
329 double obslat_ =
DEG_TO_RAD(point.Declination);
332 DirectionVectorToRollPitch(point.TelescopeDirection, roll, pitch);
339 double az_cel, el_cel;
340 iauHd2ae(obslon_ha, obslat_, m_Obs.
slat, &az_cel, &el_cel);
352 ASSDEBUGF(
"SPK ExtractObsRow: RA=%.4fh Dec=%.4fdeg oblon=%.4fdeg oblat=%.4fdeg rdem=%.4fdeg pdem=%.4fdeg residual=(%.1f\" %.1f\")",
353 point.RightAscension, point.Declination,
361bool SPKMathPlugin::AccumulateObsRow(
double oblon,
double oblat,
362 double rdem,
double pdem)
368 char mountChar = (m_Obs.
mount ==
ALTAZ) ?
'A' :
'E';
369 if (
Bfun(6, m_Obs.
slat, mountChar, oblon, oblat, bf) != 0)
372 double xi =
iauAnpm(oblon - rdem) * cos(oblat);
373 double eta =
iauAnpm(oblat - pdem);
375 for (
int i = 0; i < 6; i++)
377 double bfi0 = bf[i*2 + 0];
378 double bfi1 = bf[i*2 + 1];
379 m_NE_v[i] += bfi0 * xi + bfi1 * eta;
380 for (
int j = 0; j < 6; j++)
381 m_NE_A[i*6 + j] += bfi0 * bf[j*2 + 0] + bfi1 * bf[j*2 + 1];
386bool SPKMathPlugin::SolveNormalEquations(
double pmv[6])
392 memcpy(Acopy, m_NE_A,
sizeof(m_NE_A));
393 memcpy(vcopy, m_NE_v,
sizeof(m_NE_v));
395 if (
Simeqn(6, Acopy, vcopy) != 0)
398 memcpy(pmv, vcopy, 6 *
sizeof(
double));
409 if (syncPoints.size() < 3) outTermCount = 2;
410 else if (syncPoints.size() < 5) outTermCount = 4;
411 else if (syncPoints.size() < 6) outTermCount = 5;
414 std::vector<double> obsData;
415 for (
const auto &point : syncPoints)
417 UpdateAstrometry(point.ObservationJulianDate);
419 double gmst_hrs = ln_get_apparent_sidereal_time(point.ObservationJulianDate);
423 double obslat =
DEG_TO_RAD(point.Declination);
426 DirectionVectorToRollPitch(point.TelescopeDirection, roll, pitch);
430 double az_cel, el_cel;
431 iauHd2ae(obslon_ha, obslat, m_Obs.
slat, &az_cel, &el_cel);
432 obsData.push_back(az_cel);
433 obsData.push_back(el_cel);
439 obsData.push_back(obslon_ha);
440 obsData.push_back(obslat);
441 obsData.push_back(-roll);
443 obsData.push_back(pitch);
448void SPKMathPlugin::ParsePmfitCoefficients(
const double pmv[6],
int terms)
450 memset(&m_PM, 0,
sizeof(m_PM));
456 if (terms >= 1) m_PM.
pia = pmv[0];
457 if (terms >= 2) m_PM.
pib = pmv[1];
458 if (terms >= 3) m_PM.
pan = pmv[2];
459 if (terms >= 4) m_PM.
paw = pmv[3];
460 if (terms >= 5) m_PM.
pca = pmv[4];
461 if (terms >= 6) m_PM.
pvd = pmv[5];
467 if (terms >= 1) m_PM.
pia = -pmv[0];
468 if (terms >= 2) m_PM.
pib = pmv[1];
469 if (terms >= 3) m_PM.
paw = -pmv[3];
470 if (terms >= 4) m_PM.
pan = pmv[2];
471 if (terms >= 5) m_PM.
pca = -pmv[4];
472 if (terms >= 6) m_PM.
pvd = pmv[5];
476TelescopeDirectionVector SPKMathPlugin::RollPitchToDirectionVector(
double roll,
double pitch)
495void SPKMathPlugin::DirectionVectorToRollPitch(
const TelescopeDirectionVector &v,
double &roll,
double &pitch)
#define ASSDEBUGF(msg,...)
int spkAstr(int jfull, spkUTC utc, double dt, spkEOP *eop, spkOBS *obs, spkAIR *air, spkOPT *opt, spkAST *ast)
This class provides the driver side API to the in memory alignment database.
std::vector< AlignmentDatabaseEntry > AlignmentDatabaseType
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
bool GetDatabaseReferencePosition(IGeographicCoordinates &Position)
Get the database reference position.
InMemoryDatabase * pInMemoryDatabase
virtual bool Initialise(InMemoryDatabase *pInMemoryDatabase)
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
MountAlignment_t ApproximateMountAlignment
Describe the approximate alignment of the mount. This information is normally used in a one star alig...
virtual bool TransformTelescopeToCelestialJD(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianDate) override
Get the true celestial coordinates for the supplied telescope pointing direction.
virtual bool TransformCelestialToTelescopeJD(double RightAscension, double Declination, double JulianDate, TelescopeDirectionVector &ApparentTelescopeDirectionVector) override
Get the alignment corrected telescope pointing direction for the supplied celestial coordinates.
virtual bool TransformTelescopeToCelestial(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianOffset=0) override
Get the true celestial coordinates for the supplied telescope pointing direction.
virtual bool TransformCelestialToTelescope(const double RightAscension, const double Declination, double JulianOffset, TelescopeDirectionVector &ApparentTelescopeDirectionVector) override
Get the alignment corrected telescope pointing direction for the supplied celestial coordinates.
virtual bool Initialise(InMemoryDatabase *pInMemoryDatabase) override
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
const TelescopeDirectionVector TelescopeDirectionVectorFromAltitudeAzimuth(INDI::IHorizontalCoordinates HorizontalCoordinates)
Calculates a normalised direction vector from the supplied altitude and azimuth.
void EquatorialCoordinatesFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IEquatorialCoordinates &EquatorialCoordinates)
Calculates equatorial coordinates from the supplied telescope direction vector and declination.
const TelescopeDirectionVector TelescopeDirectionVectorFromEquatorialCoordinates(INDI::IEquatorialCoordinates EquatorialCoordinates)
Calculates a telescope direction vector from the supplied equatorial coordinates.
void AltitudeAzimuthFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IHorizontalCoordinates &HorizontalCoordinates)
Calculates an altitude and azimuth from the supplied normalised direction vector and declination.
double range24(double r)
range24 Limits a number to be between 0-24 range.
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Implementations for common driver routines.
void Destroy(DummyMathPlugin *pPlugin)
DummyMathPlugin * Create()
const char * GetDisplayName()
Namespace to encapsulate INDI client, drivers, and mediator classes.
int Simeqn(int, double *, double *)
int Pmfit(double phi, char mount, int n, double *obs, int nt, double *pm, double *sigmas, double *skysig)
int Bfun(int, double, char, double, double, double *)
int spkVtel(spkVTS isoln, spkOBS *obs, spkOPT *opt, spkPM *pm, spkAST *ast, spkAX3 *ax3, spkTAR *tar, spkPO *po, double *tara, double *tare, double *tarr, double *tarp, double soln[5])
Holds a normalised direction vector (direction cosines)