moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indi / lx200generic.cpp
blobaf6ab54578931bb5e6b8afc68127a5e7cf61ad9e
1 #if 0
2 LX200 Generic
3 Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #endif
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <math.h>
28 #include <unistd.h>
29 #include <time.h>
31 #include "indicom.h"
32 #include "lx200driver.h"
33 #include "lx200gps.h"
34 #include "lx200classic.h"
37 ** Return the timezone offset in hours (as a double, so fractional
38 ** hours are possible, for instance in Newfoundland). Also sets
39 ** daylight on non-Linux systems to record whether DST is in effect.
43 #if !(TIMEZONE_IS_INT)
44 static int daylight = 0;
45 #endif
47 static inline double timezoneOffset()
49 /*
50 ** In Linux, there's a timezone variable that holds the timezone offset;
51 ** Otherwise, we need to make a little detour. The directions of the offset
52 ** are different: CET is -3600 in Linux and +3600 elsewhere.
54 #if TIMEZONE_IS_INT
55 return timezone / (60 * 60);
56 #else
57 time_t now;
58 struct tm *tm;
59 now = time(NULL);
60 tm = localtime(&now);
61 daylight = tm->tm_isdst;
62 return -(tm->tm_gmtoff) / (60 * 60);
63 #endif
66 LX200Generic *telescope = NULL;
67 int MaxReticleFlashRate = 3;
69 /* There is _one_ binary for all LX200 drivers, but each binary is renamed
70 ** to its device name (i.e. lx200gps, lx200_16..etc). The main function will
71 ** fetch from std args the binary name and ISInit will create the apporpiate
72 ** device afterwards. If the binary name does not match any known devices,
73 ** we simply create a generic device
75 extern char* me;
77 #define COMM_GROUP "Communication"
78 #define BASIC_GROUP "Main Control"
79 #define MOVE_GROUP "Movement Control"
80 #define DATETIME_GROUP "Date/Time"
81 #define SITE_GROUP "Site Management"
82 #define FOCUS_GROUP "Focus Control"
84 #define RA_THRESHOLD 0.01
85 #define DEC_THRESHOLD 0.05
86 #define LX200_SLEW 0
87 #define LX200_TRACK 1
88 #define LX200_SYNC 2
89 #define LX200_PARK 3
91 static void ISPoll(void *);
92 static void retryConnection(void *);
94 /*INDI controls */
95 static ISwitch PowerS[] = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}};
96 static ISwitch AlignmentS [] = {{"Polar", "", ISS_ON, 0, 0}, {"AltAz", "", ISS_OFF, 0, 0}, {"Land", "", ISS_OFF, 0, 0}};
97 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}};
98 static ISwitch SlewModeS[] = {{"Max", "", ISS_ON, 0, 0}, {"Find", "", ISS_OFF, 0, 0}, {"Centering", "", ISS_OFF, 0, 0}, {"Guide", "", ISS_OFF, 0 , 0}};
99 static ISwitch OnCoordSetS[] = {{"SLEW", "Slew", ISS_ON, 0, 0 }, {"TRACK", "Track", ISS_OFF, 0, 0}, {"SYNC", "Sync", ISS_OFF, 0 , 0}};
100 static ISwitch TrackModeS[] = {{ "Default", "", ISS_ON, 0, 0} , { "Lunar", "", ISS_OFF, 0, 0}, {"Manual", "", ISS_OFF, 0, 0}};
101 static ISwitch abortSlewS[] = {{"ABORT", "Abort", ISS_OFF, 0, 0 }};
102 static ISwitch ParkS[] = { {"PARK", "Park", ISS_OFF, 0, 0} };
104 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}};
106 static ISwitch FocusSpeedS[] = { {"FOCUS_HALT", "Halt", ISS_ON, 0, 0}, { "FOCUS_FAST", "Fast", ISS_OFF, 0, 0}, {"FOCUS_MEDIUM", "Medium", ISS_OFF, 0, 0}, {"FOCUS_SLOW", "Slow", ISS_OFF, 0, 0}};
107 static ISwitch FocusMotionS[] = { {"FOCUS_IN", "Focus in", ISS_OFF, 0, 0}, {"FOCUS_OUT", "Focus out", ISS_OFF, 0, 0}};
108 static INumber FocusTimerN[] = { {"TIMEOUT", "Timeout (s)", "%10.6m", 0., 120., 1., 0., 0, 0, 0 }};
110 static INumberVectorProperty FocusTimerNP = { mydev, "FOCUS_TIMEOUT", "Focus Timer", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusTimerN, NARRAY(FocusTimerN), "", 0};
112 /* equatorial position */
113 INumber eq[] = {
114 {"RA", "RA H:M:S", "%10.6m", 0., 24., 0., 0., 0, 0, 0},
115 {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0},
117 //TODO decide appropiate TIME_OUT
118 // N.B. No Static identifier as it is needed for external linkage
119 INumberVectorProperty eqNum = {
120 mydev, "EQUATORIAL_EOD_COORD", "Equatorial JNow", BASIC_GROUP, IP_RW, 0, IPS_IDLE,
121 eq, NARRAY(eq), "", 0};
123 /* Fundamental group */
124 ISwitchVectorProperty PowerSP = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0};
125 static IText PortT[] = {{"PORT", "Port", 0, 0, 0, 0}};
126 static ITextVectorProperty Port = { mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_IDLE, PortT, NARRAY(PortT), "", 0};
128 /* Basic data group */
129 static ISwitchVectorProperty AlignmentSw = { mydev, "Alignment", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, AlignmentS, NARRAY(AlignmentS), "", 0};
131 /* Movement group */
132 static ISwitchVectorProperty OnCoordSetSw = { mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, OnCoordSetS, NARRAY(OnCoordSetS), "", 0};
134 static ISwitchVectorProperty abortSlewSw = { mydev, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, abortSlewS, NARRAY(abortSlewS), "", 0};
136 ISwitchVectorProperty ParkSP = {mydev, "PARK", "Park Scope", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, ParkS, NARRAY(ParkS), "", 0 };
138 static ISwitchVectorProperty SlewModeSw = { mydev, "Slew rate", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SlewModeS, NARRAY(SlewModeS), "", 0};
140 static ISwitchVectorProperty TrackModeSw = { mydev, "Tracking Mode", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, TrackModeS, NARRAY(TrackModeS), "", 0};
142 static INumber TrackFreq[] = {{ "trackFreq", "Freq", "%g", 56.4, 60.1, 0.1, 60.1, 0, 0, 0}};
144 static INumberVectorProperty TrackingFreq= { mydev, "Tracking Frequency", "", MOVE_GROUP, IP_RW, 0, IPS_IDLE, TrackFreq, NARRAY(TrackFreq), "", 0};
146 static ISwitchVectorProperty MovementSw = { mydev, "MOVEMENT", "Move toward", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, MovementS, NARRAY(MovementS), "", 0};
148 // Focus Control
149 static ISwitchVectorProperty FocusSpeedSw = {mydev, "FOCUS_SPEED", "Speed", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusSpeedS, NARRAY(FocusSpeedS), "", 0};
151 static ISwitchVectorProperty FocusMotionSw = {mydev, "FOCUS_MOTION", "Motion", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusMotionS, NARRAY(FocusMotionS), "", 0};
153 /* Data & Time */
154 static IText UTC[] = {{"UTC", "UTC", 0, 0, 0, 0}};
155 ITextVectorProperty Time = { mydev, "TIME", "UTC Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, UTC, NARRAY(UTC), "", 0};
156 static INumber STime[] = {{"LST", "Sidereal time", "%10.6m" , 0.,24.,0.,0., 0, 0, 0}};
157 INumberVectorProperty SDTime = { mydev, "SDTIME", "Sidereal Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, STime, NARRAY(STime), "", 0};
160 /* Site managment */
161 static ISwitchVectorProperty SitesSw = { mydev, "Sites", "", SITE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SitesS, NARRAY(SitesS), "", 0};
162 /* geographic location */
163 static INumber geo[] = {
164 {"LAT", "Lat. D:M:S +N", "%10.6m", -90., 90., 0., 0., 0, 0, 0},
165 {"LONG", "Long. D:M:S +E", "%10.6m", 0., 360., 0., 0., 0, 0, 0},
167 static INumberVectorProperty geoNum = {
168 mydev, "GEOGRAPHIC_COORD", "Geographic Location", SITE_GROUP, IP_RW, 0., IPS_IDLE,
169 geo, NARRAY(geo), "", 0};
170 static IText SiteNameT[] = {{"SiteName", "", 0, 0, 0, 0}};
171 static ITextVectorProperty SiteName = { mydev, "Site Name", "", SITE_GROUP, IP_RW, 0 , IPS_IDLE, SiteNameT, NARRAY(SiteNameT), "", 0};
173 void changeLX200GenericDeviceName(const char * newName)
175 strcpy(PowerSP.device , newName);
176 strcpy(Port.device , newName);
177 strcpy(AlignmentSw.device, newName);
179 // BASIC_GROUP
180 strcpy(eqNum.device, newName);
181 strcpy(OnCoordSetSw.device , newName );
182 strcpy(abortSlewSw.device , newName );
183 strcpy(ParkSP.device, newName);
185 // MOVE_GROUP
186 strcpy(SlewModeSw.device , newName );
187 strcpy(TrackModeSw.device , newName );
188 strcpy(TrackingFreq.device , newName );
189 strcpy(MovementSw.device , newName );
191 // FOCUS_GROUP
192 strcpy(FocusSpeedSw.device , newName );
193 strcpy(FocusMotionSw.device , newName );
194 strcpy(FocusTimerNP.device, newName);
196 // DATETIME_GROUP
197 strcpy(Time.device , newName );
198 strcpy(SDTime.device , newName );
200 // SITE_GROUP
201 strcpy(SitesSw.device , newName );
202 strcpy(SiteName.device , newName );
203 strcpy(geoNum.device , newName );
207 void changeAllDeviceNames(const char *newName)
209 changeLX200GenericDeviceName(newName);
210 changeLX200AutostarDeviceName(newName);
211 changeLX200_16DeviceName(newName);
212 changeLX200ClassicDeviceName(newName);
213 changeLX200GPSDeviceName(newName);
217 /* send client definitions of all properties */
218 void ISInit()
220 static int isInit=0;
222 if (isInit)
223 return;
225 isInit = 1;
227 PortT[0].text = strcpy(new char[32], "/dev/ttyS0");
228 UTC[0].text = strcpy(new char[32], "YYYY-MM-DDTHH:MM:SS");
230 if (strstr(me, "lx200classic"))
232 fprintf(stderr , "initilizaing from LX200 classic device...\n");
233 // 1. mydev = device_name
234 changeAllDeviceNames("LX200 Classic");
235 // 2. device = sub_class
236 telescope = new LX200Classic();
237 telescope->setCurrentDeviceName("LX200 Classic");
239 MaxReticleFlashRate = 3;
242 else if (strstr(me, "lx200gps"))
244 fprintf(stderr , "initilizaing from LX200 GPS device...\n");
245 // 1. mydev = device_name
246 changeAllDeviceNames("LX200 GPS");
247 // 2. device = sub_class
248 telescope = new LX200GPS();
249 telescope->setCurrentDeviceName("LX200 GPS");
251 MaxReticleFlashRate = 9;
253 else if (strstr(me, "lx200_16"))
256 IDLog("Initilizaing from LX200 16 device...\n");
257 // 1. mydev = device_name
258 changeAllDeviceNames("LX200 16");
259 // 2. device = sub_class
260 telescope = new LX200_16();
261 telescope->setCurrentDeviceName("LX200 16");
263 MaxReticleFlashRate = 3;
265 else if (strstr(me, "lx200autostar"))
267 fprintf(stderr , "initilizaing from autostar device...\n");
269 // 1. change device name
270 changeAllDeviceNames("LX200 Autostar");
271 // 2. device = sub_class
272 telescope = new LX200Autostar();
273 telescope->setCurrentDeviceName("LX200 Autostar");
275 MaxReticleFlashRate = 9;
277 // be nice and give them a generic device
278 else
280 telescope = new LX200Generic();
281 telescope->setCurrentDeviceName("LX200 Generic");
286 void ISGetProperties (const char *dev)
287 { ISInit(); telescope->ISGetProperties(dev); IEAddTimer (POLLMS, ISPoll, NULL);}
288 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
289 { ISInit(); telescope->ISNewSwitch(dev, name, states, names, n);}
290 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
291 { ISInit(); telescope->ISNewText(dev, name, texts, names, n);}
292 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
293 { ISInit(); telescope->ISNewNumber(dev, name, values, names, n);}
294 void ISPoll (void *p) { telescope->ISPoll(); IEAddTimer (POLLMS, ISPoll, NULL); p=p;}
295 void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/)
298 /**************************************************
299 *** LX200 Generic Implementation
300 ***************************************************/
302 LX200Generic::LX200Generic()
304 struct tm *utp;
305 time_t t;
306 time (&t);
307 utp = gmtime (&t);
309 currentSiteNum = 1;
310 trackingMode = LX200_TRACK_DEFAULT;
311 lastSet = -1;
312 fault = false;
313 simulation = false;
314 targetRA = 0;
315 targetDEC = 0;
316 currentRA = 0;
317 currentDEC = 0;
318 currentSet = 0;
319 UTCOffset = 0;
320 lastMove[0] = lastMove[1] = lastMove[2] = lastMove[3] = 0;
322 localTM = new tm;
324 utp->tm_mon += 1;
325 utp->tm_year += 1900;
326 JD = UTtoJD(utp);
328 IDLog("Julian Day is %g\n", JD);
330 // Children call parent routines, this is the default
331 IDLog("initilizaing from generic LX200 device...\n");
332 IDLog("INDI Version: 2004-02-17\n");
334 //enableSimulation(true);
337 void LX200Generic::setCurrentDeviceName(const char * devName)
339 strcpy(thisDevice, devName);
343 void LX200Generic::ISGetProperties(const char *dev)
346 if (dev && strcmp (thisDevice, dev))
347 return;
349 // COMM_GROUP
350 IDDefSwitch (&PowerSP, NULL);
351 IDDefText (&Port, NULL);
352 IDDefSwitch (&AlignmentSw, NULL);
354 // BASIC_GROUP
355 IDDefNumber (&eqNum, NULL);
356 IDDefSwitch (&OnCoordSetSw, NULL);
357 IDDefSwitch (&abortSlewSw, NULL);
358 IDDefSwitch (&ParkSP, NULL);
360 // MOVE_GROUP
361 IDDefNumber (&TrackingFreq, NULL);
362 IDDefSwitch (&SlewModeSw, NULL);
363 IDDefSwitch (&TrackModeSw, NULL);
364 IDDefSwitch (&MovementSw, NULL);
366 // FOCUS_GROUP
367 IDDefSwitch(&FocusSpeedSw, NULL);
368 IDDefSwitch(&FocusMotionSw, NULL);
369 IDDefNumber(&FocusTimerNP, NULL);
371 // DATETIME_GROUP
372 IDDefText (&Time, NULL);
373 IDDefNumber (&SDTime, NULL);
375 // SITE_GROUP
376 IDDefSwitch (&SitesSw, NULL);
377 IDDefText (&SiteName, NULL);
378 IDDefNumber (&geoNum, NULL);
380 /* Send the basic data to the new client if the previous client(s) are already connected. */
381 if (PowerSP.s == IPS_OK)
382 getBasicData();
386 void LX200Generic::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
388 int err;
389 struct tm *ltp = new tm;
390 struct tm utm;
391 time_t ltime;
392 time (&ltime);
393 localtime_r (&ltime, ltp);
394 IText *tp;
396 // ignore if not ours //
397 if (strcmp (dev, thisDevice))
398 return;
400 // suppress warning
401 n=n;
403 if (!strcmp(name, Port.name) )
405 Port.s = IPS_OK;
406 tp = IUFindText( &Port, names[0] );
407 if (!tp)
408 return;
410 tp->text = new char[strlen(texts[0])+1];
411 strcpy(tp->text, texts[0]);
412 IDSetText (&Port, NULL);
413 return;
416 if (!strcmp (name, SiteName.name) )
418 if (checkPower(&SiteName))
419 return;
421 if ( ( err = setSiteName(texts[0], currentSiteNum) < 0) )
423 handleError(&SiteName, err, "Setting site name");
424 return;
426 SiteName.s = IPS_OK;
427 tp = IUFindText(&SiteName, names[0]);
428 tp->text = new char[strlen(texts[0])+1];
429 strcpy(tp->text, texts[0]);
430 IDSetText(&SiteName , "Site name updated");
431 return;
434 if (!strcmp (name, Time.name))
436 if (checkPower(&Time))
437 return;
439 if (extractISOTime(texts[0], &utm) < 0)
441 Time.s = IPS_IDLE;
442 IDSetText(&Time , "Time invalid");
443 return;
445 utm.tm_mon += 1;
446 ltp->tm_mon += 1;
447 utm.tm_year += 1900;
448 ltp->tm_year += 1900;
451 /*dayDiff = utm.tm_mday - ltp->tm_mday;
452 if (dayDiff == 0)
453 UTCOffset = (ltp->tm_hour - utm.tm_hour);
454 else if (dayDiff > 0)
455 UTCOffset = ltp->tm_hour - utm.tm_hour - 24;
456 else UTCOffset = ltp->tm_hour - utm.tm_hour + 24;*/
457 tzset();
459 UTCOffset = timezoneOffset();
461 IDLog("local time is %02d:%02d:%02d\nUTCOffset: %g\n", ltp->tm_hour, ltp->tm_min, ltp->tm_sec, UTCOffset);
463 getSDTime(&STime[0].value);
464 IDSetNumber(&SDTime, NULL);
466 if ( ( err = setUTCOffset(UTCOffset) < 0) )
468 Time.s = IPS_IDLE;
469 IDSetText( &Time , "Setting UTC Offset failed.");
470 return;
473 if ( ( err = setLocalTime(ltp->tm_hour, ltp->tm_min, ltp->tm_sec) < 0) )
475 handleError(&Time, err, "Setting local time");
476 return;
479 tp = IUFindText(&Time, names[0]);
480 if (!tp)
481 return;
482 tp->text = new char[strlen(texts[0])+1];
483 strcpy(tp->text, texts[0]);
484 Time.s = IPS_OK;
486 // update JD
487 JD = UTtoJD(&utm);
489 IDLog("New JD is %f\n", (float) JD);
491 if ((localTM->tm_mday == ltp->tm_mday ) && (localTM->tm_mon == ltp->tm_mon) &&
492 (localTM->tm_year == ltp->tm_year))
494 IDSetText(&Time , "Time updated to %s", texts[0]);
495 return;
498 localTM = ltp;
500 if (!strcmp(dev, "LX200 GPS"))
502 if ( ( err = setCalenderDate(utm.tm_mday, utm.tm_mon, utm.tm_year) < 0) )
504 handleError(&Time, err, "Setting UTC date.");
505 return;
508 else
510 if ( ( err = setCalenderDate(ltp->tm_mday, ltp->tm_mon, ltp->tm_year) < 0) )
512 handleError(&Time, err, "Setting local date.");
513 return;
517 IDSetText(&Time , "Date changed, updating planetary data...");
522 void LX200Generic::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
524 int h =0, m =0, s=0, err;
525 double newRA =0, newDEC =0;
527 // ignore if not ours //
528 if (strcmp (dev, thisDevice))
529 return;
531 if (!strcmp (name, eqNum.name))
533 int i=0, nset=0;
535 if (checkPower(&eqNum))
536 return;
538 for (nset = i = 0; i < n; i++)
540 INumber *eqp = IUFindNumber (&eqNum, names[i]);
541 if (eqp == &eq[0])
543 newRA = values[i];
544 nset += newRA >= 0 && newRA <= 24.0;
545 } else if (eqp == &eq[1])
547 newDEC = values[i];
548 nset += newDEC >= -90.0 && newDEC <= 90.0;
552 if (nset == 2)
554 /*eqNum.s = IPS_BUSY;*/
555 char RAStr[32], DecStr[32];
557 fs_sexa(RAStr, newRA, 2, 3600);
558 fs_sexa(DecStr, newDEC, 2, 3600);
560 IDLog("We received J2000 RA %g - DEC %g\n", newRA, newDEC);
561 IDLog("We received J2000 RA %s - DEC %s\n", RAStr, DecStr);
563 /*apparentCoord( (double) J2000, JD, &newRA, &newDEC);
565 fs_sexa(RAStr, newRA, 2, 3600);
566 fs_sexa(DecStr, newDEC, 2, 3600);
568 IDLog("Processed to JNow RA %f - DEC %f\n", newRA, newDEC);
569 IDLog("Processed to JNow RA %s - DEC %s\n", RAStr, DecStr);*/
571 if ( (err = setObjectRA(newRA)) < 0 || ( err = setObjectDEC(newDEC)) < 0)
573 handleError(&eqNum, err, "Setting RA/DEC");
574 return;
577 /*eqNum.s = IPS_BUSY;*/
578 targetRA = newRA;
579 targetDEC = newDEC;
581 if (MovementSw.s == IPS_BUSY)
583 for (int i=0; i < 4; i++)
585 lastMove[i] = 0;
586 MovementS[i].s = ISS_OFF;
589 MovementSw.s = IPS_IDLE;
590 IDSetSwitch(&MovementSw, NULL);
593 if (handleCoordSet())
595 eqNum.s = IPS_IDLE;
596 IDSetNumber(&eqNum, NULL);
599 } // end nset
600 else
602 eqNum.s = IPS_IDLE;
603 IDSetNumber(&eqNum, "RA or Dec missing or invalid");
606 return;
607 } /* end eqNum */
609 if ( !strcmp (name, SDTime.name) )
611 if (checkPower(&SDTime))
612 return;
615 if (values[0] < 0.0 || values[0] > 24.0)
617 SDTime.s = IPS_IDLE;
618 IDSetNumber(&SDTime , "Time invalid");
619 return;
622 getSexComponents(values[0], &h, &m, &s);
623 IDLog("Time is %02d:%02d:%02d\n", h, m, s);
625 if ( ( err = setSDTime(h, m, s) < 0) )
627 handleError(&SDTime, err, "Setting siderial time");
628 return;
631 SDTime.np[0].value = values[0];
632 SDTime.s = IPS_OK;
634 IDSetNumber(&SDTime , "Sidereal time updated to %02d:%02d:%02d", h, m, s);
636 return;
639 if (!strcmp (name, geoNum.name))
641 // new geographic coords
642 double newLong = 0, newLat = 0;
643 int i, nset;
644 char msg[128];
646 if (checkPower(&geoNum))
647 return;
650 for (nset = i = 0; i < n; i++)
652 INumber *geop = IUFindNumber (&geoNum, names[i]);
653 if (geop == &geo[0])
655 newLat = values[i];
656 nset += newLat >= -90.0 && newLat <= 90.0;
657 } else if (geop == &geo[1])
659 newLong = values[i];
660 nset += newLong >= 0.0 && newLong < 360.0;
664 if (nset == 2)
666 char l[32], L[32];
667 geoNum.s = IPS_OK;
668 fs_sexa (l, newLat, 3, 3600);
669 fs_sexa (L, newLong, 4, 3600);
671 if ( ( err = setSiteLongitude(360.0 - newLong) < 0) )
673 handleError(&geoNum, err, "Setting site coordinates");
674 return;
677 setSiteLatitude(newLat);
678 geoNum.np[0].value = newLat;
679 geoNum.np[1].value = newLong;
680 snprintf (msg, sizeof(msg), "Site location updated to Lat %.32s - Long %.32s", l, L);
681 } else
683 geoNum.s = IPS_IDLE;
684 strcpy(msg, "Lat or Long missing or invalid");
686 IDSetNumber (&geoNum, "%s", msg);
687 return;
690 if ( !strcmp (name, TrackingFreq.name) )
693 if (checkPower(&TrackingFreq))
694 return;
696 IDLog("Trying to set track freq of: %f\n", values[0]);
698 if ( ( err = setTrackFreq(values[0])) < 0)
700 handleError(&TrackingFreq, err, "Setting tracking frequency");
701 return;
704 TrackingFreq.s = IPS_OK;
705 TrackingFreq.np[0].value = values[0];
706 IDSetNumber(&TrackingFreq, "Tracking frequency set to %04.1f", values[0]);
707 if (trackingMode != LX200_TRACK_MANUAL)
709 trackingMode = LX200_TRACK_MANUAL;
710 TrackModeS[0].s = ISS_OFF;
711 TrackModeS[1].s = ISS_OFF;
712 TrackModeS[2].s = ISS_ON;
713 TrackModeSw.s = IPS_OK;
714 selectTrackingMode(trackingMode);
715 IDSetSwitch(&TrackModeSw, NULL);
718 return;
721 if (!strcmp(name, FocusTimerNP.name))
723 if (checkPower(&FocusTimerNP))
724 return;
726 // Don't update if busy
727 if (FocusTimerNP.s == IPS_BUSY)
728 return;
730 IUUpdateNumbers(&FocusTimerNP, values, names, n);
732 FocusTimerNP.s = IPS_OK;
734 IDSetNumber(&FocusTimerNP, NULL);
735 IDLog("Setting focus timer to %g\n", FocusTimerN[0].value);
737 return;
743 void LX200Generic::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
745 int index;
746 int dd, mm, err;
747 char combinedDir[64];
748 ISwitch *swp;
750 // suppress warning
751 names = names;
753 //IDLog("in new Switch with Device= %s and Property= %s and #%d items\n", dev, name,n);
754 //IDLog("SolarSw name is %s\n", SolarSw.name);
756 //IDLog("The device name is %s\n", dev);
757 // ignore if not ours //
758 if (strcmp (thisDevice, dev))
759 return;
761 // FIRST Switch ALWAYS for power
762 if (!strcmp (name, PowerSP.name))
764 IUResetSwitches(&PowerSP);
765 IUUpdateSwitches(&PowerSP, states, names, n);
766 powerTelescope();
767 return;
770 // Coord set
771 if (!strcmp(name, OnCoordSetSw.name))
773 if (checkPower(&OnCoordSetSw))
774 return;
776 IUResetSwitches(&OnCoordSetSw);
777 IUUpdateSwitches(&OnCoordSetSw, states, names, n);
778 currentSet = getOnSwitch(&OnCoordSetSw);
779 OnCoordSetSw.s = IPS_OK;
780 IDSetSwitch(&OnCoordSetSw, NULL);
783 // Parking
784 if (!strcmp(name, ParkSP.name))
786 if (checkPower(&ParkSP))
787 return;
789 ParkSP.s = IPS_IDLE;
792 if ( (err = getSDTime(&STime[0].value)) < 0)
794 handleError(&ParkSP, err, "Get siderial time");
795 return;
798 if (AlignmentS[0].s == ISS_ON)
800 targetRA = STime[0].value;
801 targetDEC = 0;
802 setObjectRA(targetRA);
803 setObjectDEC(targetDEC);
806 else if (AlignmentS[1].s == ISS_ON)
808 targetRA = calculateRA(STime[0].value);
809 targetDEC = calculateDec(geo[0].value, STime[0].value);
810 setObjectRA(targetRA);
811 setObjectDEC(targetDEC);
812 IDLog("Parking the scope in AltAz (0,0) which corresponds to (RA,DEC) of (%g,%g)\n", targetRA, targetDEC);
813 IDLog("Current Sidereal time is: %g\n", STime[0].value);
814 IDSetNumber(&SDTime, NULL);
816 else
818 IDSetSwitch(&ParkSP, "You can only park the telescope in Polar or AltAz modes.");
819 return;
822 IDSetNumber(&SDTime, NULL);
824 currentSet = LX200_PARK;
825 handleCoordSet();
828 // Abort Slew
829 if (!strcmp (name, abortSlewSw.name))
831 if (checkPower(&abortSlewSw))
833 abortSlewSw.s = IPS_IDLE;
834 IDSetSwitch(&abortSlewSw, NULL);
835 return;
838 IUResetSwitches(&abortSlewSw);
839 abortSlew();
841 if (eqNum.s == IPS_BUSY)
843 abortSlewSw.s = IPS_OK;
844 eqNum.s = IPS_IDLE;
845 IDSetSwitch(&abortSlewSw, "Slew aborted.");
846 IDSetNumber(&eqNum, NULL);
848 else if (MovementSw.s == IPS_BUSY)
851 for (int i=0; i < 4; i++)
852 lastMove[i] = 0;
854 MovementSw.s = IPS_IDLE;
855 abortSlewSw.s = IPS_OK;
856 eqNum.s = IPS_IDLE;
857 IUResetSwitches(&MovementSw);
858 IUResetSwitches(&abortSlewSw);
859 IDSetSwitch(&abortSlewSw, "Slew aborted.");
860 IDSetSwitch(&MovementSw, NULL);
861 IDSetNumber(&eqNum, NULL);
863 else
865 IUResetSwitches(&MovementSw);
866 abortSlewSw.s = IPS_OK;
867 IDSetSwitch(&abortSlewSw, NULL);
870 return;
873 // Alignment
874 if (!strcmp (name, AlignmentSw.name))
876 if (checkPower(&AlignmentSw))
877 return;
879 IUResetSwitches(&AlignmentSw);
880 IUUpdateSwitches(&AlignmentSw, states, names, n);
881 index = getOnSwitch(&AlignmentSw);
883 if ( ( err = setAlignmentMode(index) < 0) )
885 handleError(&AlignmentSw, err, "Setting alignment");
886 return;
889 AlignmentSw.s = IPS_OK;
890 IDSetSwitch (&AlignmentSw, NULL);
891 return;
895 // Sites
896 if (!strcmp (name, SitesSw.name))
898 if (checkPower(&SitesSw))
899 return;
901 IUResetSwitches(&SitesSw);
902 IUUpdateSwitches(&SitesSw, states, names, n);
903 currentSiteNum = getOnSwitch(&SitesSw) + 1;
905 if ( ( err = selectSite(currentSiteNum) < 0) )
907 handleError(&SitesSw, err, "Selecting sites");
908 return;
911 if ( ( err = getSiteLatitude(&dd, &mm) < 0))
913 handleError(&SitesSw, err, "Selecting sites");
914 return;
917 if (dd > 0) geoNum.np[0].value = dd + mm / 60.0;
918 else geoNum.np[0].value = dd - mm / 60.0;
920 if ( ( err = getSiteLongitude(&dd, &mm) < 0))
922 handleError(&SitesSw, err, "Selecting sites");
923 return;
926 if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm / 60.0);
927 else geoNum.np[1].value = (dd - mm / 60.0) * -1.0;
929 getSiteName( SiteName.tp[0].text, currentSiteNum);
931 IDLog("Selecting site %d\n", currentSiteNum);
933 geoNum.s = SiteName.s = SitesSw.s = IPS_OK;
935 IDSetNumber (&geoNum, NULL);
936 IDSetText (&SiteName, NULL);
937 IDSetSwitch (&SitesSw, NULL);
938 return;
941 // Focus speed
942 if (!strcmp (name, FocusSpeedSw.name))
944 if (checkPower(&FocusSpeedSw))
945 return;
947 IUResetSwitches(&FocusSpeedSw);
948 IUUpdateSwitches(&FocusSpeedSw, states, names, n);
949 index = getOnSwitch(&FocusSpeedSw);
952 if ( ( err = setFocuserSpeedMode(index) < 0) )
954 handleError(&FocusSpeedSw, err, "Setting focuser speed mode");
955 return;
958 /* disable timer and motion */
959 if (index == 0)
961 IUResetSwitches(&FocusMotionSw);
962 FocusMotionSw.s = IPS_IDLE;
963 FocusTimerNP.s = IPS_IDLE;
964 IDSetSwitch(&FocusMotionSw, NULL);
965 IDSetNumber(&FocusTimerNP, NULL);
969 FocusSpeedSw.s = IPS_OK;
970 IDSetSwitch(&FocusSpeedSw, NULL);
971 return;
974 // Focus Motion
975 if (!strcmp (name, FocusMotionSw.name))
977 if (checkPower(&FocusMotionSw))
978 return;
980 IUResetSwitches(&FocusMotionSw);
982 // If speed is "halt"
983 if (FocusSpeedS[0].s == ISS_ON)
985 FocusMotionSw.s = IPS_IDLE;
986 IDSetSwitch(&FocusMotionSw, NULL);
987 return;
990 IUUpdateSwitches(&FocusMotionSw, states, names, n);
991 index = getOnSwitch(&FocusMotionSw);
994 if ( ( err = setFocuserMotion(index) < 0) )
996 handleError(&FocusMotionSw, err, "Setting focuser speed");
997 return;
1001 FocusMotionSw.s = IPS_BUSY;
1003 // with a timer
1004 if (FocusTimerN[0].value > 0)
1005 FocusTimerNP.s = IPS_BUSY;
1007 IDSetSwitch(&FocusMotionSw, NULL);
1008 return;
1011 // Slew mode
1012 if (!strcmp (name, SlewModeSw.name))
1014 if (checkPower(&SlewModeSw))
1015 return;
1017 IUResetSwitches(&SlewModeSw);
1018 IUUpdateSwitches(&SlewModeSw, states, names, n);
1019 index = getOnSwitch(&SlewModeSw);
1021 if ( ( err = setSlewMode(index) < 0) )
1023 handleError(&SlewModeSw, err, "Setting slew mode");
1024 return;
1027 SlewModeSw.s = IPS_OK;
1028 IDSetSwitch(&SlewModeSw, NULL);
1029 return;
1032 // Movement
1033 if (!strcmp (name, MovementSw.name))
1035 if (checkPower(&MovementSw))
1036 return;
1038 index = -1;
1039 IUUpdateSwitches(&MovementSw, states, names, n);
1040 swp = IUFindSwitch(&MovementSw, names[0]);
1042 if (!swp)
1044 abortSlew();
1045 IUResetSwitches(&MovementSw);
1046 MovementSw.s = IPS_IDLE;
1047 IDSetSwitch(&MovementSw, NULL);
1050 if (swp == &MovementS[0]) index = 0;
1051 else if (swp == &MovementS[1]) index = 1;
1052 else if (swp == &MovementS[2]) index = 2;
1053 else index = 3;
1055 lastMove[index] = lastMove[index] == 0 ? 1 : 0;
1056 if (lastMove[index] == 0)
1057 MovementS[index].s = ISS_OFF;
1059 // North/South movement is illegal
1060 if (lastMove[LX200_NORTH] && lastMove[LX200_SOUTH])
1062 abortSlew();
1063 for (int i=0; i < 4; i++)
1064 lastMove[i] = 0;
1066 IUResetSwitches(&MovementSw);
1067 MovementSw.s = IPS_IDLE;
1068 IDSetSwitch(&MovementSw, "Slew aborted.");
1069 return;
1072 // East/West movement is illegal
1073 if (lastMove[LX200_EAST] && lastMove[LX200_WEST])
1075 abortSlew();
1076 for (int i=0; i < 4; i++)
1077 lastMove[i] = 0;
1079 IUResetSwitches(&MovementSw);
1080 MovementSw.s = IPS_IDLE;
1081 IDSetSwitch(&MovementSw, "Slew aborted.");
1082 return;
1085 IDLog("We have switch %d \n ", index);
1086 IDLog("NORTH: %d -- WEST: %d -- EAST: %d -- SOUTH %d\n", lastMove[0], lastMove[1], lastMove[2], lastMove[3]);
1088 if (lastMove[index] == 1)
1090 IDLog("issuing a move command\n");
1091 if ( ( err = MoveTo(index) < 0) )
1093 handleError(&MovementSw, err, "Setting motion direction");
1094 return;
1097 else
1098 HaltMovement(index);
1100 if (!lastMove[0] && !lastMove[1] && !lastMove[2] && !lastMove[3])
1101 MovementSw.s = IPS_IDLE;
1103 if (lastMove[index] == 0)
1104 IDSetSwitch(&MovementSw, "Moving toward %s aborted.", Direction[index]);
1105 else
1107 MovementSw.s = IPS_BUSY;
1108 if (lastMove[LX200_NORTH] && lastMove[LX200_WEST])
1109 strcpy(combinedDir, "North West");
1110 else if (lastMove[LX200_NORTH] && lastMove[LX200_EAST])
1111 strcpy(combinedDir, "North East");
1112 else if (lastMove[LX200_SOUTH] && lastMove[LX200_WEST])
1113 strcpy(combinedDir, "South West");
1114 else if (lastMove[LX200_SOUTH] && lastMove[LX200_EAST])
1115 strcpy(combinedDir, "South East");
1116 else
1117 strcpy(combinedDir, Direction[index]);
1119 IDSetSwitch(&MovementSw, "Moving %s...", combinedDir);
1121 return;
1124 // Tracking mode
1125 if (!strcmp (name, TrackModeSw.name))
1127 if (checkPower(&TrackModeSw))
1128 return;
1130 IUResetSwitches(&TrackModeSw);
1131 IUUpdateSwitches(&TrackModeSw, states, names, n);
1132 trackingMode = getOnSwitch(&TrackModeSw);
1134 if ( ( err = selectTrackingMode(trackingMode) < 0) )
1136 handleError(&TrackModeSw, err, "Setting tracking mode.");
1137 return;
1140 getTrackFreq(&TrackFreq[0].value);
1141 TrackModeSw.s = IPS_OK;
1142 IDSetNumber(&TrackingFreq, NULL);
1143 IDSetSwitch(&TrackModeSw, NULL);
1144 return;
1149 void LX200Generic::handleError(ISwitchVectorProperty *svp, int err, const char *msg)
1152 svp->s = IPS_ALERT;
1154 /* First check to see if the telescope is connected */
1155 if (testTelescope())
1157 /* The telescope is off locally */
1158 PowerS[0].s = ISS_OFF;
1159 PowerS[1].s = ISS_ON;
1160 PowerSP.s = IPS_BUSY;
1161 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1163 IDSetSwitch(svp, NULL);
1164 IEAddTimer(10000, retryConnection, NULL);
1165 return;
1168 /* If the error is a time out, then the device doesn't support this property or busy*/
1169 if (err == -2)
1171 svp->s = IPS_ALERT;
1172 IDSetSwitch(svp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1174 else
1175 /* Changing property failed, user should retry. */
1176 IDSetSwitch( svp , "%s failed.", msg);
1178 fault = true;
1181 void LX200Generic::handleError(INumberVectorProperty *nvp, int err, const char *msg)
1184 nvp->s = IPS_ALERT;
1186 /* First check to see if the telescope is connected */
1187 if (testTelescope())
1189 /* The telescope is off locally */
1190 PowerS[0].s = ISS_OFF;
1191 PowerS[1].s = ISS_ON;
1192 PowerSP.s = IPS_BUSY;
1193 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1195 IDSetNumber(nvp, NULL);
1196 IEAddTimer(10000, retryConnection, NULL);
1197 return;
1200 /* If the error is a time out, then the device doesn't support this property */
1201 if (err == -2)
1203 nvp->s = IPS_ALERT;
1204 IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1206 else
1207 /* Changing property failed, user should retry. */
1208 IDSetNumber( nvp , "%s failed.", msg);
1210 fault = true;
1213 void LX200Generic::handleError(ITextVectorProperty *tvp, int err, const char *msg)
1216 tvp->s = IPS_ALERT;
1218 /* First check to see if the telescope is connected */
1219 if (testTelescope())
1221 /* The telescope is off locally */
1222 PowerS[0].s = ISS_OFF;
1223 PowerS[1].s = ISS_ON;
1224 PowerSP.s = IPS_BUSY;
1225 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1227 IDSetText(tvp, NULL);
1228 IEAddTimer(10000, retryConnection, NULL);
1229 return;
1232 /* If the error is a time out, then the device doesn't support this property */
1233 if (err == -2)
1235 tvp->s = IPS_ALERT;
1236 IDSetText(tvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1239 else
1240 /* Changing property failed, user should retry. */
1241 IDSetText( tvp , "%s failed.", msg);
1243 fault = true;
1246 void LX200Generic::correctFault()
1249 fault = false;
1250 IDMessage(thisDevice, "Telescope is online.");
1254 bool LX200Generic::isTelescopeOn(void)
1256 if (simulation) return true;
1258 return (PowerSP.sp[0].s == ISS_ON);
1261 static void retryConnection(void * p)
1263 p=p;
1265 if (testTelescope())
1267 PowerSP.s = IPS_IDLE;
1268 IDSetSwitch(&PowerSP, "The connection to the telescope is lost.");
1269 return;
1272 PowerS[0].s = ISS_ON;
1273 PowerS[1].s = ISS_OFF;
1274 PowerSP.s = IPS_OK;
1276 IDSetSwitch(&PowerSP, "The connection to the telescope has been resumed.");
1280 void LX200Generic::ISPoll()
1282 double dx, dy;
1283 /*static int okCounter = 3;*/
1284 int err=0;
1286 if (!isTelescopeOn())
1287 return;
1289 switch (eqNum.s)
1291 case IPS_IDLE:
1292 getLX200RA(&currentRA);
1293 getLX200DEC(&currentDEC);
1295 if ( fabs (currentRA - lastRA) > 0.01 || fabs (currentDEC - lastDEC) > 0.01)
1297 eqNum.np[0].value = lastRA = currentRA;
1298 eqNum.np[1].value = lastDEC = currentDEC;
1299 IDSetNumber (&eqNum, NULL);
1301 break;
1303 case IPS_BUSY:
1304 getLX200RA(&currentRA);
1305 getLX200DEC(&currentDEC);
1306 dx = targetRA - currentRA;
1307 dy = targetDEC - currentDEC;
1309 IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
1310 IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
1312 eqNum.np[0].value = currentRA;
1313 eqNum.np[1].value = currentDEC;
1315 // Wait until acknowledged or within threshold
1316 if (fabs(dx) <= RA_THRESHOLD && fabs(dy) <= DEC_THRESHOLD)
1318 /* Don't set current to target. This might leave residual cumulative error
1319 currentRA = targetRA;
1320 currentDEC = targetDEC;
1323 eqNum.np[0].value = lastRA = currentRA;
1324 eqNum.np[1].value = lastDEC = currentDEC;
1325 IUResetSwitches(&OnCoordSetSw);
1326 OnCoordSetSw.s = IPS_OK;
1327 eqNum.s = IPS_OK;
1328 IDSetNumber (&eqNum, NULL);
1330 switch (currentSet)
1332 case LX200_SLEW:
1333 OnCoordSetSw.sp[0].s = ISS_ON;
1334 IDSetSwitch (&OnCoordSetSw, "Slew is complete.");
1335 break;
1337 case LX200_TRACK:
1338 OnCoordSetSw.sp[1].s = ISS_ON;
1339 IDSetSwitch (&OnCoordSetSw, "Slew is complete. Tracking...");
1340 break;
1342 case LX200_SYNC:
1343 break;
1345 case LX200_PARK:
1346 if (setSlewMode(LX200_SLEW_GUIDE) < 0)
1348 handleError(&eqNum, err, "Setting slew mode");
1349 return;
1352 IUResetSwitches(&SlewModeSw);
1353 SlewModeS[LX200_SLEW_GUIDE].s = ISS_ON;
1354 IDSetSwitch(&SlewModeSw, NULL);
1356 MoveTo(LX200_EAST);
1357 IUResetSwitches(&MovementSw);
1358 MovementS[LX200_EAST].s = ISS_ON;
1359 MovementSw.s = IPS_BUSY;
1360 for (int i=0; i < 4; i++)
1361 lastMove[i] = 0;
1362 lastMove[LX200_EAST] = 1;
1363 IDSetSwitch(&MovementSw, NULL);
1365 ParkSP.s = IPS_OK;
1366 IDSetSwitch (&ParkSP, "Park is complete. Turn off the telescope now.");
1367 break;
1370 } else
1372 eqNum.np[0].value = currentRA;
1373 eqNum.np[1].value = currentDEC;
1374 IDSetNumber (&eqNum, NULL);
1376 break;
1378 case IPS_OK:
1380 /*if (--okCounter >= 0)
1381 break;
1383 // Activate again in 3 seconds
1384 okCounter = 3;*/
1386 if ( (err = getLX200RA(&currentRA)) < 0 || (err = getLX200DEC(&currentDEC)) < 0)
1388 handleError(&eqNum, err, "Getting RA/DEC");
1389 return;
1392 if (fault)
1393 correctFault();
1395 if ( (currentRA != lastRA) || (currentDEC != lastDEC))
1397 eqNum.np[0].value = lastRA = currentRA;
1398 eqNum.np[1].value = lastDEC = currentDEC;
1399 IDSetNumber (&eqNum, NULL);
1401 break;
1404 case IPS_ALERT:
1405 break;
1408 switch (MovementSw.s)
1410 case IPS_IDLE:
1411 break;
1412 case IPS_BUSY:
1413 getLX200RA(&currentRA);
1414 getLX200DEC(&currentDEC);
1415 /*apparentCoord( JD, (double) J2000, &currentRA, &currentDEC);*/
1416 eqNum.np[0].value = currentRA;
1417 eqNum.np[1].value = currentDEC;
1419 IDSetNumber (&eqNum, NULL);
1420 break;
1421 case IPS_OK:
1422 break;
1423 case IPS_ALERT:
1424 break;
1427 switch (FocusTimerNP.s)
1429 case IPS_IDLE:
1430 break;
1432 case IPS_BUSY:
1433 FocusTimerN[0].value--;
1435 if (FocusTimerN[0].value == 0)
1438 if ( ( err = setFocuserSpeedMode(0) < 0) )
1440 handleError(&FocusSpeedSw, err, "setting focuser speed mode");
1441 IDLog("Error setting focuser speed mode\n");
1442 return;
1446 FocusMotionSw.s = IPS_IDLE;
1447 FocusTimerNP.s = IPS_OK;
1448 FocusSpeedSw.s = IPS_OK;
1450 IUResetSwitches(&FocusMotionSw);
1451 IUResetSwitches(&FocusSpeedSw);
1452 FocusSpeedS[0].s = ISS_ON;
1454 IDSetSwitch(&FocusSpeedSw, NULL);
1455 IDSetSwitch(&FocusMotionSw, NULL);
1458 IDSetNumber(&FocusTimerNP, NULL);
1459 break;
1461 case IPS_OK:
1462 break;
1464 case IPS_ALERT:
1465 break;
1471 void LX200Generic::getBasicData()
1474 int err;
1475 struct tm *timep;
1476 time_t ut;
1477 time (&ut);
1478 timep = gmtime (&ut);
1479 strftime (Time.tp[0].text, sizeof(Time.tp[0].text), "%Y-%m-%dT%H:%M:%S", timep);
1481 IDLog("PC UTC time is %s\n", Time.tp[0].text);
1483 getAlignment();
1485 checkLX200Format();
1487 if ( (err = getTimeFormat(&timeFormat)) < 0)
1488 IDMessage(thisDevice, "Failed to retrieve time format from device.");
1489 else
1491 timeFormat = (timeFormat == 24) ? LX200_24 : LX200_AM;
1492 // We always do 24 hours
1493 if (timeFormat != LX200_24)
1494 toggleTimeFormat();
1497 getLX200RA(&targetRA);
1498 getLX200DEC(&targetDEC);
1500 eqNum.np[0].value = targetRA;
1501 eqNum.np[1].value = targetDEC;
1503 IDSetNumber (&eqNum, NULL);
1505 SiteNameT[0].text = new char[64];
1507 if ( (err = getSiteName(SiteNameT[0].text, currentSiteNum)) < 0)
1508 IDMessage(thisDevice, "Failed to get site name from device");
1509 else
1510 IDSetText (&SiteName, NULL);
1512 if ( (err = getTrackFreq(&TrackFreq[0].value)) < 0)
1513 IDMessage(thisDevice, "Failed to get tracking frequency from device.");
1514 else
1515 IDSetNumber (&TrackingFreq, NULL);
1518 updateLocation();
1519 updateTime();
1523 int LX200Generic::handleCoordSet()
1526 int err;
1527 char syncString[256];
1528 char RAStr[32], DecStr[32];
1529 double dx, dy;
1531 IDLog("In Handle Coord Set()\n");
1533 switch (currentSet)
1536 // Slew
1537 case LX200_SLEW:
1538 lastSet = LX200_SLEW;
1539 if (eqNum.s == IPS_BUSY)
1541 IDLog("Aboring Slew\n");
1542 abortSlew();
1544 // sleep for 100 mseconds
1545 usleep(100000);
1548 if ((err = Slew()))
1550 slewError(err);
1551 return (-1);
1554 eqNum.s = IPS_BUSY;
1555 fs_sexa(RAStr, targetRA, 2, 3600);
1556 fs_sexa(DecStr, targetDEC, 2, 3600);
1557 IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
1558 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1559 break;
1561 // Track
1562 case LX200_TRACK:
1563 IDLog("We're in LX200_TRACK\n");
1564 if (eqNum.s == IPS_BUSY)
1566 IDLog("Aboring Slew\n");
1567 abortSlew();
1569 // sleep for 200 mseconds
1570 usleep(200000);
1573 dx = fabs ( targetRA - currentRA );
1574 dy = fabs (targetDEC - currentDEC);
1577 if (dx >= TRACKING_THRESHOLD || dy >= TRACKING_THRESHOLD)
1579 IDLog("Exceeded Tracking threshold, will attempt to slew to the new target.\n");
1580 IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
1581 IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
1583 if ((err = Slew()))
1585 slewError(err);
1586 return (-1);
1589 fs_sexa(RAStr, targetRA, 2, 3600);
1590 fs_sexa(DecStr, targetDEC, 2, 3600);
1591 eqNum.s = IPS_BUSY;
1592 IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
1593 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1595 else
1597 IDLog("Tracking called, but tracking threshold not reached yet.\n");
1598 eqNum.s = IPS_OK;
1599 eqNum.np[0].value = currentRA;
1600 eqNum.np[1].value = currentDEC;
1602 if (lastSet != LX200_TRACK)
1603 IDSetNumber(&eqNum, "Tracking...");
1604 else
1605 IDSetNumber(&eqNum, NULL);
1607 lastSet = LX200_TRACK;
1608 break;
1610 // Sync
1611 case LX200_SYNC:
1612 lastSet = LX200_SYNC;
1613 eqNum.s = IPS_IDLE;
1615 if ( ( err = Sync(syncString) < 0) )
1617 IDSetNumber( &eqNum , "Synchronization failed.");
1618 return (-1);
1621 eqNum.s = IPS_OK;
1622 IDLog("Synchronization successful %s\n", syncString);
1623 IDSetNumber(&eqNum, "Synchronization successful.");
1624 break;
1626 // PARK
1627 // Set RA to LST and DEC to 0 degrees, slew, then change to 'guide' slew after slew is complete.
1628 case LX200_PARK:
1629 if (eqNum.s == IPS_BUSY)
1631 abortSlew();
1633 // sleep for 200 mseconds
1634 usleep(200000);
1637 if ((err = Slew()))
1639 slewError(err);
1640 return (-1);
1643 ParkSP.s = IPS_BUSY;
1644 eqNum.s = IPS_BUSY;
1645 IDSetNumber(&eqNum, NULL);
1646 IDSetSwitch(&ParkSP, "The telescope is slewing to park position. Turn off the telescope after park is complete.");
1648 break;
1652 return (0);
1656 int LX200Generic::getOnSwitch(ISwitchVectorProperty *sp)
1658 for (int i=0; i < sp->nsp ; i++)
1659 if (sp->sp[i].s == ISS_ON)
1660 return i;
1662 return -1;
1666 int LX200Generic::checkPower(ISwitchVectorProperty *sp)
1668 if (simulation) return 0;
1670 if (PowerSP.s != IPS_OK)
1672 if (!strcmp(sp->label, ""))
1673 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->name);
1674 else
1675 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->label);
1677 sp->s = IPS_IDLE;
1678 IDSetSwitch(sp, NULL);
1679 return -1;
1682 return 0;
1685 int LX200Generic::checkPower(INumberVectorProperty *np)
1687 if (simulation) return 0;
1689 if (PowerSP.s != IPS_OK)
1692 if (!strcmp(np->label, ""))
1693 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->name);
1694 else
1695 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->label);
1697 np->s = IPS_IDLE;
1698 IDSetNumber(np, NULL);
1699 return -1;
1702 return 0;
1706 int LX200Generic::checkPower(ITextVectorProperty *tp)
1709 if (simulation) return 0;
1711 if (PowerSP.s != IPS_OK)
1713 if (!strcmp(tp->label, ""))
1714 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->name);
1715 else
1716 IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->label);
1718 tp->s = IPS_IDLE;
1719 IDSetText(tp, NULL);
1720 return -1;
1723 return 0;
1727 void LX200Generic::powerTelescope()
1729 switch (PowerSP.sp[0].s)
1731 case ISS_ON:
1733 if (simulation)
1735 PowerSP.s = IPS_OK;
1736 IDSetSwitch (&PowerSP, "Simulated telescope is online.");
1737 updateTime();
1738 return;
1741 if (Connect(Port.tp[0].text))
1743 PowerS[0].s = ISS_OFF;
1744 PowerS[1].s = ISS_ON;
1745 IDSetSwitch (&PowerSP, "Error connecting to port %s\n", Port.tp[0].text);
1746 return;
1748 if (testTelescope())
1750 PowerS[0].s = ISS_OFF;
1751 PowerS[1].s = ISS_ON;
1752 IDSetSwitch (&PowerSP, "Error connecting to Telescope. Telescope is offline.");
1753 return;
1756 IDLog("telescope test successfful\n");
1757 PowerSP.s = IPS_OK;
1758 IDSetSwitch (&PowerSP, "Telescope is online. Retrieving basic data...");
1759 getBasicData();
1760 break;
1762 case ISS_OFF:
1763 PowerS[0].s = ISS_OFF;
1764 PowerS[1].s = ISS_ON;
1765 PowerSP.s = IPS_IDLE;
1766 IDSetSwitch (&PowerSP, "Telescope is offline.");
1767 IDLog("Telescope is offline.");
1768 Disconnect();
1769 break;
1775 void LX200Generic::slewError(int slewCode)
1777 OnCoordSetSw.s = IPS_IDLE;
1778 ParkSP.s = IPS_IDLE;
1779 IDSetSwitch(&ParkSP, NULL);
1781 if (slewCode == 1)
1782 IDSetSwitch (&OnCoordSetSw, "Object below horizon.");
1783 else if (slewCode == 2)
1784 IDSetSwitch (&OnCoordSetSw, "Object below the minimum elevation limit.");
1785 else
1786 IDSetSwitch (&OnCoordSetSw, "Slew failed.");
1791 void LX200Generic::getAlignment()
1794 if (PowerSP.s != IPS_OK)
1795 return;
1797 signed char align = ACK();
1798 if (align < 0)
1800 IDSetSwitch (&AlignmentSw, "Failed to get telescope alignment.");
1801 return;
1804 AlignmentS[0].s = ISS_OFF;
1805 AlignmentS[1].s = ISS_OFF;
1806 AlignmentS[2].s = ISS_OFF;
1808 switch (align)
1810 case 'P': AlignmentS[0].s = ISS_ON;
1811 break;
1812 case 'A': AlignmentS[1].s = ISS_ON;
1813 break;
1814 case 'L': AlignmentS[2].s = ISS_ON;
1815 break;
1818 AlignmentSw.s = IPS_OK;
1819 IDSetSwitch (&AlignmentSw, NULL);
1820 IDLog("ACK success %c\n", align);
1823 void LX200Generic::enableSimulation(bool enable)
1825 simulation = enable;
1827 if (simulation)
1828 IDLog("Warning: Simulation is activated.\n");
1829 else
1830 IDLog("Simulation is disabled.\n");
1833 void LX200Generic::updateTime()
1836 char cdate[32];
1837 double ctime;
1838 int h, m, s;
1839 int day, month, year, result;
1840 int UTC_h, UTC_month, UTC_year, UTC_day, daysInFeb;
1841 bool leapYear;
1843 tzset();
1845 UTCOffset = timezoneOffset();
1846 IDLog("Daylight: %s - TimeZone: %g\n", daylight ? "Yes" : "No", UTCOffset);
1849 if (simulation)
1851 sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", 1979, 6, 25, 3, 30, 30);
1852 IDLog("Telescope ISO date and time: %s\n", UTC[0].text);
1853 IDSetText(&Time, NULL);
1854 return;
1857 getLocalTime24(&ctime);
1858 getSexComponents(ctime, &h, &m, &s);
1860 UTC_h = h;
1862 if ( (result = getSDTime(&STime[0].value)) < 0)
1863 IDMessage(thisDevice, "Failed to retrieve siderial time from device.");
1865 getCalenderDate(cdate);
1867 result = sscanf(cdate, "%d/%d/%d", &year, &month, &day);
1868 if (result != 3) return;
1870 if (year % 4 == 0)
1872 if (year % 100 == 0)
1874 if (year % 400 == 0)
1875 leapYear = true;
1876 else
1877 leapYear = false;
1879 else
1880 leapYear = true;
1882 else
1883 leapYear = false;
1885 daysInFeb = leapYear ? 29 : 28;
1887 UTC_year = year;
1888 UTC_month = month;
1889 UTC_day = day;
1891 IDLog("day: %d - month %d - year: %d\n", day, month, year);
1893 // we'll have to convert telescope time to UTC manually starting from hour up
1894 // seems like a stupid way to do it.. oh well
1895 UTC_h = (int) UTCOffset + h;
1896 if (UTC_h < 0)
1898 UTC_h += 24;
1899 UTC_day--;
1901 else if (UTC_h > 24)
1903 UTC_h -= 24;
1904 UTC_day++;
1907 switch (UTC_month)
1909 case 1:
1910 case 8:
1911 if (UTC_day < 1)
1913 UTC_day = 31;
1914 UTC_month--;
1916 else if (UTC_day > 31)
1918 UTC_day = 1;
1919 UTC_month++;
1921 break;
1923 case 2:
1924 if (UTC_day < 1)
1926 UTC_day = 31;
1927 UTC_month--;
1929 else if (UTC_day > daysInFeb)
1931 UTC_day = 1;
1932 UTC_month++;
1934 break;
1936 case 3:
1937 if (UTC_day < 1)
1939 UTC_day = daysInFeb;
1940 UTC_month--;
1942 else if (UTC_day > 31)
1944 UTC_day = 1;
1945 UTC_month++;
1947 break;
1949 case 4:
1950 case 6:
1951 case 9:
1952 case 11:
1953 if (UTC_day < 1)
1955 UTC_day = 31;
1956 UTC_month--;
1958 else if (UTC_day > 30)
1960 UTC_day = 1;
1961 UTC_month++;
1963 break;
1965 case 5:
1966 case 7:
1967 case 10:
1968 case 12:
1969 if (UTC_day < 1)
1971 UTC_day = 30;
1972 UTC_month--;
1974 else if (UTC_day > 31)
1976 UTC_day = 1;
1977 UTC_month++;
1979 break;
1983 if (UTC_month < 1)
1985 UTC_month = 12;
1986 UTC_year--;
1988 else if (UTC_month > 12)
1990 UTC_month = 1;
1991 UTC_year++;
1994 /* Format it into ISO 8601 */
1995 sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", UTC_year, UTC_month, UTC_day, UTC_h, m, s);
1997 IDLog("Local telescope time: %02d:%02d:%02d\n", h, m , s);
1998 IDLog("Telescope SD Time is: %g\n", STime[0].value);
1999 IDLog("UTC date and time: %s\n", UTC[0].text);
2002 // Let's send everything to the client
2003 IDSetText(&Time, NULL);
2004 IDSetNumber(&SDTime, NULL);
2008 void LX200Generic::updateLocation()
2011 int dd = 0, mm = 0, err = 0;
2013 if ( (err = getSiteLatitude(&dd, &mm)) < 0)
2014 IDMessage(thisDevice, "Failed to get site latitude from device.");
2015 else
2017 if (dd > 0)
2018 geoNum.np[0].value = dd + mm/60.0;
2019 else
2020 geoNum.np[0].value = dd - mm/60.0;
2022 IDLog("Autostar Latitude: %d:%d\n", dd, mm);
2023 IDLog("INDI Latitude: %g\n", geoNum.np[0].value);
2026 if ( (err = getSiteLongitude(&dd, &mm)) < 0)
2027 IDMessage(thisDevice, "Failed to get site longitude from device.");
2028 else
2030 if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm/60.0);
2031 else geoNum.np[1].value = (dd - mm/60.0) * -1.0;
2033 IDLog("Autostar Longitude: %d:%d\n", dd, mm);
2034 IDLog("INDI Longitude: %g\n", geoNum.np[1].value);
2037 IDSetNumber (&geoNum, NULL);