26 #include <libnova/transform.h>
27 #include <libnova/precession.h>
30 #include <libnova/utility.h>
36 #define SYNSCAN_SLEW_RATES 9
38 constexpr uint16_t SynscanLegacyDriver::SLEW_RATE[];
45 strncpy(LastParkRead,
"", 1);
63 return "SynScan Legacy";
92 IUFillText(&BasicMountInfoT[MI_FW_VERSION],
"FW_VERSION",
"Firmware version",
"-");
93 IUFillText(&BasicMountInfoT[MI_MOUNT_CODE],
"MOUNT_CODE",
"Mount code",
"-");
94 IUFillText(&BasicMountInfoT[MI_ALIGN_STATUS],
"ALIGNMENT_STATUS",
"Alignment status",
"-");
95 IUFillText(&BasicMountInfoT[MI_GOTO_STATUS],
"GOTO_STATUS",
"Goto status",
"-");
96 IUFillText(&BasicMountInfoT[MI_POINT_STATUS],
"MOUNT_POINTING_STATUS",
97 "Mount pointing status",
"-");
98 IUFillText(&BasicMountInfoT[MI_TRACK_MODE],
"TRACKING_MODE",
"Tracking mode",
"-");
124 if (!strcmp(name, UseWiFiSP.name))
130 LOG_ERROR(
"Cannot select WiFi mode while already connected. It must be selected when when disconnected.");
136 if (UseWiFiS[WIFI_ENABLED].s ==
ISS_ON)
142 LOG_INFO(
"Driver is configured for WiFi connection to 192.168.4.2 at TCP port 11882");
149 const char *newNames[] = {
"CONNECTION_SERIAL",
"CONNECTION_TCP" };
155 LOG_INFO(
"Driver is configured for serial connection to the hand controller.");
161 const char *newNames[] = {
"CONNECTION_SERIAL",
"CONNECTION_TCP" };
173 char *formats[],
char *names[],
int n)
189 UpdateMountInformation(
false);
206 result = std::stoi(res,
nullptr, 16);
208 catch (std::invalid_argument &)
210 LOGF_ERROR(
"Failed to parse %s to integer.", res.c_str());
222 int bytesWritten = 0;
224 char res[MAX_SYN_BUF] = {0};
239 memset(res, 0, MAX_SYN_BUF);
247 LOG_ERROR(
"Mount is not aligned. Please align the mount first and connection again.");
255 memset(res, 0, MAX_SYN_BUF);
256 LOG_DEBUG(
"Getting Firmware version...");
265 int tmp1 { 0 }, tmp2 { 0 };
287 LOG_WARN(
"Firmware version is too old. Update Synscan firmware to v4.38+");
297 memset(res, 0, MAX_SYN_BUF);
307 MountCode =
static_cast<int>(*
reinterpret_cast<unsigned char*
>(&res[0]));
309 MountCode =
static_cast<int>(*
reinterpret_cast<unsigned char*
>(&res[1]));
315 memset(res, 0, MAX_SYN_BUF);
321 if (res[1] ==
'#' &&
static_cast<int>(res[0]) != 0)
359 char res[MAX_SYN_BUF] = {0};
360 int bytesWritten, bytesRead;
362 long unsigned int n1, n2;
372 LOG_WARN(
"Synscan Mount not responding");
421 memset(res, 0, MAX_SYN_BUF);
430 memset(res, 0, MAX_SYN_BUF);
439 memset(res, 0, MAX_SYN_BUF);
451 memset(res, 0, MAX_SYN_BUF);
476 UpdateMountInformation(
true);
531 memset(res, 0, MAX_SYN_BUF);
539 if (strncmp((
char *)res, LastParkRead, 18) == 0)
570 strncpy(LastParkRead, res, 20);
574 memset(res, 0, MAX_SYN_BUF);
581 LOG_DEBUG(
"Read current position failed");
585 sscanf(res,
"%lx,%lx#", &n1, &n2);
586 ra =
static_cast<double>(n1) / 0x100000000 * 24.0;
587 dec =
static_cast<double>(n2) / 0x100000000 * 360.0;
605 double DiffAlt { 0 };
614 if (std::abs(DiffAlt) > 4)
618 else if (std::abs(DiffAlt) > 1.2)
622 else if (std::abs(DiffAlt) > 0.5)
626 else if (std::abs(DiffAlt) > 0.2)
630 else if (std::abs(DiffAlt) > 0.025)
634 LOGF_DEBUG(
"Slewing Alt axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
636 if (NewRate != CustomNSSlewRate)
640 CustomNSSlewRate = NewRate;
645 CustomNSSlewRate = NewRate;
654 LOG_DEBUG(
"Slewing on Alt axis finished");
658 DiffAz = (DiffAz + 360) * 2;
659 else if (DiffAz > 180)
660 DiffAz = (DiffAz - 360) * 2;
665 if (std::abs(DiffAz) > 4)
669 else if (std::abs(DiffAz) > 1.2)
673 else if (std::abs(DiffAz) > 0.5)
677 else if (std::abs(DiffAz) > 0.2)
681 else if (std::abs(DiffAz) > 0.025)
685 LOGF_DEBUG(
"Slewing Az axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
687 if (NewRate != CustomWESlewRate)
691 CustomWESlewRate = NewRate;
696 CustomWESlewRate = NewRate;
705 LOG_DEBUG(
"Slewing on Az axis finished");
717 char res[MAX_SYN_BUF] = {0};
718 int bytesWritten, bytesRead;
741 if (bytesRead != 1 || res[0] !=
'#')
743 LOG_DEBUG(
"Timeout waiting for scope to start tracking.");
751 char res[MAX_SYN_BUF] = {0};
752 int bytesWritten, bytesRead;
763 LOG_WARN(
"Wrong answer from the mount");
776 epochPos.declination =
dec;
782 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
783 int n2 = J2000Pos.declination * 0x1000000 / 360;
785 LOGF_DEBUG(
"Goto - JNow RA: %g JNow DE: %g J2000 RA: %g J2000 DE: %g",
ra,
dec, J2000Pos.rightascension,
786 J2000Pos.declination);
791 snprintf(res, MAX_SYN_BUF,
"r%08X,%08X", n1, n2);
793 memset(&res[18], 0, 1);
796 if (bytesRead != 1 || res[0] !=
'#')
798 LOG_DEBUG(
"Timeout waiting for scope to complete goto.");
807 LOGF_DEBUG(
"Goto - JNow RA: %g JNow DE: %g (az: %g alt: %g)",
ra,
dec, TargetAltAz.azimuth, TargetAltAz.altitude);
808 char RAStr[MAX_SYN_BUF] = {0}, DEStr[MAX_SYN_BUF] = {0}, AZStr[MAX_SYN_BUF] = {0}, ATStr[MAX_SYN_BUF] = {0};
811 fs_sexa(AZStr, TargetAltAz.azimuth, 2, 3600);
812 fs_sexa(ATStr, TargetAltAz.altitude, 2, 3600);
814 LOGF_INFO(
"Goto RA: %s DE: %s AZ: %s ALT: %s", RAStr, DEStr, AZStr, ATStr);
827 char res[MAX_SYN_BUF] = {0};
828 int bytesWritten, bytesRead;
832 strncpy(LastParkRead,
"", 1);
847 if (bytesRead != 1 || res[0] !=
'#')
849 LOG_DEBUG(
"Timeout waiting for scope to stop tracking.");
856 if (bytesRead != 1 || res[0] !=
'#')
858 LOG_DEBUG(
"Timeout waiting for scope to respond to park.");
881 LOG_INFO(
"Setting arbitrary park positions is not supported yet.");
888 LOG_DEBUG(
"Setting Park Data to Default.");
900 char res[MAX_SYN_BUF] = {0};
901 int bytesWritten, bytesRead;
911 CustomNSSlewRate = -1;
912 CustomWESlewRate = -1;
923 if (bytesRead != 1 || res[0] !=
'#')
925 LOG_DEBUG(
"Timeout waiting for scope to stop tracking.");
950 PassthruCommand(37, 17, 2, 0, 0);
954 int tt = (CustomNSSlewRate == -1 ?
SlewRate : CustomNSSlewRate);
959 PassthruCommand(37, 17, 2, tt, 0);
963 PassthruCommand(36, 17, 2, tt, 0);
977 PassthruCommand(37, 16, 2, 0, 0);
981 int tt = (CustomWESlewRate == -1 ?
SlewRate : CustomWESlewRate);
986 PassthruCommand(36, 16, 2, tt, 0);
990 PassthruCommand(37, 16, 2, tt, 0);
1003 int SynscanLegacyDriver::PassthruCommand(
int cmd,
int target,
int msgsize,
int data,
int numReturn)
1005 char test[20] = {0};
1006 int bytesRead, bytesWritten;
1017 memset(test, 0, 20);
1025 test[7] = numReturn;
1029 memset(test, 0, 20);
1038 retval = retval << 8;
1043 retval = retval << 8;
1059 time_t now = time (
nullptr);
1060 strftime(timeString,
MAXINDINAME,
"%T", gmtime(&now));
1068 char res[MAX_SYN_BUF] = {0};
1069 int bytesWritten = 0, bytesRead = 0;
1081 ln_zonedate localTime;
1083 int offset, daylightflag;
1085 localTime.hours = res[0];
1086 localTime.minutes = res[1];
1087 localTime.seconds = res[2];
1088 localTime.months = res[3];
1089 localTime.days = res[4];
1090 localTime.years = res[5];
1091 offset = (int)res[6];
1095 localTime.gmtoff = offset;
1097 daylightflag = res[7];
1098 localTime.years += 2000;
1099 localTime.gmtoff *= 3600;
1101 ln_zonedate_to_date(&localTime, &utcTime);
1107 sec = (int)utcTime.seconds;
1108 sprintf(utc,
"%04d-%02d-%dT%d:%02d:%02d", utcTime.years, utcTime.months, utcTime.days, utcTime.hours,
1109 utcTime.minutes, sec);
1110 if (daylightflag == 1)
1111 offset = offset + 1;
1112 sprintf(ofs,
"%d", offset);
1119 LOGF_INFO(
"Mount UTC Time %s Offset %d", utc, offset);
1139 char res[MAX_SYN_BUF] = {0};
1140 int bytesWritten = 0, bytesRead = 0;
1151 LOG_WARN(
"Bad echo in ReadLocation");
1167 int a, b, c, d, e, f, g, h;
1177 LOGF_DEBUG(
"Pos %d:%d:%d %d:%d:%d", a, b, c, e, f, g);
1205 char LongitudeStr[32] = {0}, LatitudeStr[32] = {0};
1206 fs_sexa(LongitudeStr, lon, 2, 3600);
1207 fs_sexa(LatitudeStr, lat, 2, 3600);
1208 LOGF_INFO(
"Mount Longitude %s Latitude %s", LongitudeStr, LatitudeStr);
1217 LOG_INFO(
"Mount does not support setting location.");
1225 char res[MAX_SYN_BUF] = {0};
1226 int bytesWritten = 0, bytesRead = 0;
1230 struct ln_zonedate ltm;
1232 ln_date_to_zonedate(utc, <m, (
long)utc_offset * 3600.0);
1240 res[2] = ltm.minutes;
1241 res[3] = (char)(
int)ltm.seconds;
1242 res[4] = ltm.months;
1247 res[7] = (char)(
int)utc_offset;
1252 LOGF_INFO(
"Setting mount date/time to %04d-%02d-%02d %d:%02d:%02d UTC Offset: %d",
1253 ltm.years, ltm.months, ltm.days, ltm.hours, ltm.minutes, ltm.seconds, utc_offset);
1266 LOG_INFO(
"Invalid return from set time");
1275 char res[MAX_SYN_BUF] = {0};
1276 int bytesWritten = 0, bytesRead = 0;
1278 bool IsWest =
false;
1281 ln_lnlat_posn p1 { 0, 0 };
1300 if (longitude > 180)
1302 p1.lng = 360.0 - longitude;
1310 ln_lnlat_to_hlnlat(&p1, &p2);
1311 LOGF_INFO(
"Update location to latitude %d:%d:%1.2f longitude %d:%d:%1.2f",
1312 p2.lat.degrees, p2.lat.minutes, p2.lat.seconds, p2.lng.degrees, p2.lng.minutes, p2.lng.seconds);
1315 res[1] = p2.lat.degrees;
1316 res[2] = p2.lat.minutes;
1317 tmp = p2.lat.seconds + 0.5;
1320 if (p2.lat.neg == 0)
1329 res[5] = p2.lng.degrees;
1330 res[6] = p2.lng.minutes;
1331 s = (int)(p2.lng.seconds + 0.5);
1348 LOG_INFO(
"Invalid response for location setting");
1385 char res[MAX_SYN_BUF] = {0};
1386 int bytesWritten, bytesRead;
1401 LOGF_DEBUG(
"Sync - ra: %g de: %g to az: %g alt: %g",
ra,
dec, TargetAltAz.azimuth, TargetAltAz.altitude);
1403 int Az = (int)(TargetAltAz.azimuth * 16777216 / 360);
1409 *
reinterpret_cast<unsigned char*
>(&res[4]) = (
unsigned char)(Az / 65536);
1410 Az -= (Az / 65536) * 65536;
1411 *
reinterpret_cast<unsigned char*
>(&res[5]) = (
unsigned char)(Az / 256);
1412 Az -= (Az / 256) * 256;
1413 *
reinterpret_cast<unsigned char*
>(&res[6]) = (
unsigned char)Az;
1418 int Alt = (int)(TargetAltAz.altitude * 16777216 / 360);
1424 *
reinterpret_cast<unsigned char*
>(&res[4]) = (
unsigned char)(Alt / 65536);
1425 Alt -= (Alt / 65536) * 65536;
1426 *
reinterpret_cast<unsigned char*
>(&res[5]) = (
unsigned char)(Alt / 256);
1427 Alt -= (Alt / 256) * 256;
1428 *
reinterpret_cast<unsigned char*
>(&res[6]) = (
unsigned char)Alt;
1440 epochPos.declination =
dec;
1447 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
1448 int n2 = J2000Pos.declination * 0x1000000 / 360;
1452 snprintf(res, MAX_SYN_BUF,
"s%08X,%08X", n1, n2);
1453 memset(&res[18], 0, 1);
1461 if (bytesRead != 1 || res[0] !=
'#')
1463 LOG_DEBUG(
"Timeout waiting for scope to complete syncing.");
1468 if (IsTrackingBeforeSync)
1474 void SynscanLegacyDriver::UpdateMountInformation(
bool inform_client)
1476 bool BasicMountInfoHasChanged =
false;
1482 BasicMountInfoHasChanged =
true;
1484 if (std::string(BasicMountInfoT[MI_MOUNT_CODE].text) != MountCodeStr)
1486 IUSaveText(&BasicMountInfoT[MI_MOUNT_CODE], MountCodeStr.c_str());
1487 BasicMountInfoHasChanged =
true;
1490 if (std::string(BasicMountInfoT[MI_ALIGN_STATUS].text) !=
AlignmentStatus)
1493 BasicMountInfoHasChanged =
true;
1495 if (std::string(BasicMountInfoT[MI_GOTO_STATUS].text) !=
GotoStatus)
1498 BasicMountInfoHasChanged =
true;
1500 if (std::string(BasicMountInfoT[MI_POINT_STATUS].text) !=
PointingStatus)
1503 BasicMountInfoHasChanged =
true;
1505 if (std::string(BasicMountInfoT[MI_TRACK_MODE].text) !=
TrackingMode)
1508 BasicMountInfoHasChanged =
true;
1511 if (BasicMountInfoHasChanged && inform_client)
1515 void SynscanLegacyDriver::MountSim()
1517 static struct timeval ltv;
1523 gettimeofday(&tv,
nullptr);
1525 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1528 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1531 da = currentSlewRate * dt;
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
const char * getDeviceName() const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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 addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
Connection::Interface * getActiveConnection()
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
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.
INumberVectorProperty LocationNP
virtual bool initProperties() override
Called to initialize basic properties required all the time.
ITextVectorProperty TimeTP
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
void setTelescopeConnection(const uint8_t &value)
setTelescopeConnection Set telescope connection mode. Child class should call this in the constructor...
ISwitchVectorProperty SlewRateSP
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Connection::TCP * tcpConnection
@ TELESCOPE_HAS_PIER_SIDE
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
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....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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 ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
std::string HandsetFwVersion
virtual void initParking()
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
std::string TrackingStatus
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual const char * getDefaultName() override
int HexStrToInteger(const std::string &str)
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool Park() override
Park the telescope to its home position.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
std::string AlignmentStatus
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool AnalyzeMount()
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
std::string PointingStatus
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
double range24(double r)
range24 Limits a number to be between 0-24 range.
double rangeDec(double decdegrees)
rangeDec Limits declination value to be in -90 to 90 range.
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.
#define TRACKRATE_SIDEREAL
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 IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, 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,...)
void J2000toObserved(IEquatorialCoordinates *J2000pos, double jd, IEquatorialCoordinates *observed)
*J2000toObserved converts catalogue to observed
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
#define SYNSCAN_SLEW_RATES