26#include <libnova/transform.h>
35#define SIDRATE 0.004178
79 IUFillNumber(&GuideRateN[0],
"GUIDE_RATE",
"x Sidereal",
"%g", 0.1, 1.0, 0.1, 0.5);
112 const struct timespec timeout = {0, 50000000L};
113 char initCMD[] =
":V#";
118 int nbytes_written = 0;
120 LOG_DEBUG(
"Initializing IOptron using :V# CMD...");
122 for (
int i = 0; i < 2; i++)
128 nanosleep(&timeout,
nullptr);
136 nanosleep(&timeout,
nullptr);
142 response[nbytes_read] =
'\0';
145 if (!strcmp(response,
"V1.00#"))
149 nanosleep(&timeout,
nullptr);
160 if (!strcmp(name, GuideRateNP.
name))
164 if (setZEQ25GuideRate(GuideRateN[0].value) ==
TTY_OK)
178bool LX200ZEQ25::isZEQ25Home()
180 const struct timespec timeout = {0, 10000000L};
183 int nbytes_write = 0, nbytes_read = 0;
196 nanosleep(&timeout,
nullptr);
197 tcflush(
PortFD, TCIFLUSH);
198 nanosleep(&timeout,
nullptr);
205 return (bool_return[0] ==
'1');
208int LX200ZEQ25::gotoZEQ25Home()
210 return setZEQ25StandardProcedure(
PortFD,
":MH#");
219 int nbytes_written = 0;
222 const char *
cmd =
":SE#";
226 tcflush(
PortFD, TCIOFLUSH);
244 response[nbytes_read] =
'\0';
247 tcflush(
PortFD, TCIFLUSH);
249 if (response[0] ==
'0')
255 LOGF_ERROR(
"Only received #%d bytes, expected 1.", nbytes_read);
259bool LX200ZEQ25::getMountInfo()
261 char cmd[] =
":MountInfo#";
266 int nbytes_written = 0;
286 response[nbytes_read] =
'\0';
289 if (nbytes_read == 4)
291 if (!strcmp(response,
"8407"))
292 LOG_INFO(
"Detected iEQ45/iEQ30 Mount.");
293 else if (!strcmp(response,
"8497"))
294 LOG_INFO(
"Detected iEQ45 AA Mount.");
295 else if (!strcmp(response,
"8408"))
297 else if (!strcmp(response,
"8498"))
298 LOG_INFO(
"Detected SmartEQ Mount.");
300 LOG_INFO(
"Unknown mount detected.");
302 tcflush(
PortFD, TCIFLUSH);
308 LOGF_ERROR(
"Only received #%d bytes, expected 4.", nbytes_read);
340 bool isMountParked = isZEQ25Parked();
345 LOG_DEBUG(
"Checking if mount is at home position...");
354 double guideRate = 0;
355 if (getZEQ25GuideRate(&guideRate) ==
TTY_OK)
357 GuideRateN[0].value = guideRate;
372 LOG_ERROR(
"Error setting RA/DEC. Unable to Sync.");
388 LOG_INFO(
"Synchronization successful.");
399 const struct timespec timeout = {0, 100000000L};
403 char RAStr[64], DecStr[64];
437 nanosleep(&timeout,
nullptr);
450 if (slewZEQ25() ==
false)
453 LOGF_DEBUG(
"Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
462 LOGF_INFO(
"Slewing to RA: %s - DEC: %s", RAStr, DecStr);
466bool LX200ZEQ25::slewZEQ25()
471 int nbytes_write = 0, nbytes_read = 0;
487 tcflush(
PortFD, TCIFLUSH);
491 return (slewNum[0] ==
'1');
504 int nbytes_written = 0;
506 snprintf(
cmd, 8,
":SR%d#", index + 1);
526 response[nbytes_read] =
'\0';
529 tcflush(
PortFD, TCIFLUSH);
531 return (response[0] ==
'1');
534 LOGF_ERROR(
"Only received #%d bytes, expected 1.", nbytes_read);
538int LX200ZEQ25::getZEQ25MoveRate()
550 int nbytes_written = 0;
570 response[nbytes_read - 1] =
'\0';
573 tcflush(
PortFD, TCIFLUSH);
582 LOGF_ERROR(
"Only received #%d bytes, expected 2.", nbytes_read);
588 struct ln_zonedate ltm;
593 ln_date_to_zonedate(utc, <m, utc_offset * 3600.0);
595 JD = ln_get_julian_day(utc);
606 if (setZEQ25Date(ltm.days, ltm.months, ltm.years) < 0)
612 if (setZEQ25UTCOffset(utc_offset) < 0)
628 double final_longitude;
631 final_longitude = longitude - 360.0;
633 final_longitude = longitude;
635 if (!
isSimulation() && setZEQ25Longitude(final_longitude) < 0)
637 LOG_ERROR(
"Error setting site longitude coordinates");
643 LOG_ERROR(
"Error setting site latitude coordinates");
649 fs_sexa(L, longitude, 4, 3600);
651 LOGF_INFO(
"Site location updated to Lat %.32s - Long %.32s", l, L);
656int LX200ZEQ25::setZEQ25Longitude(
double Long)
660 char temp_string[32];
669 snprintf(temp_string,
sizeof(temp_string),
":Sg %c%03d*%02d:%02d#", sign, abs(d), m, s);
671 return (setZEQ25StandardProcedure(
PortFD, temp_string));
674int LX200ZEQ25::setZEQ25Latitude(
double Lat)
678 char temp_string[32];
687 snprintf(temp_string,
sizeof(temp_string),
":St %c%02d*%02d:%02d#", sign, abs(d), m, s);
689 return (setZEQ25StandardProcedure(
PortFD, temp_string));
692int LX200ZEQ25::setZEQ25UTCOffset(
double hours)
694 char temp_string[16];
696 int h = 0, m = 0, s = 0;
705 snprintf(temp_string,
sizeof(temp_string),
":SG %c%02d:%02d#", sign, abs(h), m);
707 return (setZEQ25StandardProcedure(
PortFD, temp_string));
710int LX200ZEQ25::setZEQ25Date(
int days,
int months,
int years)
712 char command[16] = {0};
713 snprintf(command,
sizeof(command),
":SC %02d/%02d/%02d#", months, days, years % 100);
714 return (setZEQ25StandardProcedure(
PortFD, command));
717int LX200ZEQ25::setZEQ25ObjectRA(
int fd,
double ra)
723 snprintf(
cmd,
sizeof(
cmd),
":Sr %02d:%02d:%02d#", h, m, s);
728int LX200ZEQ25::setZEQ25ObjectDEC(
int fd,
double dec)
737 snprintf(
cmd,
sizeof(
cmd),
":Sd -%02d*%02d:%02d#", d, m,s);
739 snprintf(
cmd,
sizeof(
cmd),
":Sd %+03d*%02d:%02d#", d, m, s);
744int LX200ZEQ25::setZEQ25StandardProcedure(
int fd,
const char *data)
746 const struct timespec timeout = {0, 10000000L};
749 int nbytes_write = 0, nbytes_read = 0;
756 error_type =
tty_read(
fd, bool_return, 1, 5, &nbytes_read);
759 nanosleep(&timeout,
nullptr);
760 tcflush(
fd, TCIFLUSH);
761 nanosleep(&timeout,
nullptr);
768 if (bool_return[0] ==
'0')
788 LOG_ERROR(
"Error setting N/S motion direction.");
793 (current_move ==
LX200_NORTH) ?
"North" :
"South");
804 (current_move ==
LX200_NORTH) ?
"North" :
"South");
820 LOG_ERROR(
"Error setting W/E motion direction.");
835 (current_move ==
LX200_WEST) ?
"West" :
"East");
842int LX200ZEQ25::moveZEQ25To(
int direction)
845 int nbytes_write = 0;
869 tcflush(
PortFD, TCIFLUSH);
873int LX200ZEQ25::haltZEQ25Movement()
877 int nbytes_write = 0;
882 tcflush(
PortFD, TCIFLUSH);
888 return (setZEQ25TrackMode(mode) == 0);
891int LX200ZEQ25::setZEQ25TrackMode(
int mode)
900 snprintf(
cmd, 6,
":RT%d#", mode);
902 return setZEQ25StandardProcedure(
PortFD,
cmd);
905int LX200ZEQ25::setZEQ25Park()
908 int nbytes_write = 0;
915 tcflush(
PortFD, TCIFLUSH);
919int LX200ZEQ25::setZEQ25UnPark()
922 int nbytes_write = 0;
929 tcflush(
PortFD, TCIFLUSH);
933bool LX200ZEQ25::isZEQ25Parked()
945 int nbytes_written = 0;
965 response[nbytes_read] =
'\0';
968 tcflush(
PortFD, TCIFLUSH);
970 return (response[0] ==
'1');
973 LOGF_ERROR(
"Only received #%d bytes, expected 1.", nbytes_read);
984 double parkAZ = horizontalPos.
azimuth;
985 double parkAlt = horizontalPos.
altitude;
987 char AzStr[16], AltStr[16];
988 fs_sexa(AzStr, parkAZ, 2, 3600);
989 fs_sexa(AltStr, parkAlt, 2, 3600);
991 LOGF_DEBUG(
"Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr,
1015 if (gotoZEQ25Home() < 0)
1027 LOG_INFO(
"Parking is in progress...");
1034 char AzStr[16], AltStr[16];
1035 fs_sexa(AzStr, parkAz, 2, 3600);
1036 fs_sexa(AltStr, parkAlt, 2, 3600);
1038 LOGF_DEBUG(
"Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
1043 horizontalPos.alt = parkAlt;
1044 horizontalPos.az = parkAz + 180;
1045 if (horizontalPos.az > 360)
1046 horizontalPos.az -= 360;
1048 IGeographicCoordinates observer;
1051 if (observer.lng > 180)
1052 observer.lng -= 360;
1055 ln_get_equ_from_hrz(&horizontalPos, &observer, ln_get_julian_from_sys(), &equatorialPos);
1065 if (slewZEQ25() ==
false)
1067 LOGF_ERROR(
"Error Slewing to Az %s - Alt %s", AzStr, AltStr);
1074 LOG_INFO(
"Parking is in progress...");
1085 if (setZEQ25UnPark() < 0)
1097 char AzStr[16], AltStr[16];
1098 fs_sexa(AzStr, parkAz, 2, 3600);
1099 fs_sexa(AltStr, parkAlt, 2, 3600);
1100 LOGF_DEBUG(
"Syncing to parked coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
1105 horizontalPos.alt = parkAlt;
1106 horizontalPos.az = parkAz + 180;
1107 if (horizontalPos.az > 360)
1108 horizontalPos.az -= 360;
1110 IGeographicCoordinates observer;
1113 if (observer.lng > 180)
1114 observer.lng -= 360;
1117 ln_get_equ_from_hrz(&horizontalPos, &observer, ln_get_julian_from_sys(), &equatorialPos);
1157 LOG_INFO(
"Telescope arrived at home position.");
1168 LOG_INFO(
"Slew is complete. Tracking...");
1190 if (getZEQ25PierSide(side))
1198void LX200ZEQ25::mountSim()
1200 static struct timeval ltv;
1206 gettimeofday(&tv,
nullptr);
1208 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1211 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1266 if (getZEQ25PierSide(side))
1272int LX200ZEQ25::getZEQ25GuideRate(
double *rate)
1274 char cmd[] =
":AG#";
1278 int nbytes_read = 0;
1279 int nbytes_written = 0;
1285 snprintf(response, 8,
"%3d#",
static_cast<int>(GuideRateN[0].value * 100));
1286 nbytes_read = strlen(response);
1290 tcflush(
PortFD, TCIFLUSH);
1307 if (nbytes_read > 0)
1309 response[nbytes_read] =
'\0';
1314 if (sscanf(response,
"%d#", &rate_num) > 0)
1316 *rate = rate_num / 100.0;
1317 tcflush(
PortFD, TCIFLUSH);
1322 LOGF_ERROR(
"Error: Malformed result (%s).", response);
1327 LOGF_ERROR(
"Only received #%d bytes, expected 1.", nbytes_read);
1331int LX200ZEQ25::setZEQ25GuideRate(
double rate)
1337 int nbytes_read = 0;
1338 int nbytes_written = 0;
1340 int num = rate * 100;
1341 snprintf(
cmd, 16,
":RG%03d#", num);
1347 strcpy(response,
"1");
1348 nbytes_read = strlen(response);
1352 tcflush(
PortFD, TCIFLUSH);
1369 if (nbytes_read > 0)
1371 response[nbytes_read] =
'\0';
1374 tcflush(
PortFD, TCIFLUSH);
1378 LOGF_ERROR(
"Only received #%d bytes, expected 1.", nbytes_read);
1384 int nbytes_write = 0;
1389 sprintf(
cmd,
":Mn%05d#", duration_msec);
1392 sprintf(
cmd,
":Ms%05d#", duration_msec);
1395 sprintf(
cmd,
":Me%05d#", duration_msec);
1398 sprintf(
cmd,
":Mw%05d#", duration_msec);
1408 tcflush(
PortFD, TCIFLUSH);
1412bool LX200ZEQ25::getZEQ25PierSide(TelescopePierSide &side)
1414 int nbytes_written = 0;
1417 char response[8] = {0};
1418 int nbytes_read = 0;
1422 strcpy(response,
"1");
1423 nbytes_read = strlen(response);
1427 tcflush(
PortFD, TCIFLUSH);
1444 if (nbytes_read > 0)
1446 response[nbytes_read] =
'\0';
1449 if ( response[0] ==
'0')
1451 else if ( response[0] ==
'1')
1465 char command[64] = {0};
1468 snprintf(command, 64,
":SG %c%02d:%02d#", offset >= 0 ?
'+' :
'-', h, m);
1469 return setZEQ25StandardProcedure(
PortFD, command);
1483 LOG_WARN(
"Telescope is already homed.");
1487 if (gotoZEQ25Home() < 0)
1489 LOG_ERROR(
"Error slewing to home position.");
1494 LOG_INFO(
"Slewing to home position.");
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setLabel(const char *label)
int findOnSwitchIndex() const
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
double GetAxis1Park() const
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
IGeographicCoordinates m_Location
@ TELESCOPE_HAS_PIER_SIDE
@ TELESCOPE_HAS_TRACK_MODE
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
@ LX200_HAS_PULSE_GUIDING
virtual void slewError(int slewCode)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool sendScopeLocation()
bool sendLocationOnStartup
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool sendScopeTime()
void setLX200Capability(uint32_t cap)
virtual IPState ExecuteHomeAction(TelescopeHomeAction action) override
Execute a homing action.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual const char * getDefaultName() override
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual void getBasicData() override
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool Park() override
Park the telescope to its home position.
virtual bool checkConnection() override
virtual bool setUTCOffset(double offset) override
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool Sync(double, double) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool isSlewComplete() override
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch HomeSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch MovementWESP
INDI::PropertyNumber LocationNP
#define setLocalTime(fd, x, y, z)
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
void getSexComponents(double value, int *d, int *m, int *s)
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.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
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.
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#define LOGF_INFO(fmt,...)
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
#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,...)
#define DEBUGF(priority, msg,...)
int setStandardProcedure(int fd, const char *data)
#define getLX200DEC(fd, x)
#define getLX200RA(fd, x)
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.