26#include <libnova/transform.h>
27#include <libnova/precession.h>
30#include <libnova/utility.h>
36#define SYNSCAN_SLEW_RATES 9
38constexpr 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",
"-");
127 if (!strcmp(name, UseWiFiSP.name))
133 LOG_ERROR(
"Cannot select WiFi mode while already connected. It must be selected when when disconnected.");
139 if (UseWiFiS[WIFI_ENABLED].s ==
ISS_ON)
145 LOG_INFO(
"Driver is configured for WiFi connection to 192.168.4.2 at TCP port 11882");
152 const char *newNames[] = {
"CONNECTION_SERIAL",
"CONNECTION_TCP" };
158 LOG_INFO(
"Driver is configured for serial connection to the hand controller.");
164 const char *newNames[] = {
"CONNECTION_SERIAL",
"CONNECTION_TCP" };
176 char *formats[],
char *names[],
int n)
192 UpdateMountInformation(
false);
209 result = std::stoi(res,
nullptr, 16);
211 catch (std::invalid_argument &)
213 LOGF_ERROR(
"Failed to parse %s to integer.", res.c_str());
225 int bytesWritten = 0;
227 char res[MAX_SYN_BUF] = {0};
242 memset(res, 0, MAX_SYN_BUF);
250 LOG_ERROR(
"Mount is not aligned. Please align the mount first and connection again.");
258 memset(res, 0, MAX_SYN_BUF);
259 LOG_DEBUG(
"Getting Firmware version...");
268 int tmp1 { 0 }, tmp2 { 0 };
290 LOG_WARN(
"Firmware version is too old. Update Synscan firmware to v4.38+");
300 memset(res, 0, MAX_SYN_BUF);
310 MountCode =
static_cast<int>(*
reinterpret_cast<unsigned char *
>(&res[0]));
312 MountCode =
static_cast<int>(*
reinterpret_cast<unsigned char*
>(&res[1]));
318 memset(res, 0, MAX_SYN_BUF);
324 if (res[1] ==
'#' &&
static_cast<int>(res[0]) != 0)
362 char res[MAX_SYN_BUF] = {0};
363 int bytesWritten, bytesRead;
365 long unsigned int n1, n2;
375 LOG_WARN(
"Synscan Mount not responding");
424 memset(res, 0, MAX_SYN_BUF);
433 memset(res, 0, MAX_SYN_BUF);
442 memset(res, 0, MAX_SYN_BUF);
454 memset(res, 0, MAX_SYN_BUF);
479 UpdateMountInformation(
true);
534 memset(res, 0, MAX_SYN_BUF);
542 if (strncmp((
char *)res, LastParkRead, 18) == 0)
573 snprintf(LastParkRead, 20,
"%.20s", res);
577 memset(res, 0, MAX_SYN_BUF);
584 LOG_DEBUG(
"Read current position failed");
588 sscanf(res,
"%lx,%lx#", &n1, &n2);
589 ra =
static_cast<double>(n1) / 0x100000000 * 24.0;
590 dec =
static_cast<double>(n2) / 0x100000000 * 360.0;
608 double DiffAlt { 0 };
617 if (std::abs(DiffAlt) > 4)
621 else if (std::abs(DiffAlt) > 1.2)
625 else if (std::abs(DiffAlt) > 0.5)
629 else if (std::abs(DiffAlt) > 0.2)
633 else if (std::abs(DiffAlt) > 0.025)
637 LOGF_DEBUG(
"Slewing Alt axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
639 if (NewRate != CustomNSSlewRate)
643 CustomNSSlewRate = NewRate;
648 CustomNSSlewRate = NewRate;
657 LOG_DEBUG(
"Slewing on Alt axis finished");
661 DiffAz = (DiffAz + 360) * 2;
662 else if (DiffAz > 180)
663 DiffAz = (DiffAz - 360) * 2;
668 if (std::abs(DiffAz) > 4)
672 else if (std::abs(DiffAz) > 1.2)
676 else if (std::abs(DiffAz) > 0.5)
680 else if (std::abs(DiffAz) > 0.2)
684 else if (std::abs(DiffAz) > 0.025)
688 LOGF_DEBUG(
"Slewing Az axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
690 if (NewRate != CustomWESlewRate)
694 CustomWESlewRate = NewRate;
699 CustomWESlewRate = NewRate;
708 LOG_DEBUG(
"Slewing on Az axis finished");
720 char res[MAX_SYN_BUF] = {0};
721 int bytesWritten, bytesRead;
744 if (bytesRead != 1 || res[0] !=
'#')
746 LOG_DEBUG(
"Timeout waiting for scope to start tracking.");
754 char res[MAX_SYN_BUF] = {0};
755 int bytesWritten, bytesRead;
766 LOG_WARN(
"Wrong answer from the mount");
779 epochPos.declination =
dec;
785 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
786 int n2 = J2000Pos.declination * 0x1000000 / 360;
788 LOGF_DEBUG(
"Goto - JNow RA: %g JNow DE: %g J2000 RA: %g J2000 DE: %g",
ra,
dec, J2000Pos.rightascension,
789 J2000Pos.declination);
794 snprintf(res, MAX_SYN_BUF,
"r%08X,%08X", n1, n2);
796 memset(&res[18], 0, 1);
799 if (bytesRead != 1 || res[0] !=
'#')
801 LOG_DEBUG(
"Timeout waiting for scope to complete goto.");
810 LOGF_DEBUG(
"Goto - JNow RA: %g JNow DE: %g (az: %g alt: %g)",
ra,
dec, TargetAltAz.azimuth, TargetAltAz.altitude);
811 char RAStr[MAX_SYN_BUF] = {0}, DEStr[MAX_SYN_BUF] = {0}, AZStr[MAX_SYN_BUF] = {0}, ATStr[MAX_SYN_BUF] = {0};
814 fs_sexa(AZStr, TargetAltAz.azimuth, 2, 3600);
815 fs_sexa(ATStr, TargetAltAz.altitude, 2, 3600);
817 LOGF_INFO(
"Goto RA: %s DE: %s AZ: %s ALT: %s", RAStr, DEStr, AZStr, ATStr);
830 char res[MAX_SYN_BUF] = {0};
831 int bytesWritten, bytesRead;
835 strncpy(LastParkRead,
"", 1);
850 if (bytesRead != 1 || res[0] !=
'#')
852 LOG_DEBUG(
"Timeout waiting for scope to stop tracking.");
859 if (bytesRead != 1 || res[0] !=
'#')
861 LOG_DEBUG(
"Timeout waiting for scope to respond to park.");
884 LOG_INFO(
"Setting arbitrary park positions is not supported yet.");
891 LOG_DEBUG(
"Setting Park Data to Default.");
903 char res[MAX_SYN_BUF] = {0};
904 int bytesWritten, bytesRead;
914 CustomNSSlewRate = -1;
915 CustomWESlewRate = -1;
926 if (bytesRead != 1 || res[0] !=
'#')
928 LOG_DEBUG(
"Timeout waiting for scope to stop tracking.");
953 PassthruCommand(37, 17, 2, 0, 0);
957 int tt = (CustomNSSlewRate == -1 ?
SlewRate : CustomNSSlewRate);
962 PassthruCommand(37, 17, 2, tt, 0);
966 PassthruCommand(36, 17, 2, tt, 0);
980 PassthruCommand(37, 16, 2, 0, 0);
984 int tt = (CustomWESlewRate == -1 ?
SlewRate : CustomWESlewRate);
989 PassthruCommand(36, 16, 2, tt, 0);
993 PassthruCommand(37, 16, 2, tt, 0);
1006int SynscanLegacyDriver::PassthruCommand(
int cmd,
int target,
int msgsize,
int data,
int numReturn)
1008 char test[20] = {0};
1009 int bytesRead, bytesWritten;
1020 memset(test, 0, 20);
1028 test[7] = numReturn;
1032 memset(test, 0, 20);
1041 retval = retval << 8;
1046 retval = retval << 8;
1062 time_t now = time (
nullptr);
1063 strftime(timeString,
MAXINDINAME,
"%T", gmtime(&now));
1071 char res[MAX_SYN_BUF] = {0};
1072 int bytesWritten = 0, bytesRead = 0;
1084 ln_zonedate localTime;
1086 int offset, daylightflag;
1088 localTime.hours = res[0];
1089 localTime.minutes = res[1];
1090 localTime.seconds = res[2];
1091 localTime.months = res[3];
1092 localTime.days = res[4];
1093 localTime.years = res[5];
1094 offset = (int)res[6];
1098 localTime.gmtoff = offset;
1100 daylightflag = res[7];
1101 localTime.years += 2000;
1102 localTime.gmtoff *= 3600;
1104 ln_zonedate_to_date(&localTime, &utcTime);
1110 sec = (int)utcTime.seconds;
1111 sprintf(utc,
"%04d-%02d-%dT%d:%02d:%02d", utcTime.years, utcTime.months, utcTime.days, utcTime.hours,
1112 utcTime.minutes, sec);
1113 if (daylightflag == 1)
1114 offset = offset + 1;
1115 snprintf(ofs, 16,
"%d", offset);
1122 LOGF_INFO(
"Mount UTC Time %s Offset %d", utc, offset);
1142 char res[MAX_SYN_BUF] = {0};
1143 int bytesWritten = 0, bytesRead = 0;
1154 LOG_WARN(
"Bad echo in ReadLocation");
1170 int a, b, c, d, e, f, g, h;
1180 LOGF_DEBUG(
"Pos %d:%d:%d %d:%d:%d", a, b, c, e, f, g);
1208 char LongitudeStr[32] = {0}, LatitudeStr[32] = {0};
1209 fs_sexa(LongitudeStr, lon, 2, 3600);
1210 fs_sexa(LatitudeStr, lat, 2, 3600);
1211 LOGF_INFO(
"Mount Longitude %s Latitude %s", LongitudeStr, LatitudeStr);
1220 LOG_INFO(
"Mount does not support setting location.");
1228 char res[MAX_SYN_BUF] = {0};
1229 int bytesWritten = 0, bytesRead = 0;
1233 struct ln_zonedate ltm;
1235 ln_date_to_zonedate(utc, <m, (
long)utc_offset * 3600.0);
1243 res[2] = ltm.minutes;
1244 res[3] = (char)(
int)ltm.seconds;
1245 res[4] = ltm.months;
1250 res[7] = (char)(
int)utc_offset;
1255 LOGF_INFO(
"Setting mount date/time to %04d-%02d-%02d %d:%02d:%02d UTC Offset: %d",
1256 ltm.years, ltm.months, ltm.days, ltm.hours, ltm.minutes, ltm.seconds, utc_offset);
1269 LOG_INFO(
"Invalid return from set time");
1278 char res[MAX_SYN_BUF] = {0};
1279 int bytesWritten = 0, bytesRead = 0;
1281 bool IsWest =
false;
1284 ln_lnlat_posn p1 { 0, 0 };
1303 if (longitude > 180)
1305 p1.lng = 360.0 - longitude;
1313 ln_lnlat_to_hlnlat(&p1, &p2);
1314 LOGF_INFO(
"Update location to latitude %d:%d:%1.2f longitude %d:%d:%1.2f",
1315 p2.lat.degrees, p2.lat.minutes, p2.lat.seconds, p2.lng.degrees, p2.lng.minutes, p2.lng.seconds);
1318 res[1] = p2.lat.degrees;
1319 res[2] = p2.lat.minutes;
1320 tmp = p2.lat.seconds + 0.5;
1323 if (p2.lat.neg == 0)
1332 res[5] = p2.lng.degrees;
1333 res[6] = p2.lng.minutes;
1334 s = (int)(p2.lng.seconds + 0.5);
1351 LOG_INFO(
"Invalid response for location setting");
1388 char res[MAX_SYN_BUF] = {0};
1389 int bytesWritten, bytesRead;
1404 LOGF_DEBUG(
"Sync - ra: %g de: %g to az: %g alt: %g",
ra,
dec, TargetAltAz.azimuth, TargetAltAz.altitude);
1406 int Az = (int)(TargetAltAz.azimuth * 16777216 / 360);
1412 *
reinterpret_cast<unsigned char*
>(&res[4]) = (
unsigned char)(Az / 65536);
1413 Az -= (Az / 65536) * 65536;
1414 *
reinterpret_cast<unsigned char*
>(&res[5]) = (
unsigned char)(Az / 256);
1415 Az -= (Az / 256) * 256;
1416 *
reinterpret_cast<unsigned char*
>(&res[6]) = (
unsigned char)Az;
1421 int Alt = (int)(TargetAltAz.altitude * 16777216 / 360);
1427 *
reinterpret_cast<unsigned char*
>(&res[4]) = (
unsigned char)(Alt / 65536);
1428 Alt -= (Alt / 65536) * 65536;
1429 *
reinterpret_cast<unsigned char*
>(&res[5]) = (
unsigned char)(Alt / 256);
1430 Alt -= (Alt / 256) * 256;
1431 *
reinterpret_cast<unsigned char*
>(&res[6]) = (
unsigned char)Alt;
1443 epochPos.declination =
dec;
1450 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
1451 int n2 = J2000Pos.declination * 0x1000000 / 360;
1455 snprintf(res, MAX_SYN_BUF,
"s%08X,%08X", n1, n2);
1456 memset(&res[18], 0, 1);
1464 if (bytesRead != 1 || res[0] !=
'#')
1466 LOG_DEBUG(
"Timeout waiting for scope to complete syncing.");
1471 if (IsTrackingBeforeSync)
1477void SynscanLegacyDriver::UpdateMountInformation(
bool inform_client)
1479 bool BasicMountInfoHasChanged =
false;
1480 std::string MountCodeStr = std::to_string(
MountCode);
1485 BasicMountInfoHasChanged =
true;
1487 if (std::string(BasicMountInfoT[MI_MOUNT_CODE].text) != MountCodeStr)
1489 IUSaveText(&BasicMountInfoT[MI_MOUNT_CODE], MountCodeStr.c_str());
1490 BasicMountInfoHasChanged =
true;
1493 if (std::string(BasicMountInfoT[MI_ALIGN_STATUS].text) !=
AlignmentStatus)
1496 BasicMountInfoHasChanged =
true;
1498 if (std::string(BasicMountInfoT[MI_GOTO_STATUS].text) !=
GotoStatus)
1501 BasicMountInfoHasChanged =
true;
1503 if (std::string(BasicMountInfoT[MI_POINT_STATUS].text) !=
PointingStatus)
1506 BasicMountInfoHasChanged =
true;
1508 if (std::string(BasicMountInfoT[MI_TRACK_MODE].text) !=
TrackingMode)
1511 BasicMountInfoHasChanged =
true;
1514 if (BasicMountInfoHasChanged && inform_client)
1518void SynscanLegacyDriver::MountSim()
1520 static struct timeval ltv;
1526 gettimeofday(&tv,
nullptr);
1528 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1531 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1534 da = currentSlewRate * dt;
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
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...
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.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
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...
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
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
@ TELESCOPE_HAS_PIER_SIDE
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.
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch SlewRateSP
INDI::PropertyText TimeTP
INDI::PropertySwitch MountTypeSP
INDI::PropertyNumber LocationNP
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 sidereal time given longitude and system clock.
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 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,...)
#define TRACKRATE_SIDEREAL
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,...
#define SYNSCAN_SLEW_RATES