2 LX200 - based Omegon
EQ500X Equatorial Mount
4 Copyright (C) 2019 Eric Dejouhanet (eric.dejouhanet@gmail.com)
6 This library is free software;
7 you can redistribute it and / or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation;
11 version 2.1 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT
ANY WARRANTY;
15 without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library;
21 if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
32 #include <libnova/sidereal_time.h>
33 #include <libnova/transform.h>
60 #define MechanicalPoint_DEC_FormatR "+DD:MM:SS"
61 #define MechanicalPoint_DEC_FormatW "+DDD:MM:SS"
62 #define MechanicalPoint_RA_Format "HH:MM:SS"
65 static int const EQ500X_TIMEOUT = 5;
68 static double constexpr ONEDEGREE = 1.0;
69 static double constexpr ARCMINUTE = ONEDEGREE / 60.0;
70 static double constexpr ARCSECOND = ONEDEGREE / 3600.0;
73 static double RA_GRANULARITY = std::lround((15.0 * ARCSECOND) * 3600.0) / 3600.0;
74 static double DEC_GRANULARITY = std::lround((1.0 * ARCSECOND) * 3600.0) / 3600.0;
78 static int MAX_CONVERGENCE_LOOPS = 144;
93 {
":RG#", 0, 1 * ARCSECOND, 0.7 * ARCMINUTE, 100 },
94 {
":RC#", 1, 0.7 * ARCMINUTE, 10 * ARCMINUTE, 200 },
95 {
":RM#", 2, 10 * ARCMINUTE, 5 * ONEDEGREE, 500 },
96 {
":RS#", 3, 5 * ONEDEGREE, 360 * ONEDEGREE, 1000 }
121 LOG_DEBUG(
"Initializing from EQ500X device...");
172 LOG_DEBUG(
"Testing telescope connection using GR...");
176 tcflush(
PortFD, TCIFLUSH);
179 for (
int i = 0; i < 2; i++)
185 LOG_DEBUG(
"Failure. Telescope is not responding to GR/GD!");
191 const struct timespec timeout = {0, 50000000L};
192 nanosleep(&timeout,
nullptr);
197 const struct timespec timeout = {0, 250000000L};
199 nanosleep(&timeout,
nullptr);
201 nanosleep(&timeout,
nullptr);
203 nanosleep(&timeout,
nullptr);
205 nanosleep(&timeout,
nullptr);
207 nanosleep(&timeout,
nullptr);
209 nanosleep(&timeout,
nullptr);
211 nanosleep(&timeout,
nullptr);
213 nanosleep(&timeout,
nullptr);
215 nanosleep(&timeout,
nullptr);
217 nanosleep(&timeout,
nullptr);
221 LOG_DEBUG(
"Connection check successful!");
230 LOGF_INFO(
"Location updated: Longitude (%g) Latitude (%g)", longitude, latitude);
239 double const LST =
getLST();
241 LOGF_INFO(
"Location updated: mount considered parked, synced to LST %gh.", LST);
273 struct timespec clock = {0, 0};
274 clock_gettime(CLOCK_MONOTONIC, &clock);
275 long const now = clock.tv_sec * 1000 +
static_cast <long> (
round(clock.tv_nsec / 1000000.0));
280 if (
nullptr != adjustment)
295 LOGF_DEBUG(
"New mechanical RA/DEC simulated as %lf°/%lf° (%+lf°,%+lf°), stored as %lfh/%lf° = %s/%s",
297 || RAmIncrease) ? rates[adjustment -
adjustments]*delta : 0, (DECmDecrease
322 while (+12 <= HA) HA -= 24;
323 while (HA <= -12) HA += 24;
333 LOGF_DEBUG(
"Mount HA=%lfh pointing %s on %s side", HA,
343 _gotoEngaged =
false;
355 double const ra_delta = currentMechPosition.
RA_degrees_to(targetMechPosition);
356 double const dec_delta = currentMechPosition.
DEC_degrees_to(targetMechPosition);
357 double const abs_ra_delta = std::abs(ra_delta);
358 double const abs_dec_delta = std::abs(dec_delta);
361 if (RA_GRANULARITY <= abs_ra_delta || DEC_GRANULARITY <= abs_dec_delta)
364 struct _adjustment const *ra_adjust =
nullptr, *dec_adjust =
nullptr;
370 assert(
nullptr != ra_adjust);
371 LOGF_DEBUG(
"RA %lf-%lf = %+lf° under %lf° would require adjustment at %s until less than %lf°",
372 targetMechPosition.
RAm() * 15.0, currentMechPosition.
RAm() * 15.0, ra_delta, ra_adjust->
distance, ra_adjust->
slew_rate,
379 assert(
nullptr != dec_adjust);
380 LOGF_DEBUG(
"DEC %lf-%lf = %+lf° under %lf° would require adjustment at %s until less than %lf°",
381 targetMechPosition.
DECm(), currentMechPosition.
DECm(), dec_delta, dec_adjust->distance, dec_adjust->slew_rate,
382 dec_adjust->epsilon);
385 char CmdString[32] = {0};
388 static struct _adjustment const * previous_adjustment =
nullptr;
392 adjustment = ra_adjust < dec_adjust ? dec_adjust : ra_adjust;
395 if (ra_adjust != adjustment)
399 strcat(CmdString,
":Qe#");
404 strcat(CmdString,
":Qw#");
410 if (dec_adjust != adjustment)
414 strcat(CmdString,
":Qn#");
415 DECmDecrease =
false;
419 strcat(CmdString,
":Qs#");
420 DECmIncrease =
false;
425 if (previous_adjustment != adjustment)
428 strcat(CmdString, adjustment->
slew_rate);
431 if (adjustment < previous_adjustment)
432 countdown = MAX_CONVERGENCE_LOOPS;
437 previous_adjustment = adjustment;
443 if (ra_adjust == adjustment)
446 double const ra_epsilon =
std::max(adjustment->
epsilon, RA_GRANULARITY);
449 bool const doDecrease = ra_epsilon <= ra_delta;
450 bool const doIncrease = ra_delta <= -ra_epsilon;
451 assert(!(doDecrease && doIncrease));
454 if (RAmIncrease && (!doDecrease || doIncrease))
456 strcat(CmdString,
":Qe#");
459 if (RAmDecrease && (!doIncrease || doDecrease))
461 strcat(CmdString,
":Qw#");
466 if (doDecrease && !RAmIncrease)
468 strcat(CmdString,
":Me#");
471 if (doIncrease && !RAmDecrease)
473 strcat(CmdString,
":Mw#");
480 if (dec_adjust == adjustment)
483 double const dec_epsilon =
std::max(adjustment->
epsilon, DEC_GRANULARITY);
486 bool const doDecrease = dec_epsilon <= dec_delta;
487 bool const doIncrease = dec_delta <= -dec_epsilon;
488 assert(!(doDecrease && doIncrease));
491 if (DECmIncrease && (!doDecrease || doIncrease))
493 strcat(CmdString,
":Qn#");
494 DECmIncrease =
false;
496 if (DECmDecrease && (!doIncrease || doDecrease))
498 strcat(CmdString,
":Qs#");
499 DECmDecrease =
false;
503 if (doDecrease && !DECmIncrease)
505 strcat(CmdString,
":Mn#");
508 if (doIncrease && !DECmDecrease)
510 strcat(CmdString,
":Ms#");
516 assert(!(RAmIncrease && RAmDecrease) && !(DECmDecrease && DECmIncrease));
519 LOGF_DEBUG(
"Centering (%lf°,%lf°) delta (%lf°,%lf°) moving %c%c%c%c at %s until less than (%lf°,%lf°)",
520 targetMechPosition.
RAm() * 15.0, targetMechPosition.
DECm(), ra_delta, dec_delta, RAmDecrease ?
'W' :
'.',
521 RAmIncrease ?
'E' :
'.', DECmDecrease ?
'N' :
'.', DECmIncrease ?
'S' :
'.', adjustment->
slew_rate,
525 if (CmdString[0] !=
'\0')
530 LOGF_ERROR(
"Error centering (%lf°,%lf°)", targetMechPosition.
RAm() * 15.0, targetMechPosition.
DECm());
542 if (!RAmIncrease && !RAmDecrease && !DECmDecrease && !DECmIncrease)
544 LOGF_INFO(
"Centering delta (%lf,%lf) intermediate adjustment complete (%d loops)", ra_delta, dec_delta,
545 MAX_CONVERGENCE_LOOPS - countdown);
546 adjustment =
nullptr;
551 else if (--countdown <= 0)
553 LOGF_ERROR(
"Failed centering to (%lf,%lf) under loop limit, aborting...", targetMechPosition.
RAm(),
554 targetMechPosition.
DECm());
563 LOG_INFO(
"Slew is complete. Tracking...");
566 adjustment =
nullptr;
576 if (DECmDecrease) DECmDecrease =
false;
577 if (DECmIncrease) DECmIncrease =
false;
578 if (RAmIncrease) RAmIncrease =
false;
579 if (RAmDecrease) RAmDecrease =
false;
580 adjustment =
nullptr;
584 if (ra_changed || dec_changed)
593 adjustment =
nullptr;
607 while (+12 <= HA) HA -= 24;
608 while (HA <= -12) HA += 24;
643 const struct timespec timeout = {0, 100000000L};
644 nanosleep(&timeout,
nullptr);
665 LOGF_INFO(
"Goto target (%lfh,%lf°) HA %lf, quadrant %s",
ra,
dec, HA,
670 countdown = MAX_CONVERGENCE_LOOPS;
682 char RAStr[16] = {0}, DecStr[16] = {0};
686 LOGF_INFO(
"Slewing to JNow RA: %s - DEC: %s", RAStr, DecStr);
701 tcflush(
PortFD, TCIFLUSH);
705 if (!strncmp(
"No name", b,
sizeof(b)))
758 LOG_ERROR(
"Error setting N/S motion direction.");
763 (current_move ==
LX200_NORTH) ?
"North" :
"South");
774 (current_move ==
LX200_NORTH) ?
"North" :
"South");
790 int nbytes_write = 0;
801 int error_type =
tty_read(
PortFD, data, len, EQ500X_TIMEOUT, &nbytes_read);
803 LOGF_DEBUG(
"RES <%.*s> (%d)", nbytes_read, data, error_type);
819 return buf[0] !=
'0';
848 LOGF_DEBUG(
"Mount mechanical DEC reads '%s' as %lf.", b, result.
DECm());
858 LOGF_DEBUG(
"Mount mechanical RA reads '%s' as %lf.", b, result.
RAm());
876 snprintf(CmdString,
sizeof(CmdString),
":Sr%s#:Sd%s#", p.
toStringRA(bufRA,
sizeof(bufRA)), p.
toStringDEC(bufDEC,
878 LOGF_DEBUG(
"Target RA '%f' DEC '%f' converted to '%s'",
static_cast <float> (p.
RAm()),
static_cast <float> (p.
DECm()),
885 if (buf[0] ==
'1' && buf[1] ==
'1')
887 else LOGF_ERROR(
"Failed '%s', mount replied %c%c", CmdString, buf[0], buf[1]);
888 else LOGF_ERROR(
"Failed getting 2-byte reply to '%s'", CmdString);
910 return _RAm == 0 && _DECm == 0;
915 return static_cast <double> (_RAm) / 3600.0;
920 return static_cast <double> (_DECm) / 3600.0;
925 _RAm = std::lround(std::fmod(value + 24.0, 24.0) * 3600.0);
932 _DECm = std::lround(std::fmod(value, 256.0) * 3600.0);
935 if ((-256 * 3600 < _DECm && _DECm < -180 * 3600) || (0 <= _DECm && _DECm <= +180 * 3600))
936 _pointingState = POINTING_NORMAL;
938 _pointingState = POINTING_BEYOND_POLE;
945 switch (_pointingState)
947 case POINTING_BEYOND_POLE:
948 return static_cast <double> ((12 * 3600 + _RAm) % (24 * 3600)) / 3600.0;
949 case POINTING_NORMAL:
951 return static_cast <double> ((24 * 3600 + _RAm) % (24 * 3600)) / 3600.0;
958 long _DEC = 90 * 3600 - _DECm;
959 if (POINTING_BEYOND_POLE == _pointingState)
960 _DEC = 180 * 3600 - _DEC;
961 while (+90 * 3600 < _DEC) _DEC -= 180 * 3600;
962 while (_DEC < -90 * 3600) _DEC += 180 * 3600;
963 return static_cast <double> (_DEC) / 3600.0;
968 switch (_pointingState)
970 case POINTING_BEYOND_POLE:
971 _RAm =
static_cast <long> (std::fmod(12.0 + _RAsky, 24.0) * 3600.0);
973 case POINTING_NORMAL:
974 _RAm =
static_cast <long> (std::fmod(24.0 + _RAsky, 24.0) * 3600.0);
982 switch (_pointingState)
984 case POINTING_BEYOND_POLE:
985 _DECm = 90 * 3600 - std::lround((180.0 - _DECsky) * 3600.0);
987 case POINTING_NORMAL:
988 _DECm = 90 * 3600 - std::lround(_DECsky * 3600.0);
999 int const hours = (24 +
static_cast <int> (_RAm / 3600)) % 24;
1000 int const minutes =
static_cast <int> (_RAm / 60) % 60;
1001 int const seconds =
static_cast <int> (_RAm) % 60;
1003 int const written = snprintf(buf, buf_length,
"%02d:%02d:%02d", hours, minutes, seconds);
1005 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1017 int hours = 0, minutes = 0, seconds = 0;
1018 if (3 == sscanf(buf,
"%02d:%02d:%02d", &hours, &minutes, &seconds))
1021 _RAm = (24 * 3600 + (hours % 24) * 3600 + minutes * 60 + seconds) % (24 * 3600);
1032 int const degrees =
static_cast <int> (_DECm / 3600) % 256;
1033 int const minutes =
static_cast <int> (std::abs(_DECm) / 60) % 60;
1034 int const seconds =
static_cast <int> (std::abs(_DECm)) % 60;
1036 if (degrees < -255 || +255 < degrees)
1037 return (
char const*)0;
1039 char high_digit =
'0';
1040 if (-100 < degrees && degrees < 100)
1042 high_digit =
'0' + (std::abs(degrees) / 10);
1044 else switch (std::abs(degrees) / 10)
1095 return (
char const*)0;
1099 char const low_digit =
'0' + (std::abs(degrees) % 10);
1101 int const written = snprintf(buf, buf_length,
"%c%c%c:%02d:%02d", (0 <= degrees) ?
'+' :
'-', high_digit, low_digit,
1104 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1111 int const degrees =
static_cast <int> (_DECm / 3600) % 256;
1112 int const minutes =
static_cast <int> (std::abs(_DECm) / 60) % 60;
1113 int const seconds =
static_cast <int> (std::abs(_DECm)) % 60;
1115 if (degrees < -255 || +255 < degrees)
1116 return (
char const*)0;
1118 int const written = snprintf(buf, buf_length,
"%+03d:%02d:%02d", degrees, minutes, seconds);
1120 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1129 strncpy(b, buf,
sizeof(b));
1142 } *
const DecValues = (
struct _DecValues*) b;
1144 if (DecValues->degrees[1] <
'0' ||
'I' < DecValues->degrees[1])
1147 int const sgn = DecValues->degrees[0] ==
'-' ? -1 : +1;
1150 switch (DecValues->degrees[1])
1153 DecValues->degrees[0] =
'1';
1154 DecValues->degrees[1] =
'0';
1157 DecValues->degrees[0] =
'1';
1158 DecValues->degrees[1] =
'1';
1161 DecValues->degrees[0] =
'1';
1162 DecValues->degrees[1] =
'2';
1165 DecValues->degrees[0] =
'1';
1166 DecValues->degrees[1] =
'3';
1169 DecValues->degrees[0] =
'1';
1170 DecValues->degrees[1] =
'4';
1173 DecValues->degrees[0] =
'1';
1174 DecValues->degrees[1] =
'5';
1177 DecValues->degrees[0] =
'1';
1178 DecValues->degrees[1] =
'6';
1181 DecValues->degrees[0] =
'1';
1182 DecValues->degrees[1] =
'7';
1185 DecValues->degrees[0] =
'1';
1186 DecValues->degrees[1] =
'8';
1189 DecValues->degrees[0] =
'1';
1190 DecValues->degrees[1] =
'9';
1193 DecValues->degrees[0] =
'2';
1194 DecValues->degrees[1] =
'0';
1197 DecValues->degrees[0] =
'2';
1198 DecValues->degrees[1] =
'1';
1201 DecValues->degrees[0] =
'2';
1202 DecValues->degrees[1] =
'2';
1205 DecValues->degrees[0] =
'2';
1206 DecValues->degrees[1] =
'3';
1209 DecValues->degrees[0] =
'2';
1210 DecValues->degrees[1] =
'4';
1213 DecValues->degrees[0] =
'2';
1214 DecValues->degrees[1] =
'5';
1217 DecValues->degrees[0] =
'0';
1220 DecValues->degrees[3] = DecValues->minutes[2] = DecValues->seconds[2] =
'\0';
1223 atol(DecValues->degrees) * 3600 +
1224 atol(DecValues->minutes) * 60 +
1225 atol(DecValues->seconds) );
1228 if ((-256 * 3600 < _DECm && _DECm <= -180 * 3600) || (0 <= _DECm && _DECm <= +180 * 3600))
1229 _pointingState = POINTING_NORMAL;
1231 _pointingState = POINTING_BEYOND_POLE;
1241 long delta = b.
_RAm - _RAm;
1242 if (delta > +12 * 3600) delta -= 24 * 3600;
1243 if (delta < -12 * 3600) delta += 24 * 3600;
1244 return static_cast <double> (delta * 15) / 3600.0;
1250 return static_cast <double> (b.
_DECm - _DECm) / 3600.0;
1255 double const ra_distance = RA_degrees_to(b);
1256 double const dec_distance = DEC_degrees_to(b);
1258 return std::sqrt(ra_distance * ra_distance + dec_distance * dec_distance);
1263 return (_pointingState != b._pointingState) || (RA_GRANULARITY <= std::abs(RA_degrees_to(b)))
1264 || (DEC_GRANULARITY <= std::abs(DEC_degrees_to(b)));
1275 return _pointingState = pointingState;
1280 return _pointingState;
enum PointingState setPointingState(enum PointingState)
bool operator==(MechanicalPoint const &) const
char const * toStringDEC_Sim(char *, size_t) const
double DEC_degrees_to(MechanicalPoint const &) const
bool operator!=(MechanicalPoint const &) const
bool atParkingPosition() const
enum PointingState getPointingState() const
bool parseStringRA(char const *, size_t)
double RA_degrees_to(MechanicalPoint const &) const
char const * toStringDEC(char *, size_t) const
double operator-(MechanicalPoint const &) const
bool parseStringDEC(char const *, size_t)
char const * toStringRA(char *, size_t) const
virtual bool updateLocation(double, double, double) override
Update telescope location settings.
bool setTargetMechanicalPosition(MechanicalPoint const &)
virtual bool MoveNS(INDI_DIR_NS, TelescopeMotionCommand) override
Start or Stop the telescope motion in the direction dir.
virtual bool checkConnection() override
bool gotoTargetPosition(MechanicalPoint const &)
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
const char * getDefautName()
int getReply(char *, size_t const)
virtual bool ReadScopeStatus() override
Read telescope status.
int sendCmd(char const *)
virtual bool Sync(double, double) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
bool getCurrentMechanicalPosition(MechanicalPoint &)
virtual void getBasicData() override
virtual void setPierSide(TelescopePierSide)
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
bool operator!=(std::nullptr_t) const
void setCurrentPollingPeriod(uint32_t msec)
setCurrentPollingPeriod Change the current polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
bool isSimulation() const
TelescopeStatus TrackState
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
ISwitchVectorProperty SlewRateSP
ISwitchVectorProperty PierSideSP
INumberVectorProperty EqNP
@ TELESCOPE_HAS_PIER_SIDE
TelescopePierSide getPierSide()
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
ISwitchVectorProperty MovementWESP
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void slewError(int slewCode)
bool updateSlewRate(int index)
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
void setLX200Capability(uint32_t cap)
struct _adjustment adjustments[]
struct _simEQ500X simEQ500X_t
#define MechanicalPoint_DEC_FormatW
#define MechanicalPoint_RA_Format
#define MechanicalPoint_DEC_FormatR
simEQ500X_t simEQ500X_zero
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Implementations for common driver routines.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
int MoveTo(int fd, int direction)
int HaltMovement(int fd, int direction)
int getCommandString(int fd, char *data, const char *cmd)
char MechanicalDECStr[16]
double round(double value, int decimal_places)