2LX200 - based Omegon
EQ500X Equatorial Mount
6This library is free software;
7you can redistribute it and / or
8modify it under the terms of the GNU Lesser General Public
9License as published by the Free Software Foundation;
11version 2.1 of the License, or (at your option) any later version.
13This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY;
15without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Lesser General Public License for more details.
19You should have received a copy of the GNU Lesser General Public
20License along with this library;
21if not, write to the Free Software
22Foundation, 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"
65static int const EQ500X_TIMEOUT = 5;
68static double constexpr ONEDEGREE = 1.0;
69static double constexpr ARCMINUTE = ONEDEGREE / 60.0;
70static double constexpr ARCSECOND = ONEDEGREE / 3600.0;
73static double RA_GRANULARITY = std::lround((15.0 * ARCSECOND) * 3600.0) / 3600.0;
74static double DEC_GRANULARITY = std::lround((1.0 * ARCSECOND) * 3600.0) / 3600.0;
78static 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
323 while (+12 <= HA) HA -= 24;
324 while (HA <= -12) HA += 24;
334 LOGF_DEBUG(
"Mount HA=%lfh pointing %s on %s side", HA,
344 _gotoEngaged =
false;
356 double const ra_delta = currentMechPosition.
RA_degrees_to(targetMechPosition);
357 double const dec_delta = currentMechPosition.
DEC_degrees_to(targetMechPosition);
358 double const abs_ra_delta = std::abs(ra_delta);
359 double const abs_dec_delta = std::abs(dec_delta);
362 if (RA_GRANULARITY <= abs_ra_delta || DEC_GRANULARITY <= abs_dec_delta)
365 struct _adjustment const *ra_adjust =
nullptr, *dec_adjust =
nullptr;
371 assert(
nullptr != ra_adjust);
372 LOGF_DEBUG(
"RA %lf-%lf = %+lf° under %lf° would require adjustment at %s until less than %lf°",
373 targetMechPosition.
RAm() * 15.0, currentMechPosition.
RAm() * 15.0, ra_delta, ra_adjust->
distance, ra_adjust->
slew_rate,
374 std::max(ra_adjust->
epsilon, 15.0 / 3600.0));
380 assert(
nullptr != dec_adjust);
381 LOGF_DEBUG(
"DEC %lf-%lf = %+lf° under %lf° would require adjustment at %s until less than %lf°",
382 targetMechPosition.
DECm(), currentMechPosition.
DECm(), dec_delta, dec_adjust->distance, dec_adjust->slew_rate,
383 dec_adjust->epsilon);
386 char CmdString[32] = {0};
389 static struct _adjustment const * previous_adjustment =
nullptr;
393 adjustment = ra_adjust < dec_adjust ? dec_adjust : ra_adjust;
396 if (ra_adjust != adjustment)
400 strcat(CmdString,
":Qe#");
405 strcat(CmdString,
":Qw#");
411 if (dec_adjust != adjustment)
415 strcat(CmdString,
":Qn#");
416 DECmDecrease =
false;
420 strcat(CmdString,
":Qs#");
421 DECmIncrease =
false;
426 if (previous_adjustment != adjustment)
429 strcat(CmdString, adjustment->
slew_rate);
432 if (adjustment < previous_adjustment)
433 countdown = MAX_CONVERGENCE_LOOPS;
438 previous_adjustment = adjustment;
444 if (ra_adjust == adjustment)
447 double const ra_epsilon = std::max(adjustment->
epsilon, RA_GRANULARITY);
450 bool const doDecrease = ra_epsilon <= ra_delta;
451 bool const doIncrease = ra_delta <= -ra_epsilon;
452 assert(!(doDecrease && doIncrease));
455 if (RAmIncrease && (!doDecrease || doIncrease))
457 strcat(CmdString,
":Qe#");
460 if (RAmDecrease && (!doIncrease || doDecrease))
462 strcat(CmdString,
":Qw#");
467 if (doDecrease && !RAmIncrease)
469 strcat(CmdString,
":Me#");
472 if (doIncrease && !RAmDecrease)
474 strcat(CmdString,
":Mw#");
481 if (dec_adjust == adjustment)
484 double const dec_epsilon = std::max(adjustment->
epsilon, DEC_GRANULARITY);
487 bool const doDecrease = dec_epsilon <= dec_delta;
488 bool const doIncrease = dec_delta <= -dec_epsilon;
489 assert(!(doDecrease && doIncrease));
492 if (DECmIncrease && (!doDecrease || doIncrease))
494 strcat(CmdString,
":Qn#");
495 DECmIncrease =
false;
497 if (DECmDecrease && (!doIncrease || doDecrease))
499 strcat(CmdString,
":Qs#");
500 DECmDecrease =
false;
504 if (doDecrease && !DECmIncrease)
506 strcat(CmdString,
":Mn#");
509 if (doIncrease && !DECmDecrease)
511 strcat(CmdString,
":Ms#");
517 assert(!(RAmIncrease && RAmDecrease) && !(DECmDecrease && DECmIncrease));
520 LOGF_DEBUG(
"Centering (%lf°,%lf°) delta (%lf°,%lf°) moving %c%c%c%c at %s until less than (%lf°,%lf°)",
521 targetMechPosition.
RAm() * 15.0, targetMechPosition.
DECm(), ra_delta, dec_delta, RAmDecrease ?
'W' :
'.',
522 RAmIncrease ?
'E' :
'.', DECmDecrease ?
'N' :
'.', DECmIncrease ?
'S' :
'.', adjustment->
slew_rate,
523 std::max(adjustment->
epsilon, RA_GRANULARITY), adjustment->
epsilon);
526 if (CmdString[0] !=
'\0')
531 LOGF_ERROR(
"Error centering (%lf°,%lf°)", targetMechPosition.
RAm() * 15.0, targetMechPosition.
DECm());
543 if (!RAmIncrease && !RAmDecrease && !DECmDecrease && !DECmIncrease)
545 LOGF_INFO(
"Centering delta (%lf,%lf) intermediate adjustment complete (%d loops)", ra_delta, dec_delta,
546 MAX_CONVERGENCE_LOOPS - countdown);
547 adjustment =
nullptr;
552 else if (--countdown <= 0)
554 LOGF_ERROR(
"Failed centering to (%lf,%lf) under loop limit, aborting...", targetMechPosition.
RAm(),
555 targetMechPosition.
DECm());
564 LOG_INFO(
"Slew is complete. Tracking...");
567 adjustment =
nullptr;
578 if (DECmDecrease) DECmDecrease =
false;
579 if (DECmIncrease) DECmIncrease =
false;
580 if (RAmIncrease) RAmIncrease =
false;
581 if (RAmDecrease) RAmDecrease =
false;
582 adjustment =
nullptr;
586 if (ra_changed || dec_changed)
595 adjustment =
nullptr;
609 while (+12 <= HA) HA -= 24;
610 while (HA <= -12) HA += 24;
625 Telescope::AbortSP.apply();
632 Telescope::AbortSP.apply();
647 const struct timespec timeout = {0, 100000000L};
648 nanosleep(&timeout,
nullptr);
670 LOGF_INFO(
"Goto target (%lfh,%lf°) HA %lf, quadrant %s",
ra,
dec, HA,
675 countdown = MAX_CONVERGENCE_LOOPS;
687 char RAStr[16] = {0}, DecStr[16] = {0};
691 LOGF_INFO(
"Slewing to JNow RA: %s - DEC: %s", RAStr, DecStr);
706 tcflush(
PortFD, TCIFLUSH);
710 if (!strncmp(
"No name", b,
sizeof(b)))
765 LOG_ERROR(
"Error setting N/S motion direction.");
770 (current_move ==
LX200_NORTH) ?
"North" :
"South");
781 (current_move ==
LX200_NORTH) ?
"North" :
"South");
797 int nbytes_write = 0;
808 int error_type =
tty_read(
PortFD, data, len, EQ500X_TIMEOUT, &nbytes_read);
810 LOGF_DEBUG(
"RES <%.*s> (%d)", nbytes_read, data, error_type);
826 return buf[0] !=
'0';
855 LOGF_DEBUG(
"Mount mechanical DEC reads '%s' as %lf.", b, result.
DECm());
865 LOGF_DEBUG(
"Mount mechanical RA reads '%s' as %lf.", b, result.
RAm());
883 snprintf(CmdString,
sizeof(CmdString),
":Sr%s#:Sd%s#", p.
toStringRA(bufRA,
sizeof(bufRA)), p.
toStringDEC(bufDEC,
885 LOGF_DEBUG(
"Target RA '%f' DEC '%f' converted to '%s'",
static_cast <float> (p.
RAm()),
static_cast <float> (p.
DECm()),
892 if (buf[0] ==
'1' && buf[1] ==
'1')
894 else LOGF_ERROR(
"Failed '%s', mount replied %c%c", CmdString, buf[0], buf[1]);
895 else LOGF_ERROR(
"Failed getting 2-byte reply to '%s'", CmdString);
917 return _RAm == 0 && _DECm == 0;
922 return static_cast <double> (_RAm) / 3600.0;
927 return static_cast <double> (_DECm) / 3600.0;
932 _RAm = std::lround(std::fmod(value + 24.0, 24.0) * 3600.0);
939 _DECm = std::lround(std::fmod(value, 256.0) * 3600.0);
942 if ((-256 * 3600 < _DECm && _DECm < -180 * 3600) || (0 <= _DECm && _DECm <= +180 * 3600))
943 _pointingState = POINTING_NORMAL;
945 _pointingState = POINTING_BEYOND_POLE;
952 switch (_pointingState)
954 case POINTING_BEYOND_POLE:
955 return static_cast <double> ((12 * 3600 + _RAm) % (24 * 3600)) / 3600.0;
956 case POINTING_NORMAL:
958 return static_cast <double> ((24 * 3600 + _RAm) % (24 * 3600)) / 3600.0;
965 long _DEC = 90 * 3600 - _DECm;
966 if (POINTING_BEYOND_POLE == _pointingState)
967 _DEC = 180 * 3600 - _DEC;
968 while (+90 * 3600 < _DEC) _DEC -= 180 * 3600;
969 while (_DEC < -90 * 3600) _DEC += 180 * 3600;
970 return static_cast <double> (_DEC) / 3600.0;
975 switch (_pointingState)
977 case POINTING_BEYOND_POLE:
978 _RAm =
static_cast <long> (std::fmod(12.0 + _RAsky, 24.0) * 3600.0);
980 case POINTING_NORMAL:
981 _RAm =
static_cast <long> (std::fmod(24.0 + _RAsky, 24.0) * 3600.0);
989 switch (_pointingState)
991 case POINTING_BEYOND_POLE:
992 _DECm = 90 * 3600 - std::lround((180.0 - _DECsky) * 3600.0);
994 case POINTING_NORMAL:
995 _DECm = 90 * 3600 - std::lround(_DECsky * 3600.0);
1006 int const hours = (24 +
static_cast <int> (_RAm / 3600)) % 24;
1007 int const minutes =
static_cast <int> (_RAm / 60) % 60;
1008 int const seconds =
static_cast <int> (_RAm) % 60;
1010 int const written = snprintf(buf, buf_length,
"%02d:%02d:%02d", hours, minutes, seconds);
1012 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1024 int hours = 0, minutes = 0, seconds = 0;
1025 if (3 == sscanf(buf,
"%02d:%02d:%02d", &hours, &minutes, &seconds))
1028 _RAm = (24 * 3600 + (hours % 24) * 3600 + minutes * 60 + seconds) % (24 * 3600);
1039 int const degrees =
static_cast <int> (_DECm / 3600) % 256;
1040 int const minutes =
static_cast <int> (std::abs(_DECm) / 60) % 60;
1041 int const seconds =
static_cast <int> (std::abs(_DECm)) % 60;
1043 if (degrees < -255 || +255 < degrees)
1044 return (
char const * )0;
1046 char high_digit =
'0';
1047 if (-100 < degrees && degrees < 100)
1049 high_digit =
'0' + (std::abs(degrees) / 10);
1051 else switch (std::abs(degrees) / 10)
1102 return (
char const*)0;
1106 char const low_digit =
'0' + (std::abs(degrees) % 10);
1108 int const written = snprintf(buf, buf_length,
"%c%c%c:%02d:%02d", (0 <= degrees) ?
'+' :
'-', high_digit, low_digit,
1111 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1118 int const degrees =
static_cast <int> (_DECm / 3600) % 256;
1119 int const minutes =
static_cast <int> (std::abs(_DECm) / 60) % 60;
1120 int const seconds =
static_cast <int> (std::abs(_DECm)) % 60;
1122 if (degrees < -255 || +255 < degrees)
1123 return (
char const * )0;
1125 int const written = snprintf(buf, buf_length,
"%+03d:%02d:%02d", degrees, minutes, seconds);
1127 return (0 < written && written < (
int) buf_length) ? buf : (
char const*)0;
1136 snprintf(b,
sizeof(b),
"%s", buf);
1149 } *
const DecValues = (
struct _DecValues*) b;
1151 if (DecValues->degrees[1] <
'0' ||
'I' < DecValues->degrees[1])
1154 int const sgn = DecValues->degrees[0] ==
'-' ? -1 : +1;
1157 switch (DecValues->degrees[1])
1160 DecValues->degrees[0] =
'1';
1161 DecValues->degrees[1] =
'0';
1164 DecValues->degrees[0] =
'1';
1165 DecValues->degrees[1] =
'1';
1168 DecValues->degrees[0] =
'1';
1169 DecValues->degrees[1] =
'2';
1172 DecValues->degrees[0] =
'1';
1173 DecValues->degrees[1] =
'3';
1176 DecValues->degrees[0] =
'1';
1177 DecValues->degrees[1] =
'4';
1180 DecValues->degrees[0] =
'1';
1181 DecValues->degrees[1] =
'5';
1184 DecValues->degrees[0] =
'1';
1185 DecValues->degrees[1] =
'6';
1188 DecValues->degrees[0] =
'1';
1189 DecValues->degrees[1] =
'7';
1192 DecValues->degrees[0] =
'1';
1193 DecValues->degrees[1] =
'8';
1196 DecValues->degrees[0] =
'1';
1197 DecValues->degrees[1] =
'9';
1200 DecValues->degrees[0] =
'2';
1201 DecValues->degrees[1] =
'0';
1204 DecValues->degrees[0] =
'2';
1205 DecValues->degrees[1] =
'1';
1208 DecValues->degrees[0] =
'2';
1209 DecValues->degrees[1] =
'2';
1212 DecValues->degrees[0] =
'2';
1213 DecValues->degrees[1] =
'3';
1216 DecValues->degrees[0] =
'2';
1217 DecValues->degrees[1] =
'4';
1220 DecValues->degrees[0] =
'2';
1221 DecValues->degrees[1] =
'5';
1224 DecValues->degrees[0] =
'0';
1227 DecValues->degrees[3] = DecValues->minutes[2] = DecValues->seconds[2] =
'\0';
1230 atol(DecValues->degrees) * 3600 +
1231 atol(DecValues->minutes) * 60 +
1232 atol(DecValues->seconds) );
1235 if ((-256 * 3600 < _DECm && _DECm <= -180 * 3600) || (0 <= _DECm && _DECm <= +180 * 3600))
1236 _pointingState = POINTING_NORMAL;
1238 _pointingState = POINTING_BEYOND_POLE;
1248 long delta = b.
_RAm - _RAm;
1249 if (delta > +12 * 3600) delta -= 24 * 3600;
1250 if (delta < -12 * 3600) delta += 24 * 3600;
1251 return static_cast <double> (delta * 15) / 3600.0;
1257 return static_cast <double> (b.
_DECm - _DECm) / 3600.0;
1262 double const ra_distance = RA_degrees_to(b);
1263 double const dec_distance = DEC_degrees_to(b);
1265 return std::sqrt(ra_distance * ra_distance + dec_distance * dec_distance);
1270 return (_pointingState != b._pointingState) || (RA_GRANULARITY <= std::abs(RA_degrees_to(b)))
1271 || (DEC_GRANULARITY <= std::abs(DEC_degrees_to(b)));
1282 return _pointingState = pointingState;
1287 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
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
int findOnSwitchIndex() const
TelescopeStatus TrackState
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
TelescopePierSide getPierSide()
@ TELESCOPE_HAS_PIER_SIDE
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
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
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch PierSideSP
INDI::PropertySwitch MovementWESP
INDI::PropertyNumber LocationNP
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 sidereal time given longitude and system clock.
#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)