#if 0 LX200 Generic Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #endif #include "config.h" #include #include #include #include #include #include #include #include "indicom.h" #include "lx200driver.h" #include "lx200gps.h" #include "lx200classic.h" /* ** Return the timezone offset in hours (as a double, so fractional ** hours are possible, for instance in Newfoundland). Also sets ** daylight on non-Linux systems to record whether DST is in effect. */ #if !(TIMEZONE_IS_INT) //static int daylight = 0; int daylight = 0; #endif static inline double timezoneOffset() { /* ** In Linux, there's a timezone variable that holds the timezone offset; ** Otherwise, we need to make a little detour. The directions of the offset ** are different: CET is -3600 in Linux and +3600 elsewhere. */ #if TIMEZONE_IS_INT return timezone / (60 * 60); #else time_t now; struct tm *tm; now = time(NULL); tm = localtime(&now); daylight = tm->tm_isdst; return -(tm->tm_gmtoff) / (60 * 60); #endif } LX200Generic *telescope = NULL; int MaxReticleFlashRate = 3; /* There is _one_ binary for all LX200 drivers, but each binary is renamed ** to its device name (i.e. lx200gps, lx200_16..etc). The main function will ** fetch from std args the binary name and ISInit will create the apporpiate ** device afterwards. If the binary name does not match any known devices, ** we simply create a generic device */ extern char* me; #define COMM_GROUP "Communication" #define BASIC_GROUP "Main Control" #define MOVE_GROUP "Movement Control" #define DATETIME_GROUP "Date/Time" #define SITE_GROUP "Site Management" #define FOCUS_GROUP "Focus Control" #define RA_THRESHOLD 0.01 #define DEC_THRESHOLD 0.05 #define LX200_SLEW 0 #define LX200_TRACK 1 #define LX200_SYNC 2 #define LX200_PARK 3 static void ISPoll(void *); static void retryConnection(void *); /*INDI controls */ static ISwitch PowerS[] = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}}; static ISwitch AlignmentS [] = {{"Polar", "", ISS_ON, 0, 0}, {"AltAz", "", ISS_OFF, 0, 0}, {"Land", "", ISS_OFF, 0, 0}}; static ISwitch SitesS[] = {{"Site 1", "", ISS_ON, 0, 0}, {"Site 2", "", ISS_OFF, 0, 0}, {"Site 3", "", ISS_OFF, 0, 0}, {"Site 4", "", ISS_OFF, 0 ,0}}; static ISwitch SlewModeS[] = {{"Max", "", ISS_ON, 0, 0}, {"Find", "", ISS_OFF, 0, 0}, {"Centering", "", ISS_OFF, 0, 0}, {"Guide", "", ISS_OFF, 0 , 0}}; static ISwitch OnCoordSetS[] = {{"SLEW", "Slew", ISS_ON, 0, 0 }, {"TRACK", "Track", ISS_OFF, 0, 0}, {"SYNC", "Sync", ISS_OFF, 0 , 0}}; static ISwitch TrackModeS[] = {{ "Default", "", ISS_ON, 0, 0} , { "Lunar", "", ISS_OFF, 0, 0}, {"Manual", "", ISS_OFF, 0, 0}}; static ISwitch abortSlewS[] = {{"ABORT", "Abort", ISS_OFF, 0, 0 }}; static ISwitch ParkS[] = { {"PARK", "Park", ISS_OFF, 0, 0} }; static ISwitch MovementS[] = {{"N", "North", ISS_OFF, 0, 0}, {"W", "West", ISS_OFF, 0, 0}, {"E", "East", ISS_OFF, 0, 0}, {"S", "South", ISS_OFF, 0, 0}}; static INumber FocusSpeedN[] = {{"SPEED", "Speed", "%0.f", 0., 3., 1., 0.}}; static ISwitch FocusMotionS[] = { {"IN", "Focus in", ISS_OFF, 0, 0}, {"OUT", "Focus out", ISS_OFF, 0, 0}}; static INumber FocusTimerN[] = { {"TIMER", "Timer (s)", "%10.6m", 0., 120., 1., 0., 0, 0, 0 }}; static INumberVectorProperty FocusTimerNP = { mydev, "FOCUS_TIMER", "Focus Timer", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusTimerN, NARRAY(FocusTimerN), "", 0}; /* equatorial position */ INumber eq[] = { {"RA", "RA H:M:S", "%10.6m", 0., 24., 0., 0., 0, 0, 0}, {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0}, }; //TODO decide appropiate TIME_OUT // N.B. No Static identifier as it is needed for external linkage INumberVectorProperty eqNum = { mydev, "EQUATORIAL_EOD_COORD", "Equatorial JNow", BASIC_GROUP, IP_RW, 0, IPS_IDLE, eq, NARRAY(eq), "", 0}; /* Fundamental group */ ISwitchVectorProperty PowerSP = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0}; static IText PortT[] = {{"PORT", "Port", 0, 0, 0, 0}}; static ITextVectorProperty Port = { mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_IDLE, PortT, NARRAY(PortT), "", 0}; /* Basic data group */ static ISwitchVectorProperty AlignmentSw = { mydev, "Alignment", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, AlignmentS, NARRAY(AlignmentS), "", 0}; /* Movement group */ static ISwitchVectorProperty OnCoordSetSw = { mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, OnCoordSetS, NARRAY(OnCoordSetS), "", 0}; static ISwitchVectorProperty abortSlewSw = { mydev, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, abortSlewS, NARRAY(abortSlewS), "", 0}; ISwitchVectorProperty ParkSP = {mydev, "PARK", "Park Scope", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, ParkS, NARRAY(ParkS), "", 0 }; static ISwitchVectorProperty SlewModeSw = { mydev, "Slew rate", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SlewModeS, NARRAY(SlewModeS), "", 0}; static ISwitchVectorProperty TrackModeSw = { mydev, "Tracking Mode", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, TrackModeS, NARRAY(TrackModeS), "", 0}; static INumber TrackFreq[] = {{ "trackFreq", "Freq", "%g", 56.4, 60.1, 0.1, 60.1, 0, 0, 0}}; static INumberVectorProperty TrackingFreq= { mydev, "Tracking Frequency", "", MOVE_GROUP, IP_RW, 0, IPS_IDLE, TrackFreq, NARRAY(TrackFreq), "", 0}; static ISwitchVectorProperty MovementSw = { mydev, "MOVEMENT", "Move toward", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, MovementS, NARRAY(MovementS), "", 0}; // Focus Control static INumberVectorProperty FocusSpeedNP = {mydev, "FOCUS_SPEED", "Speed", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusSpeedN, NARRAY(FocusSpeedN), "", 0}; static ISwitchVectorProperty FocusMotionSw = {mydev, "FOCUS_MOTION", "Motion", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusMotionS, NARRAY(FocusMotionS), "", 0}; /* Data & Time */ static IText UTC[] = {{"UTC", "UTC", 0, 0, 0, 0}}; ITextVectorProperty Time = { mydev, "TIME", "UTC Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, UTC, NARRAY(UTC), "", 0}; static INumber STime[] = {{"LST", "Sidereal time", "%10.6m" , 0.,24.,0.,0., 0, 0, 0}}; INumberVectorProperty SDTime = { mydev, "SDTIME", "Sidereal Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, STime, NARRAY(STime), "", 0}; /* Site managment */ static ISwitchVectorProperty SitesSw = { mydev, "Sites", "", SITE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SitesS, NARRAY(SitesS), "", 0}; /* geographic location */ static INumber geo[] = { {"LAT", "Lat. D:M:S +N", "%10.6m", -90., 90., 0., 0., 0, 0, 0}, {"LONG", "Long. D:M:S +E", "%10.6m", 0., 360., 0., 0., 0, 0, 0}, }; static INumberVectorProperty geoNum = { mydev, "GEOGRAPHIC_COORD", "Geographic Location", SITE_GROUP, IP_RW, 0., IPS_IDLE, geo, NARRAY(geo), "", 0}; static IText SiteNameT[] = {{"SiteName", "", 0, 0, 0, 0}}; static ITextVectorProperty SiteName = { mydev, "Site Name", "", SITE_GROUP, IP_RW, 0 , IPS_IDLE, SiteNameT, NARRAY(SiteNameT), "", 0}; void changeLX200GenericDeviceName(const char * newName) { strcpy(PowerSP.device , newName); strcpy(Port.device , newName); strcpy(AlignmentSw.device, newName); // BASIC_GROUP strcpy(eqNum.device, newName); strcpy(OnCoordSetSw.device , newName ); strcpy(abortSlewSw.device , newName ); strcpy(ParkSP.device, newName); // MOVE_GROUP strcpy(SlewModeSw.device , newName ); strcpy(TrackModeSw.device , newName ); strcpy(TrackingFreq.device , newName ); strcpy(MovementSw.device , newName ); // FOCUS_GROUP strcpy(FocusSpeedNP.device , newName ); strcpy(FocusMotionSw.device , newName ); strcpy(FocusTimerNP.device, newName); // DATETIME_GROUP strcpy(Time.device , newName ); strcpy(SDTime.device , newName ); // SITE_GROUP strcpy(SitesSw.device , newName ); strcpy(SiteName.device , newName ); strcpy(geoNum.device , newName ); } void changeAllDeviceNames(const char *newName) { changeLX200GenericDeviceName(newName); changeLX200AutostarDeviceName(newName); changeLX200_16DeviceName(newName); changeLX200ClassicDeviceName(newName); changeLX200GPSDeviceName(newName); } /* send client definitions of all properties */ void ISInit() { static int isInit=0; if (isInit) return; isInit = 1; PortT[0].text = strcpy(new char[32], "/dev/ttyS0"); UTC[0].text = strcpy(new char[32], "YYYY-MM-DDTHH:MM:SS"); if (strstr(me, "lx200classic")) { fprintf(stderr , "initilizaing from LX200 classic device...\n"); // 1. mydev = device_name changeAllDeviceNames("LX200 Classic"); // 2. device = sub_class telescope = new LX200Classic(); telescope->setCurrentDeviceName("LX200 Classic"); MaxReticleFlashRate = 3; } else if (strstr(me, "lx200gps")) { fprintf(stderr , "initilizaing from LX200 GPS device...\n"); // 1. mydev = device_name changeAllDeviceNames("LX200 GPS"); // 2. device = sub_class telescope = new LX200GPS(); telescope->setCurrentDeviceName("LX200 GPS"); MaxReticleFlashRate = 9; } else if (strstr(me, "lx200_16")) { IDLog("Initilizaing from LX200 16 device...\n"); // 1. mydev = device_name changeAllDeviceNames("LX200 16"); // 2. device = sub_class telescope = new LX200_16(); telescope->setCurrentDeviceName("LX200 16"); MaxReticleFlashRate = 3; } else if (strstr(me, "lx200autostar")) { fprintf(stderr , "initilizaing from autostar device...\n"); // 1. change device name changeAllDeviceNames("LX200 Autostar"); // 2. device = sub_class telescope = new LX200Autostar(); telescope->setCurrentDeviceName("LX200 Autostar"); MaxReticleFlashRate = 9; } // be nice and give them a generic device else { telescope = new LX200Generic(); telescope->setCurrentDeviceName("LX200 Generic"); } } void ISGetProperties (const char *dev) { ISInit(); telescope->ISGetProperties(dev); IEAddTimer (POLLMS, ISPoll, NULL);} void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) { ISInit(); telescope->ISNewSwitch(dev, name, states, names, n);} void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n) { ISInit(); telescope->ISNewText(dev, name, texts, names, n);} void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) { ISInit(); telescope->ISNewNumber(dev, name, values, names, n);} void ISPoll (void *p) { telescope->ISPoll(); IEAddTimer (POLLMS, ISPoll, NULL); p=p;} void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/) {} /************************************************** *** LX200 Generic Implementation ***************************************************/ LX200Generic::LX200Generic() { struct tm *utp; time_t t; time (&t); utp = gmtime (&t); currentSiteNum = 1; trackingMode = LX200_TRACK_DEFAULT; lastSet = -1; fault = false; simulation = false; targetRA = 0; targetDEC = 0; currentRA = 0; currentDEC = 0; currentSet = 0; UTCOffset = 0; lastMove[0] = lastMove[1] = lastMove[2] = lastMove[3] = 0; localTM = new tm; utp->tm_mon += 1; utp->tm_year += 1900; JD = UTtoJD(utp); IDLog("Julian Day is %g\n", JD); // Children call tqparent routines, this is the default IDLog("initilizaing from generic LX200 device...\n"); IDLog("INDI Version: 2004-02-17\n"); //enableSimulation(true); } void LX200Generic::setCurrentDeviceName(const char * devName) { strcpy(thisDevice, devName); } void LX200Generic::ISGetProperties(const char *dev) { if (dev && strcmp (thisDevice, dev)) return; // COMM_GROUP IDDefSwitch (&PowerSP, NULL); IDDefText (&Port, NULL); IDDefSwitch (&AlignmentSw, NULL); // BASIC_GROUP IDDefNumber (&eqNum, NULL); IDDefSwitch (&OnCoordSetSw, NULL); IDDefSwitch (&abortSlewSw, NULL); IDDefSwitch (&ParkSP, NULL); // MOVE_GROUP IDDefNumber (&TrackingFreq, NULL); IDDefSwitch (&SlewModeSw, NULL); IDDefSwitch (&TrackModeSw, NULL); IDDefSwitch (&MovementSw, NULL); // FOCUS_GROUP IDDefNumber(&FocusSpeedNP, NULL); IDDefSwitch(&FocusMotionSw, NULL); IDDefNumber(&FocusTimerNP, NULL); // DATETIME_GROUP IDDefText (&Time, NULL); IDDefNumber (&SDTime, NULL); // SITE_GROUP IDDefSwitch (&SitesSw, NULL); IDDefText (&SiteName, NULL); IDDefNumber (&geoNum, NULL); /* Send the basic data to the new client if the previous client(s) are already connected. */ if (PowerSP.s == IPS_OK) getBasicData(); } void LX200Generic::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n) { int err; struct tm *ltp = new tm; struct tm utm; time_t ltime; time (<ime); localtime_r (<ime, ltp); IText *tp; // ignore if not ours // if (strcmp (dev, thisDevice)) return; // suppress warning n=n; if (!strcmp(name, Port.name) ) { Port.s = IPS_OK; tp = IUFindText( &Port, names[0] ); if (!tp) return; tp->text = new char[strlen(texts[0])+1]; strcpy(tp->text, texts[0]); IDSetText (&Port, NULL); return; } if (!strcmp (name, SiteName.name) ) { if (checkPower(&SiteName)) return; if ( ( err = setSiteName(texts[0], currentSiteNum) < 0) ) { handleError(&SiteName, err, "Setting site name"); return; } SiteName.s = IPS_OK; tp = IUFindText(&SiteName, names[0]); tp->text = new char[strlen(texts[0])+1]; strcpy(tp->text, texts[0]); IDSetText(&SiteName , "Site name updated"); return; } if (!strcmp (name, Time.name)) { if (checkPower(&Time)) return; if (extractISOTime(texts[0], &utm) < 0) { Time.s = IPS_IDLE; IDSetText(&Time , "Time invalid"); return; } ltp->tm_mon += 1; ltp->tm_year += 1900; /*dayDiff = utm.tm_mday - ltp->tm_mday; if (dayDiff == 0) UTCOffset = (ltp->tm_hour - utm.tm_hour); else if (dayDiff > 0) UTCOffset = ltp->tm_hour - utm.tm_hour - 24; else UTCOffset = ltp->tm_hour - utm.tm_hour + 24;*/ tzset(); UTCOffset = timezoneOffset(); IDLog("local time is %02d:%02d:%02d\nUTCOffset: %g\n", ltp->tm_hour, ltp->tm_min, ltp->tm_sec, UTCOffset); getSDTime(&STime[0].value); IDSetNumber(&SDTime, NULL); if ( ( err = setUTCOffset(UTCOffset) < 0) ) { Time.s = IPS_IDLE; IDSetText( &Time , "Setting UTC Offset failed."); return; } if ( ( err = setLocalTime(ltp->tm_hour, ltp->tm_min, ltp->tm_sec) < 0) ) { handleError(&Time, err, "Setting local time"); return; } tp = IUFindText(&Time, names[0]); if (!tp) return; tp->text = new char[strlen(texts[0])+1]; strcpy(tp->text, texts[0]); Time.s = IPS_OK; // update JD JD = UTtoJD(&utm); utm.tm_mon += 1; utm.tm_year += 1900; IDLog("New JD is %f\n", (float) JD); if ((localTM->tm_mday == ltp->tm_mday ) && (localTM->tm_mon == ltp->tm_mon) && (localTM->tm_year == ltp->tm_year)) { IDSetText(&Time , "Time updated to %s", texts[0]); return; } localTM = ltp; if (!strcmp(dev, "LX200 GPS")) { if ( ( err = setCalenderDate(utm.tm_mday, utm.tm_mon, utm.tm_year) < 0) ) { handleError(&Time, err, "Setting UTC date."); return; } } else { if ( ( err = setCalenderDate(ltp->tm_mday, ltp->tm_mon, ltp->tm_year) < 0) ) { handleError(&Time, err, "Setting local date."); return; } } IDSetText(&Time , "Date changed, updating planetary data..."); } } void LX200Generic::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) { int h =0, m =0, s=0, err; double newRA =0, newDEC =0; // ignore if not ours // if (strcmp (dev, thisDevice)) return; if (!strcmp (name, eqNum.name)) { int i=0, nset=0; if (checkPower(&eqNum)) return; for (nset = i = 0; i < n; i++) { INumber *eqp = IUFindNumber (&eqNum, names[i]); if (eqp == &eq[0]) { newRA = values[i]; nset += newRA >= 0 && newRA <= 24.0; } else if (eqp == &eq[1]) { newDEC = values[i]; nset += newDEC >= -90.0 && newDEC <= 90.0; } } if (nset == 2) { /*eqNum.s = IPS_BUSY;*/ char RAStr[32], DecStr[32]; fs_sexa(RAStr, newRA, 2, 3600); fs_sexa(DecStr, newDEC, 2, 3600); IDLog("We received J2000 RA %g - DEC %g\n", newRA, newDEC); IDLog("We received J2000 RA %s - DEC %s\n", RAStr, DecStr); /*aptqparentCoord( (double) J2000, JD, &newRA, &newDEC); fs_sexa(RAStr, newRA, 2, 3600); fs_sexa(DecStr, newDEC, 2, 3600); IDLog("Processed to JNow RA %f - DEC %f\n", newRA, newDEC); IDLog("Processed to JNow RA %s - DEC %s\n", RAStr, DecStr);*/ if ( (err = setObjectRA(newRA)) < 0 || ( err = setObjectDEC(newDEC)) < 0) { handleError(&eqNum, err, "Setting RA/DEC"); return; } /*eqNum.s = IPS_BUSY;*/ targetRA = newRA; targetDEC = newDEC; if (MovementSw.s == IPS_BUSY) { for (int i=0; i < 4; i++) { lastMove[i] = 0; MovementS[i].s = ISS_OFF; } MovementSw.s = IPS_IDLE; IDSetSwitch(&MovementSw, NULL); } if (handleCoordSet()) { eqNum.s = IPS_IDLE; IDSetNumber(&eqNum, NULL); } } // end nset else { eqNum.s = IPS_IDLE; IDSetNumber(&eqNum, "RA or Dec missing or invalid"); } return; } /* end eqNum */ if ( !strcmp (name, SDTime.name) ) { if (checkPower(&SDTime)) return; if (values[0] < 0.0 || values[0] > 24.0) { SDTime.s = IPS_IDLE; IDSetNumber(&SDTime , "Time invalid"); return; } getSexComponents(values[0], &h, &m, &s); IDLog("Time is %02d:%02d:%02d\n", h, m, s); if ( ( err = setSDTime(h, m, s) < 0) ) { handleError(&SDTime, err, "Setting siderial time"); return; } SDTime.np[0].value = values[0]; SDTime.s = IPS_OK; IDSetNumber(&SDTime , "Sidereal time updated to %02d:%02d:%02d", h, m, s); return; } if (!strcmp (name, geoNum.name)) { // new geographic coords double newLong = 0, newLat = 0; int i, nset; char msg[128]; if (checkPower(&geoNum)) return; for (nset = i = 0; i < n; i++) { INumber *geop = IUFindNumber (&geoNum, names[i]); if (geop == &geo[0]) { newLat = values[i]; nset += newLat >= -90.0 && newLat <= 90.0; } else if (geop == &geo[1]) { newLong = values[i]; nset += newLong >= 0.0 && newLong < 360.0; } } if (nset == 2) { char l[32], L[32]; geoNum.s = IPS_OK; fs_sexa (l, newLat, 3, 3600); fs_sexa (L, newLong, 4, 3600); if ( ( err = setSiteLongitude(360.0 - newLong) < 0) ) { handleError(&geoNum, err, "Setting site coordinates"); return; } setSiteLatitude(newLat); geoNum.np[0].value = newLat; geoNum.np[1].value = newLong; snprintf (msg, sizeof(msg), "Site location updated to Lat %.32s - Long %.32s", l, L); } else { geoNum.s = IPS_IDLE; strcpy(msg, "Lat or Long missing or invalid"); } IDSetNumber (&geoNum, "%s", msg); return; } if ( !strcmp (name, TrackingFreq.name) ) { if (checkPower(&TrackingFreq)) return; IDLog("Trying to set track freq of: %f\n", values[0]); if ( ( err = setTrackFreq(values[0])) < 0) { handleError(&TrackingFreq, err, "Setting tracking frequency"); return; } TrackingFreq.s = IPS_OK; TrackingFreq.np[0].value = values[0]; IDSetNumber(&TrackingFreq, "Tracking frequency set to %04.1f", values[0]); if (trackingMode != LX200_TRACK_MANUAL) { trackingMode = LX200_TRACK_MANUAL; TrackModeS[0].s = ISS_OFF; TrackModeS[1].s = ISS_OFF; TrackModeS[2].s = ISS_ON; TrackModeSw.s = IPS_OK; selectTrackingMode(trackingMode); IDSetSwitch(&TrackModeSw, NULL); } return; } if (!strcmp(name, FocusTimerNP.name)) { if (checkPower(&FocusTimerNP)) return; // Don't update if busy if (FocusTimerNP.s == IPS_BUSY) return; IUUpdateNumbers(&FocusTimerNP, values, names, n); FocusTimerNP.s = IPS_OK; IDSetNumber(&FocusTimerNP, NULL); IDLog("Setting focus timer to %g\n", FocusTimerN[0].value); return; } // Focus speed if (!strcmp (name, FocusSpeedNP.name)) { if (checkPower(&FocusSpeedNP)) return; if (IUUpdateNumbers(&FocusSpeedNP, values, names, n) < 0) return; /* disable timer and motion */ if (FocusSpeedN[0].value == 0) { IUResetSwitches(&FocusMotionSw); FocusMotionSw.s = IPS_IDLE; FocusTimerNP.s = IPS_IDLE; IDSetSwitch(&FocusMotionSw, NULL); IDSetNumber(&FocusTimerNP, NULL); } setFocuserSpeedMode( ( (int) FocusSpeedN[0].value)); FocusSpeedNP.s = IPS_OK; IDSetNumber(&FocusSpeedNP, NULL); return; } } void LX200Generic::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) { int index; int dd, mm, err; char combinedDir[64]; ISwitch *swp; // suppress warning names = names; //IDLog("in new Switch with Device= %s and Property= %s and #%d items\n", dev, name,n); //IDLog("SolarSw name is %s\n", SolarSw.name); //IDLog("The device name is %s\n", dev); // ignore if not ours // if (strcmp (thisDevice, dev)) return; // FIRST Switch ALWAYS for power if (!strcmp (name, PowerSP.name)) { IUResetSwitches(&PowerSP); IUUpdateSwitches(&PowerSP, states, names, n); powerTelescope(); return; } // Coord set if (!strcmp(name, OnCoordSetSw.name)) { if (checkPower(&OnCoordSetSw)) return; IUResetSwitches(&OnCoordSetSw); IUUpdateSwitches(&OnCoordSetSw, states, names, n); currentSet = getOnSwitch(&OnCoordSetSw); OnCoordSetSw.s = IPS_OK; IDSetSwitch(&OnCoordSetSw, NULL); } // Parking if (!strcmp(name, ParkSP.name)) { if (checkPower(&ParkSP)) return; ParkSP.s = IPS_IDLE; if ( (err = getSDTime(&STime[0].value)) < 0) { handleError(&ParkSP, err, "Get siderial time"); return; } if (AlignmentS[0].s == ISS_ON) { targetRA = STime[0].value; targetDEC = 0; setObjectRA(targetRA); setObjectDEC(targetDEC); } else if (AlignmentS[1].s == ISS_ON) { targetRA = calculateRA(STime[0].value); targetDEC = calculateDec(geo[0].value, STime[0].value); setObjectRA(targetRA); setObjectDEC(targetDEC); IDLog("Parking the scope in AltAz (0,0) which corresponds to (RA,DEC) of (%g,%g)\n", targetRA, targetDEC); IDLog("Current Sidereal time is: %g\n", STime[0].value); IDSetNumber(&SDTime, NULL); } else { IDSetSwitch(&ParkSP, "You can only park the telescope in Polar or AltAz modes."); return; } IDSetNumber(&SDTime, NULL); currentSet = LX200_PARK; handleCoordSet(); } // Abort Slew if (!strcmp (name, abortSlewSw.name)) { if (checkPower(&abortSlewSw)) { abortSlewSw.s = IPS_IDLE; IDSetSwitch(&abortSlewSw, NULL); return; } IUResetSwitches(&abortSlewSw); abortSlew(); if (eqNum.s == IPS_BUSY) { abortSlewSw.s = IPS_OK; eqNum.s = IPS_IDLE; IDSetSwitch(&abortSlewSw, "Slew aborted."); IDSetNumber(&eqNum, NULL); } else if (MovementSw.s == IPS_BUSY) { for (int i=0; i < 4; i++) lastMove[i] = 0; MovementSw.s = IPS_IDLE; abortSlewSw.s = IPS_OK; eqNum.s = IPS_IDLE; IUResetSwitches(&MovementSw); IUResetSwitches(&abortSlewSw); IDSetSwitch(&abortSlewSw, "Slew aborted."); IDSetSwitch(&MovementSw, NULL); IDSetNumber(&eqNum, NULL); } else { IUResetSwitches(&MovementSw); abortSlewSw.s = IPS_OK; IDSetSwitch(&abortSlewSw, NULL); } return; } // Alignment if (!strcmp (name, AlignmentSw.name)) { if (checkPower(&AlignmentSw)) return; IUResetSwitches(&AlignmentSw); IUUpdateSwitches(&AlignmentSw, states, names, n); index = getOnSwitch(&AlignmentSw); if ( ( err = tqsetAlignmentMode(index) < 0) ) { handleError(&AlignmentSw, err, "Setting tqalignment"); return; } AlignmentSw.s = IPS_OK; IDSetSwitch (&AlignmentSw, NULL); return; } // Sites if (!strcmp (name, SitesSw.name)) { if (checkPower(&SitesSw)) return; IUResetSwitches(&SitesSw); IUUpdateSwitches(&SitesSw, states, names, n); currentSiteNum = getOnSwitch(&SitesSw) + 1; if ( ( err = selectSite(currentSiteNum) < 0) ) { handleError(&SitesSw, err, "Selecting sites"); return; } if ( ( err = getSiteLatitude(&dd, &mm) < 0)) { handleError(&SitesSw, err, "Selecting sites"); return; } if (dd > 0) geoNum.np[0].value = dd + mm / 60.0; else geoNum.np[0].value = dd - mm / 60.0; if ( ( err = getSiteLongitude(&dd, &mm) < 0)) { handleError(&SitesSw, err, "Selecting sites"); return; } if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm / 60.0); else geoNum.np[1].value = (dd - mm / 60.0) * -1.0; getSiteName( SiteName.tp[0].text, currentSiteNum); IDLog("Selecting site %d\n", currentSiteNum); geoNum.s = SiteName.s = SitesSw.s = IPS_OK; IDSetNumber (&geoNum, NULL); IDSetText (&SiteName, NULL); IDSetSwitch (&SitesSw, NULL); return; } // Focus Motion if (!strcmp (name, FocusMotionSw.name)) { if (checkPower(&FocusMotionSw)) return; IUResetSwitches(&FocusMotionSw); // If speed is "halt" if (FocusSpeedN[0].value == 0) { FocusMotionSw.s = IPS_IDLE; IDSetSwitch(&FocusMotionSw, NULL); return; } IUUpdateSwitches(&FocusMotionSw, states, names, n); index = getOnSwitch(&FocusMotionSw); if ( ( err = setFocuserMotion(index) < 0) ) { handleError(&FocusMotionSw, err, "Setting focuser speed"); return; } FocusMotionSw.s = IPS_BUSY; // with a timer if (FocusTimerN[0].value > 0) FocusTimerNP.s = IPS_BUSY; IDSetSwitch(&FocusMotionSw, NULL); return; } // Slew mode if (!strcmp (name, SlewModeSw.name)) { if (checkPower(&SlewModeSw)) return; IUResetSwitches(&SlewModeSw); IUUpdateSwitches(&SlewModeSw, states, names, n); index = getOnSwitch(&SlewModeSw); if ( ( err = setSlewMode(index) < 0) ) { handleError(&SlewModeSw, err, "Setting slew mode"); return; } SlewModeSw.s = IPS_OK; IDSetSwitch(&SlewModeSw, NULL); return; } // Movement if (!strcmp (name, MovementSw.name)) { if (checkPower(&MovementSw)) return; index = -1; IUUpdateSwitches(&MovementSw, states, names, n); swp = IUFindSwitch(&MovementSw, names[0]); if (!swp) { abortSlew(); IUResetSwitches(&MovementSw); MovementSw.s = IPS_IDLE; IDSetSwitch(&MovementSw, NULL); } if (swp == &MovementS[0]) index = 0; else if (swp == &MovementS[1]) index = 1; else if (swp == &MovementS[2]) index = 2; else index = 3; lastMove[index] = lastMove[index] == 0 ? 1 : 0; if (lastMove[index] == 0) MovementS[index].s = ISS_OFF; // North/South movement is illegal if (lastMove[LX200_NORTH] && lastMove[LX200_SOUTH]) { abortSlew(); for (int i=0; i < 4; i++) lastMove[i] = 0; IUResetSwitches(&MovementSw); MovementSw.s = IPS_IDLE; IDSetSwitch(&MovementSw, "Slew aborted."); return; } // East/West movement is illegal if (lastMove[LX200_EAST] && lastMove[LX200_WEST]) { abortSlew(); for (int i=0; i < 4; i++) lastMove[i] = 0; IUResetSwitches(&MovementSw); MovementSw.s = IPS_IDLE; IDSetSwitch(&MovementSw, "Slew aborted."); return; } IDLog("We have switch %d \n ", index); IDLog("NORTH: %d -- WEST: %d -- EAST: %d -- SOUTH %d\n", lastMove[0], lastMove[1], lastMove[2], lastMove[3]); if (lastMove[index] == 1) { IDLog("issuing a move command\n"); if ( ( err = MoveTo(index) < 0) ) { handleError(&MovementSw, err, "Setting motion direction"); return; } } else HaltMovement(index); if (!lastMove[0] && !lastMove[1] && !lastMove[2] && !lastMove[3]) MovementSw.s = IPS_IDLE; if (lastMove[index] == 0) IDSetSwitch(&MovementSw, "Moving toward %s aborted.", Direction[index]); else { MovementSw.s = IPS_BUSY; if (lastMove[LX200_NORTH] && lastMove[LX200_WEST]) strcpy(combinedDir, "North West"); else if (lastMove[LX200_NORTH] && lastMove[LX200_EAST]) strcpy(combinedDir, "North East"); else if (lastMove[LX200_SOUTH] && lastMove[LX200_WEST]) strcpy(combinedDir, "South West"); else if (lastMove[LX200_SOUTH] && lastMove[LX200_EAST]) strcpy(combinedDir, "South East"); else strcpy(combinedDir, Direction[index]); IDSetSwitch(&MovementSw, "Moving %s...", combinedDir); } return; } // Tracking mode if (!strcmp (name, TrackModeSw.name)) { if (checkPower(&TrackModeSw)) return; IUResetSwitches(&TrackModeSw); IUUpdateSwitches(&TrackModeSw, states, names, n); trackingMode = getOnSwitch(&TrackModeSw); if ( ( err = selectTrackingMode(trackingMode) < 0) ) { handleError(&TrackModeSw, err, "Setting tracking mode."); return; } getTrackFreq(&TrackFreq[0].value); TrackModeSw.s = IPS_OK; IDSetNumber(&TrackingFreq, NULL); IDSetSwitch(&TrackModeSw, NULL); return; } } void LX200Generic::handleError(ISwitchVectorProperty *svp, int err, const char *msg) { svp->s = IPS_ALERT; /* First check to see if the telescope is connected */ if (testTelescope()) { /* The telescope is off locally */ PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; PowerSP.s = IPS_BUSY; IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds."); IDSetSwitch(svp, NULL); IEAddTimer(10000, retryConnection, NULL); return; } /* If the error is a time out, then the device doesn't support this property or busy*/ if (err == -2) { svp->s = IPS_ALERT; IDSetSwitch(svp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg); } else /* Changing property failed, user should retry. */ IDSetSwitch( svp , "%s failed.", msg); fault = true; } void LX200Generic::handleError(INumberVectorProperty *nvp, int err, const char *msg) { nvp->s = IPS_ALERT; /* First check to see if the telescope is connected */ if (testTelescope()) { /* The telescope is off locally */ PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; PowerSP.s = IPS_BUSY; IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds."); IDSetNumber(nvp, NULL); IEAddTimer(10000, retryConnection, NULL); return; } /* If the error is a time out, then the device doesn't support this property */ if (err == -2) { nvp->s = IPS_ALERT; IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg); } else /* Changing property failed, user should retry. */ IDSetNumber( nvp , "%s failed.", msg); fault = true; } void LX200Generic::handleError(ITextVectorProperty *tvp, int err, const char *msg) { tvp->s = IPS_ALERT; /* First check to see if the telescope is connected */ if (testTelescope()) { /* The telescope is off locally */ PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; PowerSP.s = IPS_BUSY; IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds."); IDSetText(tvp, NULL); IEAddTimer(10000, retryConnection, NULL); return; } /* If the error is a time out, then the device doesn't support this property */ if (err == -2) { tvp->s = IPS_ALERT; IDSetText(tvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg); } else /* Changing property failed, user should retry. */ IDSetText( tvp , "%s failed.", msg); fault = true; } void LX200Generic::correctFault() { fault = false; IDMessage(thisDevice, "Telescope is online."); } bool LX200Generic::isTelescopeOn(void) { if (simulation) return true; return (PowerSP.sp[0].s == ISS_ON); } static void retryConnection(void * p) { p=p; if (testTelescope()) { PowerSP.s = IPS_IDLE; IDSetSwitch(&PowerSP, "The connection to the telescope is lost."); return; } PowerS[0].s = ISS_ON; PowerS[1].s = ISS_OFF; PowerSP.s = IPS_OK; IDSetSwitch(&PowerSP, "The connection to the telescope has been resumed."); } void LX200Generic::ISPoll() { double dx, dy; /*static int okCounter = 3;*/ int err=0; if (!isTelescopeOn()) return; switch (eqNum.s) { case IPS_IDLE: getLX200RA(¤tRA); getLX200DEC(¤tDEC); if ( fabs (currentRA - lastRA) > 0.01 || fabs (currentDEC - lastDEC) > 0.01) { eqNum.np[0].value = lastRA = currentRA; eqNum.np[1].value = lastDEC = currentDEC; IDSetNumber (&eqNum, NULL); } break; case IPS_BUSY: getLX200RA(¤tRA); getLX200DEC(¤tDEC); dx = targetRA - currentRA; dy = targetDEC - currentDEC; IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA); IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC); eqNum.np[0].value = currentRA; eqNum.np[1].value = currentDEC; // Wait until acknowledged or within threshold if (fabs(dx) <= RA_THRESHOLD && fabs(dy) <= DEC_THRESHOLD) { /* Don't set current to target. This might leave residual cumulative error currentRA = targetRA; currentDEC = targetDEC; */ eqNum.np[0].value = lastRA = currentRA; eqNum.np[1].value = lastDEC = currentDEC; IUResetSwitches(&OnCoordSetSw); OnCoordSetSw.s = IPS_OK; eqNum.s = IPS_OK; IDSetNumber (&eqNum, NULL); switch (currentSet) { case LX200_SLEW: OnCoordSetSw.sp[0].s = ISS_ON; IDSetSwitch (&OnCoordSetSw, "Slew is complete."); break; case LX200_TRACK: OnCoordSetSw.sp[1].s = ISS_ON; IDSetSwitch (&OnCoordSetSw, "Slew is complete. Tracking..."); break; case LX200_SYNC: break; case LX200_PARK: if (setSlewMode(LX200_SLEW_GUIDE) < 0) { handleError(&eqNum, err, "Setting slew mode"); return; } IUResetSwitches(&SlewModeSw); SlewModeS[LX200_SLEW_GUIDE].s = ISS_ON; IDSetSwitch(&SlewModeSw, NULL); MoveTo(LX200_EAST); IUResetSwitches(&MovementSw); MovementS[LX200_EAST].s = ISS_ON; MovementSw.s = IPS_BUSY; for (int i=0; i < 4; i++) lastMove[i] = 0; lastMove[LX200_EAST] = 1; IDSetSwitch(&MovementSw, NULL); ParkSP.s = IPS_OK; IDSetSwitch (&ParkSP, "Park is complete. Turn off the telescope now."); break; } } else { eqNum.np[0].value = currentRA; eqNum.np[1].value = currentDEC; IDSetNumber (&eqNum, NULL); } break; case IPS_OK: /*if (--okCounter >= 0) break; // Activate again in 3 seconds okCounter = 3;*/ if ( (err = getLX200RA(¤tRA)) < 0 || (err = getLX200DEC(¤tDEC)) < 0) { handleError(&eqNum, err, "Getting RA/DEC"); return; } if (fault) correctFault(); if ( (currentRA != lastRA) || (currentDEC != lastDEC)) { eqNum.np[0].value = lastRA = currentRA; eqNum.np[1].value = lastDEC = currentDEC; IDSetNumber (&eqNum, NULL); } break; case IPS_ALERT: break; } switch (MovementSw.s) { case IPS_IDLE: break; case IPS_BUSY: getLX200RA(¤tRA); getLX200DEC(¤tDEC); /*aptqparentCoord( JD, (double) J2000, ¤tRA, ¤tDEC);*/ eqNum.np[0].value = currentRA; eqNum.np[1].value = currentDEC; IDSetNumber (&eqNum, NULL); break; case IPS_OK: break; case IPS_ALERT: break; } switch (FocusTimerNP.s) { case IPS_IDLE: break; case IPS_BUSY: FocusTimerN[0].value--; if (FocusTimerN[0].value == 0) { if ( ( err = setFocuserSpeedMode(0) < 0) ) { handleError(&FocusSpeedNP, err, "setting focuser speed mode"); IDLog("Error setting focuser speed mode\n"); return; } FocusMotionSw.s = IPS_IDLE; FocusTimerNP.s = IPS_OK; FocusSpeedNP.s = IPS_OK; IUResetSwitches(&FocusMotionSw); FocusSpeedN[0].value = 0; IDSetNumber(&FocusSpeedNP, NULL); IDSetSwitch(&FocusMotionSw, NULL); } IDSetNumber(&FocusTimerNP, NULL); break; case IPS_OK: break; case IPS_ALERT: break; } } void LX200Generic::getBasicData() { int err; struct tm *timep; time_t ut; time (&ut); timep = gmtime (&ut); strftime (Time.tp[0].text, sizeof(Time.tp[0].text), "%Y-%m-%dT%H:%M:%S", timep); IDLog("PC UTC time is %s\n", Time.tp[0].text); getAlignment(); checkLX200Format(); if ( (err = getTimeFormat(&timeFormat)) < 0) IDMessage(thisDevice, "Failed to retrieve time format from device."); else { timeFormat = (timeFormat == 24) ? LX200_24 : LX200_AM; // We always do 24 hours if (timeFormat != LX200_24) toggleTimeFormat(); } getLX200RA(&targetRA); getLX200DEC(&targetDEC); eqNum.np[0].value = targetRA; eqNum.np[1].value = targetDEC; IDSetNumber (&eqNum, NULL); SiteNameT[0].text = new char[64]; if ( (err = getSiteName(SiteNameT[0].text, currentSiteNum)) < 0) IDMessage(thisDevice, "Failed to get site name from device"); else IDSetText (&SiteName, NULL); if ( (err = getTrackFreq(&TrackFreq[0].value)) < 0) IDMessage(thisDevice, "Failed to get tracking frequency from device."); else IDSetNumber (&TrackingFreq, NULL); updateLocation(); updateTime(); } int LX200Generic::handleCoordSet() { int err; char syncString[256]; char RAStr[32], DecStr[32]; double dx, dy; IDLog("In Handle Coord Set()\n"); switch (currentSet) { // Slew case LX200_SLEW: lastSet = LX200_SLEW; if (eqNum.s == IPS_BUSY) { IDLog("Aboring Slew\n"); abortSlew(); // sleep for 100 mseconds usleep(100000); } if ((err = Slew())) { slewError(err); return (-1); } eqNum.s = IPS_BUSY; fs_sexa(RAStr, targetRA, 2, 3600); fs_sexa(DecStr, targetDEC, 2, 3600); IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr); IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr); break; // Track case LX200_TRACK: IDLog("We're in LX200_TRACK\n"); if (eqNum.s == IPS_BUSY) { IDLog("Aboring Slew\n"); abortSlew(); // sleep for 200 mseconds usleep(200000); } dx = fabs ( targetRA - currentRA ); dy = fabs (targetDEC - currentDEC); if (dx >= TRACKING_THRESHOLD || dy >= TRACKING_THRESHOLD) { IDLog("Exceeded Tracking threshold, will attempt to slew to the new target.\n"); IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA); IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC); if ((err = Slew())) { slewError(err); return (-1); } fs_sexa(RAStr, targetRA, 2, 3600); fs_sexa(DecStr, targetDEC, 2, 3600); eqNum.s = IPS_BUSY; IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr); IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr); } else { IDLog("Tracking called, but tracking threshold not reached yet.\n"); eqNum.s = IPS_OK; eqNum.np[0].value = currentRA; eqNum.np[1].value = currentDEC; if (lastSet != LX200_TRACK) IDSetNumber(&eqNum, "Tracking..."); else IDSetNumber(&eqNum, NULL); } lastSet = LX200_TRACK; break; // Sync case LX200_SYNC: lastSet = LX200_SYNC; eqNum.s = IPS_IDLE; if ( ( err = Sync(syncString) < 0) ) { IDSetNumber( &eqNum , "Synchronization failed."); return (-1); } eqNum.s = IPS_OK; IDLog("Synchronization successful %s\n", syncString); IDSetNumber(&eqNum, "Synchronization successful."); break; // PARK // Set RA to LST and DEC to 0 degrees, slew, then change to 'guide' slew after slew is complete. case LX200_PARK: if (eqNum.s == IPS_BUSY) { abortSlew(); // sleep for 200 mseconds usleep(200000); } if ((err = Slew())) { slewError(err); return (-1); } ParkSP.s = IPS_BUSY; eqNum.s = IPS_BUSY; IDSetNumber(&eqNum, NULL); IDSetSwitch(&ParkSP, "The telescope is slewing to park position. Turn off the telescope after park is complete."); break; } return (0); } int LX200Generic::getOnSwitch(ISwitchVectorProperty *sp) { for (int i=0; i < sp->nsp ; i++) if (sp->sp[i].s == ISS_ON) return i; return -1; } int LX200Generic::checkPower(ISwitchVectorProperty *sp) { if (simulation) return 0; if (PowerSP.s != IPS_OK) { if (!strcmp(sp->label, "")) IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->name); else IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->label); sp->s = IPS_IDLE; IDSetSwitch(sp, NULL); return -1; } return 0; } int LX200Generic::checkPower(INumberVectorProperty *np) { if (simulation) return 0; if (PowerSP.s != IPS_OK) { if (!strcmp(np->label, "")) IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->name); else IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->label); np->s = IPS_IDLE; IDSetNumber(np, NULL); return -1; } return 0; } int LX200Generic::checkPower(ITextVectorProperty *tp) { if (simulation) return 0; if (PowerSP.s != IPS_OK) { if (!strcmp(tp->label, "")) IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->name); else IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->label); tp->s = IPS_IDLE; IDSetText(tp, NULL); return -1; } return 0; } void LX200Generic::powerTelescope() { switch (PowerSP.sp[0].s) { case ISS_ON: if (simulation) { PowerSP.s = IPS_OK; IDSetSwitch (&PowerSP, "Simulated telescope is online."); updateTime(); return; } if (Connect(Port.tp[0].text)) { PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; IDSetSwitch (&PowerSP, "Error connecting to port %s\n", Port.tp[0].text); return; } if (testTelescope()) { PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; IDSetSwitch (&PowerSP, "Error connecting to Telescope. Telescope is offline."); return; } IDLog("telescope test successfful\n"); PowerSP.s = IPS_OK; IDSetSwitch (&PowerSP, "Telescope is online. Retrieving basic data..."); getBasicData(); break; case ISS_OFF: PowerS[0].s = ISS_OFF; PowerS[1].s = ISS_ON; PowerSP.s = IPS_IDLE; IDSetSwitch (&PowerSP, "Telescope is offline."); IDLog("Telescope is offline."); Disconnect(); break; } } void LX200Generic::slewError(int slewCode) { OnCoordSetSw.s = IPS_IDLE; ParkSP.s = IPS_IDLE; IDSetSwitch(&ParkSP, NULL); if (slewCode == 1) IDSetSwitch (&OnCoordSetSw, "Object below horizon."); else if (slewCode == 2) IDSetSwitch (&OnCoordSetSw, "Object below the minimum elevation limit."); else IDSetSwitch (&OnCoordSetSw, "Slew failed."); } void LX200Generic::getAlignment() { if (PowerSP.s != IPS_OK) return; signed char align = ACK(); if (align < 0) { IDSetSwitch (&AlignmentSw, "Failed to get telescope tqalignment."); return; } AlignmentS[0].s = ISS_OFF; AlignmentS[1].s = ISS_OFF; AlignmentS[2].s = ISS_OFF; switch (align) { case 'P': AlignmentS[0].s = ISS_ON; break; case 'A': AlignmentS[1].s = ISS_ON; break; case 'L': AlignmentS[2].s = ISS_ON; break; } AlignmentSw.s = IPS_OK; IDSetSwitch (&AlignmentSw, NULL); IDLog("ACK success %c\n", align); } void LX200Generic::enableSimulation(bool enable) { simulation = enable; if (simulation) IDLog("Warning: Simulation is activated.\n"); else IDLog("Simulation is disabled.\n"); } void LX200Generic::updateTime() { char cdate[32]; double ctime; int h, m, s; int day, month, year, result; int UTC_h, UTC_month, UTC_year, UTC_day, daysInFeb; bool leapYear; tzset(); UTCOffset = timezoneOffset(); IDLog("Daylight: %s - TimeZone: %g\n", daylight ? "Yes" : "No", UTCOffset); if (simulation) { sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", 1979, 6, 25, 3, 30, 30); IDLog("Telescope ISO date and time: %s\n", UTC[0].text); IDSetText(&Time, NULL); return; } getLocalTime24(&ctime); getSexComponents(ctime, &h, &m, &s); UTC_h = h; if ( (result = getSDTime(&STime[0].value)) < 0) IDMessage(thisDevice, "Failed to retrieve siderial time from device."); getCalenderDate(cdate); result = sscanf(cdate, "%d/%d/%d", &year, &month, &day); if (result != 3) return; if (year % 4 == 0) { if (year % 100 == 0) { if (year % 400 == 0) leapYear = true; else leapYear = false; } else leapYear = true; } else leapYear = false; daysInFeb = leapYear ? 29 : 28; UTC_year = year; UTC_month = month; UTC_day = day; IDLog("day: %d - month %d - year: %d\n", day, month, year); // we'll have to convert telescope time to UTC manually starting from hour up // seems like a stupid way to do it.. oh well UTC_h = (int) UTCOffset + h; if (UTC_h < 0) { UTC_h += 24; UTC_day--; } else if (UTC_h > 24) { UTC_h -= 24; UTC_day++; } switch (UTC_month) { case 1: case 8: if (UTC_day < 1) { UTC_day = 31; UTC_month--; } else if (UTC_day > 31) { UTC_day = 1; UTC_month++; } break; case 2: if (UTC_day < 1) { UTC_day = 31; UTC_month--; } else if (UTC_day > daysInFeb) { UTC_day = 1; UTC_month++; } break; case 3: if (UTC_day < 1) { UTC_day = daysInFeb; UTC_month--; } else if (UTC_day > 31) { UTC_day = 1; UTC_month++; } break; case 4: case 6: case 9: case 11: if (UTC_day < 1) { UTC_day = 31; UTC_month--; } else if (UTC_day > 30) { UTC_day = 1; UTC_month++; } break; case 5: case 7: case 10: case 12: if (UTC_day < 1) { UTC_day = 30; UTC_month--; } else if (UTC_day > 31) { UTC_day = 1; UTC_month++; } break; } if (UTC_month < 1) { UTC_month = 12; UTC_year--; } else if (UTC_month > 12) { UTC_month = 1; UTC_year++; } /* Format it into ISO 8601 */ sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", UTC_year, UTC_month, UTC_day, UTC_h, m, s); IDLog("Local telescope time: %02d:%02d:%02d\n", h, m , s); IDLog("Telescope SD Time is: %g\n", STime[0].value); IDLog("UTC date and time: %s\n", UTC[0].text); // Let's send everything to the client IDSetText(&Time, NULL); IDSetNumber(&SDTime, NULL); } void LX200Generic::updateLocation() { int dd = 0, mm = 0, err = 0; if ( (err = getSiteLatitude(&dd, &mm)) < 0) IDMessage(thisDevice, "Failed to get site latitude from device."); else { if (dd > 0) geoNum.np[0].value = dd + mm/60.0; else geoNum.np[0].value = dd - mm/60.0; IDLog("Autostar Latitude: %d:%d\n", dd, mm); IDLog("INDI Latitude: %g\n", geoNum.np[0].value); } if ( (err = getSiteLongitude(&dd, &mm)) < 0) IDMessage(thisDevice, "Failed to get site longitude from device."); else { if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm/60.0); else geoNum.np[1].value = (dd - mm/60.0) * -1.0; IDLog("Autostar Longitude: %d:%d\n", dd, mm); IDLog("INDI Longitude: %g\n", geoNum.np[1].value); } IDSetNumber (&geoNum, NULL); }