moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indi / apmount.cpp
blobd8463fc13879d7feca157c6f147bbfcb36264c99
1 #if 0
2 Astro-Physics driver
3 Copyright (C) 2005 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 "apmount.h"
36 ** Return the timezone offset in hours (as a double, so fractional
37 ** hours are possible, for instance in Newfoundland). Also sets
38 ** daylight on non-Linux systems to record whether DST is in effect.
42 #if !(TIMEZONE_IS_INT)
43 static int daylight = 0;
44 #endif
46 static inline double timezoneOffset()
48 /*
49 ** In Linux, there's a timezone variable that holds the timezone offset;
50 ** Otherwise, we need to make a little detour. The directions of the offset
51 ** are different: CET is -3600 in Linux and +3600 elsewhere.
53 #if TIMEZONE_IS_INT
54 return timezone / (60 * 60);
55 #else
56 time_t now;
57 struct tm *tm;
58 now = time(NULL);
59 tm = localtime(&now);
60 daylight = tm->tm_isdst;
61 return -(tm->tm_gmtoff) / (60 * 60);
62 #endif
65 APMount *telescope = NULL;
66 int MaxReticleFlashRate = 3;
67 extern char* me;
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
77 #define COMM_GROUP "Communication"
78 #define BASIC_GROUP "Main Control"
79 #define MOVE_GROUP "Movement Control"
80 #define FOCUS_GROUP "Focus Control"
82 #define mydev "Astro-Physics"
83 #define currentRA EqN[0].value
84 #define currentDEC EqN[1].value
86 #define RA_THRESHOLD 0.01
87 #define DEC_THRESHOLD 0.05
88 #define LX200_SLEW 0
89 #define LX200_TRACK 1
90 #define LX200_SYNC 2
91 #define LX200_PARK 3
93 static void ISPoll(void *);
94 static void retryConnection(void *);
96 /*INDI controls */
99 /* send client definitions of all properties */
100 void ISInit()
102 static int isInit=0;
104 if (isInit)
105 return;
107 isInit = 1;
109 telescope = new APMount();
110 IEAddTimer (POLLMS, ISPoll, NULL);
113 void ISGetProperties (const char *dev)
115 ISInit();
116 telescope->ISGetProperties(dev);
119 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
121 ISInit();
122 telescope->ISNewSwitch(dev, name, states, names, n);
125 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
127 ISInit();
128 telescope->ISNewText(dev, name, texts, names, n);
131 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
133 ISInit();
134 telescope->ISNewNumber(dev, name, values, names, n);
137 void ISPoll (void */*p*/)
139 telescope->ISPoll();
140 IEAddTimer (POLLMS, ISPoll, NULL);
143 void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/)
146 /**************************************************
147 *** AP Mount
148 ***************************************************/
150 APMount::APMount()
152 struct tm *utp;
153 time_t t;
154 time (&t);
155 utp = gmtime (&t);
157 initProperties();
159 currentSiteNum = 1;
160 trackingMode = 2;
161 lastSet = -1;
162 fault = false;
163 simulation = false;
164 targetRA = 0;
165 targetDEC = 0;
166 currentSet = 0;
167 UTCOffset = 0;
168 lastMove[0] = lastMove[1] = lastMove[2] = lastMove[3] = 0;
170 localTM = new tm;
172 utp->tm_mon += 1;
173 utp->tm_year += 1900;
174 JD = UTtoJD(utp);
176 IDLog("Julian Day is %g\n", JD);
177 IDLog("Initilizing from Astro-Physics device...\n");
178 IDLog("Driver Version: 2005-04-21\n");
180 //enableSimulation(true);
183 void APMount::initProperties()
186 fillSwitch(&AlignmentS[0], "Polar", "", ISS_ON);
187 fillSwitch(&AlignmentS[1], "AltAz", "", ISS_OFF);
188 fillSwitchVector(&AlignmentSP, AlignmentS, NARRAY(AlignmentS), mydev, "Alignment", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
190 fillSwitch(&PowerS[0], "CONNECT", "Connect", ISS_OFF);
191 fillSwitch(&PowerS[1], "DISCONNECT", "Disconnect", ISS_ON);
192 fillSwitchVector(&PowerSP, PowerS, NARRAY(PowerS), mydev, "CONNECTION", "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
194 fillSwitch(&OnCoordSetS[0], "SLEW", "Slew", ISS_ON);
195 fillSwitch(&OnCoordSetS[1], "TRACK", "Track", ISS_OFF);
196 fillSwitch(&OnCoordSetS[2], "SYNC", "Sync", ISS_OFF);
197 fillSwitchVector(&OnCoordSetSP, OnCoordSetS, NARRAY(OnCoordSetS), mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
199 fillSwitch(&TrackModeS[0], "Lunar", "", ISS_OFF);
200 fillSwitch(&TrackModeS[1], "Solar", "", ISS_OFF);
201 fillSwitch(&TrackModeS[2], "Sideral", "", ISS_ON);
202 fillSwitch(&TrackModeS[3], "Zero", "", ISS_OFF);
203 fillSwitchVector(&TrackModeSP, TrackModeS, NARRAY(TrackModeS), mydev, "Tracking Mode", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
205 fillSwitch(&AbortSlewS[0], "ABORT", "Abort", ISS_OFF);
206 fillSwitchVector(&AbortSlewSP, AbortSlewS, NARRAY(AbortSlewS), mydev, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
208 fillSwitch(&ParkS[0], "PARK", "Park", ISS_OFF);
209 fillSwitch(&ParkS[1], "UNPARK", "Unpark", ISS_OFF);
210 fillSwitchVector(&ParkSP, ParkS, NARRAY(ParkS), mydev, "PARK", "Park Scope", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
212 fillSwitch(&MovementS[0], "N", "North", ISS_OFF);
213 fillSwitch(&MovementS[1], "W", "West", ISS_OFF);
214 fillSwitch(&MovementS[2], "E", "East", ISS_OFF);
215 fillSwitch(&MovementS[3], "S", "South", ISS_OFF);
216 fillSwitchVector(&MovementSP, MovementS, NARRAY(MovementS), mydev, "MOVEMENT", "Move toward", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
218 fillSwitch(&FocusSpeedS[0], "FOCUS_HALT", "Halt", ISS_ON);
219 fillSwitch(&FocusSpeedS[1], "FOCUS_FAST", "Fast", ISS_ON);
220 fillSwitch(&FocusSpeedS[2], "FOCUS_SLOW", "Slow", ISS_ON);
221 fillSwitchVector(&FocusSpeedSP, FocusSpeedS, NARRAY(FocusSpeedS), mydev, "FOCUS_SPEED", "Speed", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
223 fillSwitch(&FocusMotionS[0], "FOCUS_IN", "Focus in", ISS_OFF);
224 fillSwitch(&FocusMotionS[1], "FOCUS_OUT", "Focus out", ISS_OFF);
225 fillSwitchVector(&FocusMotionSP, FocusMotionS, NARRAY(FocusMotionS), mydev, "FOCUS_MOTION", "Motion", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
227 fillText(&PortT[0], "PORT", "Port", "/dev/ttyS0");
228 fillTextVector(&PortTP, PortT, NARRAY(PortT), mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_IDLE);
230 fillText(&UTCT[0], "UTC", "", "YYYY-MM-DDTHH:MM:SS");
231 fillTextVector(&TimeTP, UTCT, NARRAY(UTCT), mydev, "TIME", "UTC Time", COMM_GROUP, IP_RW, 0, IPS_IDLE);
233 fillNumber(&EqN[0], "RA", "RA H:M:S", "%10.6m", 0., 24., 0., 0.);
234 fillNumber(&EqN[1], "DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0.);
235 fillNumberVector(&EqNP, EqN, NARRAY(EqN), mydev, "EQUATORIAL_EOD_COORD" , "Equatorial JNow", BASIC_GROUP, IP_RW, 0, IPS_IDLE);
237 fillNumber(&GeoN[0], "LAT", "Lat. D:M:S +N", "%10.6m", -90., 90., 0., 0.);
238 fillNumber(&GeoN[1], "LONG", "Long. D:M:S +E", "%10.6m", 0., 360., 0., 0.);
239 fillNumberVector(&GeoNP, GeoN, NARRAY(GeoN), mydev, "GEOGRAPHIC_COORD", "Geographic Location", COMM_GROUP, IP_RW, 0, IPS_IDLE);
241 fillNumber(&FocusTimerN[0], "TIMEOUT", "Timeout (s)", "%10.6m", 0., 120., 1., 0.);
242 fillNumberVector(&FocusTimerNP, FocusTimerN, NARRAY(FocusTimerN), mydev, "FOCUS_TIMEOUT", "Focus Timer", FOCUS_GROUP, IP_RW, 0, IPS_IDLE);
244 fillNumber(&SDTimeN[0], "LST", "Sidereal time", "%10.6m" , 0.,24.,0.,0.);
245 fillNumberVector(&SDTimeNP, SDTimeN, NARRAY(SDTimeN), mydev, "SDTIME", "Sidereal Time", COMM_GROUP, IP_RW, 0, IPS_IDLE);
250 void APMount::ISGetProperties(const char *dev)
253 if (dev && strcmp (mydev, dev))
254 return;
256 // COMM_GROUP
257 IDDefSwitch(&PowerSP, NULL);
258 IDDefText(&PortTP, NULL);
259 IDDefSwitch(&AlignmentSP, NULL);
260 IDDefText(&TimeTP, NULL);
261 IDDefNumber(&SDTimeNP, NULL);
262 IDDefNumber(&GeoNP, NULL);
264 // MAIN CONTROL
265 IDDefNumber(&EqNP, NULL);
266 IDDefSwitch(&OnCoordSetSP, NULL);
267 IDDefSwitch(&AbortSlewSP, NULL);
268 IDDefSwitch(&ParkSP, NULL);
270 // MOVEMENT CONTROL
271 IDDefSwitch(&TrackModeSP, NULL);
272 IDDefSwitch(&MovementSP, NULL);
274 // FOCUS CONTROL
275 IDDefSwitch(&FocusSpeedSP, NULL);
276 IDDefSwitch(&FocusMotionSP, NULL);
277 IDDefNumber(&FocusTimerNP, NULL);
279 /* Send the basic data to the new client if the previous client(s) are already connected. */
280 if (PowerSP.s == IPS_OK)
281 getBasicData();
285 void APMount::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int /*n*/)
287 int err;
288 struct tm *ltp = new tm;
289 struct tm utm;
290 time_t ltime;
291 time (&ltime);
292 localtime_r (&ltime, ltp);
293 IText *tp;
295 // ignore if not ours
296 if (strcmp (dev, mydev))
297 return;
299 // Port name
300 if (!strcmp(name, PortTP.name) )
302 PortTP.s = IPS_OK;
303 tp = IUFindText( &PortTP, names[0] );
304 if (!tp)
305 return;
307 IUSaveText(tp, texts[0]);
308 IDSetText (&PortTP, NULL);
309 return;
312 // Time
313 if (!strcmp (name, TimeTP.name))
315 if (checkPower(&TimeTP))
316 return;
318 if (extractISOTime(texts[0], &utm) < 0)
320 TimeTP.s = IPS_IDLE;
321 IDSetText(&TimeTP , "Time invalid");
322 return;
324 utm.tm_mon += 1;
325 ltp->tm_mon += 1;
326 utm.tm_year += 1900;
327 ltp->tm_year += 1900;
329 tzset();
331 UTCOffset = timezoneOffset();
333 IDLog("local time is %02d:%02d:%02d\nUTCOffset: %g\n", ltp->tm_hour, ltp->tm_min, ltp->tm_sec, UTCOffset);
335 getSDTime(&SDTimeN[0].value);
336 IDSetNumber(&SDTimeNP, NULL);
338 if ( ( err = setUTCOffset(UTCOffset) < 0) )
340 TimeTP.s = IPS_ALERT;
341 IDSetText( &TimeTP , "Setting UTC Offset failed.");
342 return;
345 if ( ( err = setLocalTime(ltp->tm_hour, ltp->tm_min, ltp->tm_sec) < 0) )
347 handleError(&TimeTP, err, "Setting local time");
348 return;
351 tp = IUFindText(&TimeTP, names[0]);
352 if (!tp)
353 return;
354 IUSaveText(tp, texts[0]);
355 TimeTP.s = IPS_OK;
357 // update JD
358 JD = UTtoJD(&utm);
360 IDLog("New JD is %f\n", (float) JD);
362 if ((localTM->tm_mday == ltp->tm_mday ) && (localTM->tm_mon == ltp->tm_mon) &&
363 (localTM->tm_year == ltp->tm_year))
365 IDSetText(&TimeTP , "Time updated to %s", texts[0]);
366 return;
369 delete (localTM);
370 localTM = ltp;
372 if ( ( err = setCalenderDate(ltp->tm_mday, ltp->tm_mon, ltp->tm_year) < 0) )
374 handleError(&TimeTP, err, "Setting local date.");
375 return;
378 IDSetText(&TimeTP , "Date changed, updating planetary data...");
383 void APMount::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
385 int h =0, m =0, s=0, err;
386 double newRA =0, newDEC =0;
388 // ignore if not ours //
389 if (strcmp (dev, mydev))
390 return;
392 if (!strcmp (name, EqNP.name))
394 int i=0, nset=0;
396 if (checkPower(&EqNP))
397 return;
399 for (nset = i = 0; i < n; i++)
401 INumber *eqp = IUFindNumber (&EqNP, names[i]);
402 if (eqp == &EqN[0])
404 newRA = values[i];
405 nset += newRA >= 0 && newRA <= 24.0;
406 } else if (eqp == &EqN[1])
408 newDEC = values[i];
409 nset += newDEC >= -90.0 && newDEC <= 90.0;
413 if (nset == 2)
415 char RAStr[32], DecStr[32];
417 fs_sexa(RAStr, newRA, 2, 3600);
418 fs_sexa(DecStr, newDEC, 2, 3600);
420 IDLog("We received J2000 RA %g - DEC %g\n", newRA, newDEC);
421 IDLog("We received J2000 RA %s - DEC %s\n", RAStr, DecStr);
423 if ( (err = setObjectRA(newRA)) < 0 || ( err = setObjectDEC(newDEC)) < 0)
425 handleError(&EqNP, err, "Setting RA/DEC");
426 return;
429 targetRA = newRA;
430 targetDEC = newDEC;
432 if (MovementSP.s == IPS_BUSY)
434 for (int i=0; i < 4; i++)
436 lastMove[i] = 0;
437 MovementS[i].s = ISS_OFF;
440 MovementSP.s = IPS_IDLE;
441 IDSetSwitch(&MovementSP, NULL);
444 if (handleCoordSet())
446 EqNP.s = IPS_IDLE;
447 IDSetNumber(&EqNP, NULL);
450 } // end nset
451 else
453 EqNP.s = IPS_IDLE;
454 IDSetNumber(&EqNP, "RA or Dec missing or invalid");
457 return;
458 } /* end EqNP */
460 // Sideral Time
461 if ( !strcmp (name, SDTimeNP.name) )
464 if (checkPower(&SDTimeNP))
465 return;
467 if (values[0] < 0.0 || values[0] > 24.0)
469 SDTimeNP.s = IPS_IDLE;
470 IDSetNumber(&SDTimeNP , "Time invalid");
471 return;
474 getSexComponents(values[0], &h, &m, &s);
475 IDLog("Time is %02d:%02d:%02d\n", h, m, s);
477 if ( ( err = setSDTime(h, m, s) < 0) )
479 handleError(&SDTimeNP, err, "Setting siderial time");
480 return;
483 SDTimeNP.np[0].value = values[0];
484 SDTimeNP.s = IPS_OK;
486 IDSetNumber(&SDTimeNP , "Sidereal time updated to %02d:%02d:%02d", h, m, s);
488 return;
491 // Geographical location
492 if (!strcmp (name, GeoNP.name))
494 // new geographic coords
495 double newLong = 0, newLat = 0;
496 int i, nset;
497 char msg[128];
499 if (checkPower(&GeoNP))
500 return;
502 for (nset = i = 0; i < n; i++)
504 INumber *geop = IUFindNumber (&GeoNP, names[i]);
505 if (geop == &GeoN[0])
507 newLat = values[i];
508 nset += newLat >= -90.0 && newLat <= 90.0;
509 } else if (geop == &GeoN[1])
511 newLong = values[i];
512 nset += newLong >= 0.0 && newLong < 360.0;
516 if (nset == 2)
518 char l[32], L[32];
519 GeoNP.s = IPS_OK;
520 fs_sexa (l, newLat, 3, 3600);
521 fs_sexa (L, newLong, 4, 3600);
523 if ( ( err = setSiteLongitude(360.0 - newLong) < 0) )
525 handleError(&GeoNP, err, "Setting site coordinates");
526 return;
529 setSiteLatitude(newLat);
530 GeoNP.np[0].value = newLat;
531 GeoNP.np[1].value = newLong;
532 snprintf (msg, sizeof(msg), "Site location updated to Lat %.32s - Long %.32s", l, L);
533 } else
535 GeoNP.s = IPS_IDLE;
536 strcpy(msg, "Lat or Long missing or invalid");
538 IDSetNumber (&GeoNP, "%s", msg);
539 return;
542 // Focus TImer
543 if (!strcmp(name, FocusTimerNP.name))
545 if (checkPower(&FocusTimerNP))
546 return;
548 // Don't update if busy
549 if (FocusTimerNP.s == IPS_BUSY)
550 return;
552 IUUpdateNumbers(&FocusTimerNP, values, names, n);
554 FocusTimerNP.s = IPS_OK;
556 IDSetNumber(&FocusTimerNP, NULL);
557 IDLog("Setting focus timer to %g\n", FocusTimerN[0].value);
559 return;
565 void APMount::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
567 int index;
568 int err;
569 char combinedDir[64];
570 ISwitch *swp;
572 // ignore if not ours //
573 if (strcmp (mydev, dev))
574 return;
576 // Connection
577 if (!strcmp (name, PowerSP.name))
579 IUResetSwitches(&PowerSP);
580 IUUpdateSwitches(&PowerSP, states, names, n);
581 powerTelescope();
582 return;
585 // Coord set
586 if (!strcmp(name, OnCoordSetSP.name))
588 if (checkPower(&OnCoordSetSP))
589 return;
591 IUResetSwitches(&OnCoordSetSP);
592 IUUpdateSwitches(&OnCoordSetSP, states, names, n);
593 currentSet = getOnSwitch(&OnCoordSetSP);
594 OnCoordSetSP.s = IPS_OK;
595 IDSetSwitch(&OnCoordSetSP, NULL);
598 // Parking
599 if (!strcmp(name, ParkSP.name))
601 if (checkPower(&ParkSP))
602 return;
604 IUResetSwitches(&ParkSP);
605 IUUpdateSwitches(&ParkSP, states, names, n);
606 index = getOnSwitch(&ParkSP);
608 // Park Command
609 if (index == 0)
610 APPark();
611 else
612 // Unpark
614 char **tmtexts = (char **) malloc(1);
615 char **tmtp = (char **) malloc(1);
616 tmtexts[0] = (char *) malloc (32);
617 tmtp[0] = (char *) malloc (32);
619 strcpy(tmtexts[0], timestamp());
620 strcpy(tmtp[0], "UTC");
622 // Update date and time before unparking
623 ISNewText(mydev, "TIME", tmtexts, tmtp, 1);
624 APUnpark();
626 free (tmtexts);
627 free (tmtp);
630 ParkSP.s = IPS_OK;
631 IDSetSwitch(&ParkSP, (index == 0) ? "Parking..." : "Unparking...");
632 return;
635 // Abort Slew
636 if (!strcmp (name, AbortSlewSP.name))
638 if (checkPower(&AbortSlewSP))
640 AbortSlewSP.s = IPS_IDLE;
641 IDSetSwitch(&AbortSlewSP, NULL);
642 return;
645 IUResetSwitches(&AbortSlewSP);
646 abortSlew();
648 if (EqNP.s == IPS_BUSY)
650 AbortSlewSP.s = IPS_OK;
651 EqNP.s = IPS_IDLE;
652 IDSetSwitch(&AbortSlewSP, "Slew aborted.");
653 IDSetNumber(&EqNP, NULL);
655 else if (MovementSP.s == IPS_BUSY)
658 for (int i=0; i < 4; i++)
659 lastMove[i] = 0;
661 MovementSP.s = IPS_IDLE;
662 AbortSlewSP.s = IPS_OK;
663 EqNP.s = IPS_IDLE;
664 IUResetSwitches(&MovementSP);
665 IUResetSwitches(&AbortSlewSP);
666 IDSetSwitch(&AbortSlewSP, "Slew aborted.");
667 IDSetSwitch(&MovementSP, NULL);
668 IDSetNumber(&EqNP, NULL);
670 else
672 IUResetSwitches(&MovementSP);
673 AbortSlewSP.s = IPS_OK;
674 IDSetSwitch(&AbortSlewSP, NULL);
677 return;
680 // Alignment
681 if (!strcmp (name, AlignmentSP.name))
683 if (checkPower(&AlignmentSP))
684 return;
686 IUResetSwitches(&AlignmentSP);
687 IUUpdateSwitches(&AlignmentSP, states, names, n);
688 index = getOnSwitch(&AlignmentSP);
690 if ( ( err = setAlignmentMode(index) < 0) )
692 handleError(&AlignmentSP, err, "Setting alignment");
693 return;
696 AlignmentSP.s = IPS_OK;
697 IDSetSwitch (&AlignmentSP, NULL);
698 return;
702 // Focus speed
703 if (!strcmp (name, FocusSpeedSP.name))
705 if (checkPower(&FocusSpeedSP))
706 return;
708 IUResetSwitches(&FocusSpeedSP);
709 IUUpdateSwitches(&FocusSpeedSP, states, names, n);
710 index = getOnSwitch(&FocusSpeedSP);
712 // AP doesn't have medium focus speed. Change medium to slow
713 if (index == 2) index = 3;
715 if ( ( err = setFocuserSpeedMode(index) < 0) )
717 handleError(&FocusSpeedSP, err, "Setting focuser speed mode");
718 return;
721 /* disable timer and motion */
722 if (index == 0)
724 IUResetSwitches(&FocusMotionSP);
725 FocusMotionSP.s = IPS_IDLE;
726 FocusTimerNP.s = IPS_IDLE;
727 IDSetSwitch(&FocusMotionSP, NULL);
728 IDSetNumber(&FocusTimerNP, NULL);
732 FocusSpeedSP.s = IPS_OK;
733 IDSetSwitch(&FocusSpeedSP, NULL);
734 return;
737 // Focus Motion
738 if (!strcmp (name, FocusMotionSP.name))
740 if (checkPower(&FocusMotionSP))
741 return;
743 IUResetSwitches(&FocusMotionSP);
745 // If speed is "halt"
746 if (FocusSpeedS[0].s == ISS_ON)
748 FocusMotionSP.s = IPS_IDLE;
749 IDSetSwitch(&FocusMotionSP, NULL);
750 return;
753 IUUpdateSwitches(&FocusMotionSP, states, names, n);
754 index = getOnSwitch(&FocusMotionSP);
756 if ( ( err = setFocuserMotion(index) < 0) )
758 handleError(&FocusMotionSP, err, "Setting focuser speed");
759 return;
762 FocusMotionSP.s = IPS_BUSY;
764 // with a timer
765 if (FocusTimerN[0].value > 0)
766 FocusTimerNP.s = IPS_BUSY;
768 IDSetSwitch(&FocusMotionSP, NULL);
769 return;
772 // Movement
773 if (!strcmp (name, MovementSP.name))
775 if (checkPower(&MovementSP))
776 return;
778 index = -1;
779 IUUpdateSwitches(&MovementSP, states, names, n);
780 swp = IUFindSwitch(&MovementSP, names[0]);
782 if (!swp)
784 abortSlew();
785 IUResetSwitches(&MovementSP);
786 MovementSP.s = IPS_IDLE;
787 IDSetSwitch(&MovementSP, NULL);
790 if (swp == &MovementS[0]) index = 0;
791 else if (swp == &MovementS[1]) index = 1;
792 else if (swp == &MovementS[2]) index = 2;
793 else index = 3;
795 lastMove[index] = lastMove[index] == 0 ? 1 : 0;
796 if (lastMove[index] == 0)
797 MovementS[index].s = ISS_OFF;
799 // North/South movement is illegal
800 if (lastMove[LX200_NORTH] && lastMove[LX200_SOUTH])
802 abortSlew();
803 for (int i=0; i < 4; i++)
804 lastMove[i] = 0;
806 IUResetSwitches(&MovementSP);
807 MovementSP.s = IPS_IDLE;
808 IDSetSwitch(&MovementSP, "Slew aborted.");
809 return;
812 // East/West movement is illegal
813 if (lastMove[LX200_EAST] && lastMove[LX200_WEST])
815 abortSlew();
816 for (int i=0; i < 4; i++)
817 lastMove[i] = 0;
819 IUResetSwitches(&MovementSP);
820 MovementSP.s = IPS_IDLE;
821 IDSetSwitch(&MovementSP, "Slew aborted.");
822 return;
825 IDLog("We have switch %d \n ", index);
826 IDLog("NORTH: %d -- WEST: %d -- EAST: %d -- SOUTH %d\n", lastMove[0], lastMove[1], lastMove[2], lastMove[3]);
828 if (lastMove[index] == 1)
830 IDLog("issuing a move command\n");
831 if ( ( err = MoveTo(index) < 0) )
833 handleError(&MovementSP, err, "Setting motion direction");
834 return;
837 else
838 HaltMovement(index);
840 if (!lastMove[0] && !lastMove[1] && !lastMove[2] && !lastMove[3])
841 MovementSP.s = IPS_IDLE;
843 if (lastMove[index] == 0)
844 IDSetSwitch(&MovementSP, "Moving toward %s aborted.", Direction[index]);
845 else
847 MovementSP.s = IPS_BUSY;
848 if (lastMove[LX200_NORTH] && lastMove[LX200_WEST])
849 strcpy(combinedDir, "North West");
850 else if (lastMove[LX200_NORTH] && lastMove[LX200_EAST])
851 strcpy(combinedDir, "North East");
852 else if (lastMove[LX200_SOUTH] && lastMove[LX200_WEST])
853 strcpy(combinedDir, "South West");
854 else if (lastMove[LX200_SOUTH] && lastMove[LX200_EAST])
855 strcpy(combinedDir, "South East");
856 else
857 strcpy(combinedDir, Direction[index]);
859 IDSetSwitch(&MovementSP, "Moving %s...", combinedDir);
861 return;
864 // Tracking mode
865 if (!strcmp (name, TrackModeSP.name))
867 if (checkPower(&TrackModeSP))
868 return;
870 IUResetSwitches(&TrackModeSP);
871 IUUpdateSwitches(&TrackModeSP, states, names, n);
872 trackingMode = getOnSwitch(&TrackModeSP);
874 if ( ( err = selectAPTrackingMode(trackingMode) < 0) )
876 handleError(&TrackModeSP, err, "Setting tracking mode.");
877 return;
881 TrackModeSP.s = IPS_OK;
882 IDSetSwitch(&TrackModeSP, NULL);
883 return;
888 void APMount::handleError(ISwitchVectorProperty *svp, int err, const char *msg)
891 svp->s = IPS_ALERT;
893 /* First check to see if the telescope is connected */
894 if (testAP())
896 /* The telescope is off locally */
897 PowerS[0].s = ISS_OFF;
898 PowerS[1].s = ISS_ON;
899 PowerSP.s = IPS_BUSY;
900 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
902 IDSetSwitch(svp, NULL);
903 IEAddTimer(10000, retryConnection, NULL);
904 return;
907 /* If the error is a time out, then the device doesn't support this property or busy*/
908 if (err == -2)
910 svp->s = IPS_ALERT;
911 IDSetSwitch(svp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
913 else
914 /* Changing property failed, user should retry. */
915 IDSetSwitch( svp , "%s failed.", msg);
917 fault = true;
920 void APMount::handleError(INumberVectorProperty *nvp, int err, const char *msg)
923 nvp->s = IPS_ALERT;
925 /* First check to see if the telescope is connected */
926 if (testAP())
928 /* The telescope is off locally */
929 PowerS[0].s = ISS_OFF;
930 PowerS[1].s = ISS_ON;
931 PowerSP.s = IPS_BUSY;
932 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
934 IDSetNumber(nvp, NULL);
935 IEAddTimer(10000, retryConnection, NULL);
936 return;
939 /* If the error is a time out, then the device doesn't support this property */
940 if (err == -2)
942 nvp->s = IPS_ALERT;
943 IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
945 else
946 /* Changing property failed, user should retry. */
947 IDSetNumber( nvp , "%s failed.", msg);
949 fault = true;
952 void APMount::handleError(ITextVectorProperty *tvp, int err, const char *msg)
955 tvp->s = IPS_ALERT;
957 /* First check to see if the telescope is connected */
958 if (testAP())
960 /* The telescope is off locally */
961 PowerS[0].s = ISS_OFF;
962 PowerS[1].s = ISS_ON;
963 PowerSP.s = IPS_BUSY;
964 IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
966 IDSetText(tvp, NULL);
967 IEAddTimer(10000, retryConnection, NULL);
968 return;
971 /* If the error is a time out, then the device doesn't support this property */
972 if (err == -2)
974 tvp->s = IPS_ALERT;
975 IDSetText(tvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
978 else
979 /* Changing property failed, user should retry. */
980 IDSetText( tvp , "%s failed.", msg);
982 fault = true;
985 void APMount::correctFault()
988 fault = false;
989 IDMessage(mydev, "Telescope is online.");
993 bool APMount::isTelescopeOn(void)
995 if (simulation) return true;
997 return (PowerSP.sp[0].s == ISS_ON);
1000 static void retryConnection(void * p)
1002 p=p;
1004 if (testAP())
1005 telescope->connectionLost();
1006 else
1007 telescope->connectionResumed();
1010 void APMount::ISPoll()
1012 double dx, dy;
1013 int err=0;
1015 if (!isTelescopeOn())
1016 return;
1018 switch (EqNP.s)
1020 case IPS_IDLE:
1021 getLX200RA(&currentRA);
1022 getLX200DEC(&currentDEC);
1024 if ( fabs (currentRA - lastRA) > 0.01 || fabs (currentDEC - lastDEC) > 0.01)
1026 lastRA = currentRA;
1027 lastDEC = currentDEC;
1028 IDSetNumber (&EqNP, NULL);
1030 break;
1032 case IPS_BUSY:
1033 getLX200RA(&currentRA);
1034 getLX200DEC(&currentDEC);
1035 dx = targetRA - currentRA;
1036 dy = targetDEC - currentDEC;
1038 IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
1039 IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
1041 // Wait until acknowledged or within threshold
1042 if (fabs(dx) <= RA_THRESHOLD && fabs(dy) <= DEC_THRESHOLD)
1045 lastRA = currentRA;
1046 lastDEC = currentDEC;
1047 IUResetSwitches(&OnCoordSetSP);
1048 OnCoordSetSP.s = IPS_OK;
1049 EqNP.s = IPS_OK;
1050 IDSetNumber (&EqNP, NULL);
1052 switch (currentSet)
1054 case LX200_SLEW:
1055 OnCoordSetSP.sp[0].s = ISS_ON;
1056 IDSetSwitch (&OnCoordSetSP, "Slew is complete.");
1057 break;
1059 case LX200_TRACK:
1060 OnCoordSetSP.sp[1].s = ISS_ON;
1061 IDSetSwitch (&OnCoordSetSP, "Slew is complete. Tracking...");
1062 break;
1064 case LX200_SYNC:
1065 break;
1068 } else
1069 IDSetNumber (&EqNP, NULL);
1070 break;
1072 case IPS_OK:
1074 if ( (err = getLX200RA(&currentRA)) < 0 || (err = getLX200DEC(&currentDEC)) < 0)
1076 handleError(&EqNP, err, "Getting RA/DEC");
1077 return;
1080 if (fault)
1081 correctFault();
1083 if ( (currentRA != lastRA) || (currentDEC != lastDEC))
1085 lastRA = currentRA;
1086 lastDEC = currentDEC;
1087 IDSetNumber (&EqNP, NULL);
1089 break;
1092 case IPS_ALERT:
1093 break;
1096 switch (MovementSP.s)
1098 case IPS_IDLE:
1099 break;
1100 case IPS_BUSY:
1101 getLX200RA(&currentRA);
1102 getLX200DEC(&currentDEC);
1103 IDSetNumber (&EqNP, NULL);
1104 break;
1105 case IPS_OK:
1106 break;
1107 case IPS_ALERT:
1108 break;
1111 switch (FocusTimerNP.s)
1113 case IPS_IDLE:
1114 break;
1116 case IPS_BUSY:
1117 FocusTimerN[0].value--;
1119 if (FocusTimerN[0].value == 0)
1122 if ( ( err = setFocuserSpeedMode(0) < 0) )
1124 handleError(&FocusSpeedSP, err, "setting focuser speed mode");
1125 IDLog("Error setting focuser speed mode\n");
1126 return;
1130 FocusMotionSP.s = IPS_IDLE;
1131 FocusTimerNP.s = IPS_OK;
1132 FocusSpeedSP.s = IPS_OK;
1134 IUResetSwitches(&FocusMotionSP);
1135 IUResetSwitches(&FocusSpeedSP);
1136 FocusSpeedS[0].s = ISS_ON;
1138 IDSetSwitch(&FocusSpeedSP, NULL);
1139 IDSetSwitch(&FocusMotionSP, NULL);
1142 IDSetNumber(&FocusTimerNP, NULL);
1143 break;
1145 case IPS_OK:
1146 break;
1148 case IPS_ALERT:
1149 break;
1155 void APMount::getBasicData()
1158 // #1 Save current time
1159 IUSaveText(&UTCT[0], timestamp());
1160 IDLog("PC UTC time is %s\n", UTCT[0].text);
1162 // #2 Make sure format is long
1163 checkLX200Format();
1164 timeFormat = LX200_24;
1166 // #3 Get current RA/DEC
1167 getLX200RA(&currentRA);
1168 getLX200DEC(&currentDEC);
1169 targetRA = currentRA;
1170 targetDEC = currentDEC;
1172 IDSetNumber (&EqNP, NULL);
1173 updateLocation();
1174 updateTime();
1178 int APMount::handleCoordSet()
1181 int err;
1182 char syncString[256];
1183 char RAStr[32], DecStr[32];
1184 double dx, dy;
1186 switch (currentSet)
1189 // Slew
1190 case LX200_SLEW:
1191 lastSet = LX200_SLEW;
1192 if (EqNP.s == IPS_BUSY)
1194 IDLog("Aboring Slew\n");
1195 abortSlew();
1197 // sleep for 100 mseconds
1198 usleep(100000);
1201 if ((err = Slew()))
1203 slewError(err);
1204 return (-1);
1207 EqNP.s = IPS_BUSY;
1208 fs_sexa(RAStr, targetRA, 2, 3600);
1209 fs_sexa(DecStr, targetDEC, 2, 3600);
1210 IDSetNumber(&EqNP, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
1211 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1212 break;
1214 // Track
1215 case LX200_TRACK:
1216 IDLog("We're in LX200_TRACK\n");
1217 if (EqNP.s == IPS_BUSY)
1219 IDLog("Aboring Slew\n");
1220 abortSlew();
1222 // sleep for 200 mseconds
1223 usleep(200000);
1226 dx = fabs ( targetRA - currentRA );
1227 dy = fabs (targetDEC - currentDEC);
1230 if (dx >= TRACKING_THRESHOLD || dy >= TRACKING_THRESHOLD)
1232 IDLog("Exceeded Tracking threshold, will attempt to slew to the new target.\n");
1233 IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
1234 IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
1236 if ((err = Slew()))
1238 slewError(err);
1239 return (-1);
1242 fs_sexa(RAStr, targetRA, 2, 3600);
1243 fs_sexa(DecStr, targetDEC, 2, 3600);
1244 EqNP.s = IPS_BUSY;
1245 IDSetNumber(&EqNP, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
1246 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1248 else
1250 IDLog("Tracking called, but tracking threshold not reached yet.\n");
1251 EqNP.s = IPS_OK;
1252 EqNP.np[0].value = currentRA;
1253 EqNP.np[1].value = currentDEC;
1255 if (lastSet != LX200_TRACK)
1256 IDSetNumber(&EqNP, "Tracking...");
1257 else
1258 IDSetNumber(&EqNP, NULL);
1260 lastSet = LX200_TRACK;
1261 break;
1263 // Sync
1264 case LX200_SYNC:
1265 lastSet = LX200_SYNC;
1266 EqNP.s = IPS_IDLE;
1268 if ( ( err = Sync(syncString) < 0) )
1270 IDSetNumber( &EqNP , "Synchronization failed.");
1271 return (-1);
1274 EqNP.s = IPS_OK;
1275 IDLog("Synchronization successful %s\n", syncString);
1276 IDSetNumber(&EqNP, "Synchronization successful.");
1277 break;
1280 return (0);
1284 int APMount::getOnSwitch(ISwitchVectorProperty *sp)
1286 for (int i=0; i < sp->nsp ; i++)
1287 if (sp->sp[i].s == ISS_ON)
1288 return i;
1290 return -1;
1294 int APMount::checkPower(ISwitchVectorProperty *sp)
1296 if (simulation) return 0;
1298 if (PowerSP.s != IPS_OK)
1300 if (!strcmp(sp->label, ""))
1301 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", sp->name);
1302 else
1303 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", sp->label);
1305 sp->s = IPS_IDLE;
1306 IDSetSwitch(sp, NULL);
1307 return -1;
1310 return 0;
1313 int APMount::checkPower(INumberVectorProperty *np)
1315 if (simulation) return 0;
1317 if (PowerSP.s != IPS_OK)
1320 if (!strcmp(np->label, ""))
1321 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", np->name);
1322 else
1323 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", np->label);
1325 np->s = IPS_IDLE;
1326 IDSetNumber(np, NULL);
1327 return -1;
1330 return 0;
1334 int APMount::checkPower(ITextVectorProperty *tp)
1337 if (simulation) return 0;
1339 if (PowerSP.s != IPS_OK)
1341 if (!strcmp(tp->label, ""))
1342 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", tp->name);
1343 else
1344 IDMessage (mydev, "Cannot change property %s while the telescope is offline.", tp->label);
1346 tp->s = IPS_IDLE;
1347 IDSetText(tp, NULL);
1348 return -1;
1351 return 0;
1355 void APMount::powerTelescope()
1357 switch (PowerSP.sp[0].s)
1359 case ISS_ON:
1361 if (simulation)
1363 PowerSP.s = IPS_OK;
1364 IDSetSwitch (&PowerSP, "Simulated telescope is online.");
1365 updateTime();
1366 return;
1369 if (Connect(PortT[0].text))
1371 PowerS[0].s = ISS_OFF;
1372 PowerS[1].s = ISS_ON;
1373 IDSetSwitch (&PowerSP, "Error connecting to port %s\n", PortT[0].text);
1374 return;
1377 if (testAP())
1379 PowerS[0].s = ISS_OFF;
1380 PowerS[1].s = ISS_ON;
1381 IDSetSwitch (&PowerSP, "Error connecting to Telescope. Telescope is offline.");
1382 return;
1385 IDLog("telescope test successfful\n");
1386 PowerSP.s = IPS_OK;
1387 IDSetSwitch (&PowerSP, "Telescope is online. Retrieving basic data...");
1388 getBasicData();
1389 break;
1391 case ISS_OFF:
1392 PowerS[0].s = ISS_OFF;
1393 PowerS[1].s = ISS_ON;
1394 PowerSP.s = IPS_IDLE;
1395 IDSetSwitch (&PowerSP, "Telescope is offline.");
1396 IDLog("Telescope is offline.");
1397 Disconnect();
1398 break;
1404 void APMount::slewError(int slewCode)
1406 OnCoordSetSP.s = IPS_IDLE;
1407 ParkSP.s = IPS_IDLE;
1408 IDSetSwitch(&ParkSP, NULL);
1410 if (slewCode == 1)
1411 IDSetSwitch (&OnCoordSetSP, "Object below horizon.");
1412 else if (slewCode == 2)
1413 IDSetSwitch (&OnCoordSetSP, "Object below the minimum elevation limit.");
1414 else
1415 IDSetSwitch (&OnCoordSetSP, "Slew failed.");
1420 void APMount::enableSimulation(bool enable)
1422 simulation = enable;
1424 if (simulation)
1425 IDLog("Warning: Simulation is activated.\n");
1426 else
1427 IDLog("Simulation is disabled.\n");
1430 void APMount::updateTime()
1433 char cdate[32];
1434 double ctime;
1435 int h, m, s;
1436 int day, month, year, result;
1437 int UTC_h, UTC_month, UTC_year, UTC_day, daysInFeb;
1438 bool leapYear;
1440 tzset();
1442 UTCOffset = timezoneOffset();
1443 IDLog("Daylight: %s - TimeZone: %g\n", daylight ? "Yes" : "No", UTCOffset);
1446 if (simulation)
1448 sprintf(UTCT[0].text, "%d-%02d-%02dT%02d:%02d:%02d", 1979, 6, 25, 3, 30, 30);
1449 IDLog("Telescope ISO date and time: %s\n", UTCT[0].text);
1450 IDSetText(&TimeTP, NULL);
1451 return;
1454 getLocalTime24(&ctime);
1455 getSexComponents(ctime, &h, &m, &s);
1457 UTC_h = h;
1459 if ( (result = getSDTime(&SDTimeN[0].value)) < 0)
1460 IDMessage(mydev, "Failed to retrieve siderial time from device.");
1462 getCalenderDate(cdate);
1464 result = sscanf(cdate, "%d/%d/%d", &year, &month, &day);
1465 if (result != 3) return;
1467 if (year % 4 == 0)
1469 if (year % 100 == 0)
1471 if (year % 400 == 0)
1472 leapYear = true;
1473 else
1474 leapYear = false;
1476 else
1477 leapYear = true;
1479 else
1480 leapYear = false;
1482 daysInFeb = leapYear ? 29 : 28;
1484 UTC_year = year;
1485 UTC_month = month;
1486 UTC_day = day;
1488 IDLog("day: %d - month %d - year: %d\n", day, month, year);
1490 // we'll have to convert telescope time to UTC manually starting from hour up
1491 // seems like a stupid way to do it.. oh well
1492 UTC_h = (int) UTCOffset + h;
1493 if (UTC_h < 0)
1495 UTC_h += 24;
1496 UTC_day--;
1498 else if (UTC_h > 24)
1500 UTC_h -= 24;
1501 UTC_day++;
1504 switch (UTC_month)
1506 case 1:
1507 case 8:
1508 if (UTC_day < 1)
1510 UTC_day = 31;
1511 UTC_month--;
1513 else if (UTC_day > 31)
1515 UTC_day = 1;
1516 UTC_month++;
1518 break;
1520 case 2:
1521 if (UTC_day < 1)
1523 UTC_day = 31;
1524 UTC_month--;
1526 else if (UTC_day > daysInFeb)
1528 UTC_day = 1;
1529 UTC_month++;
1531 break;
1533 case 3:
1534 if (UTC_day < 1)
1536 UTC_day = daysInFeb;
1537 UTC_month--;
1539 else if (UTC_day > 31)
1541 UTC_day = 1;
1542 UTC_month++;
1544 break;
1546 case 4:
1547 case 6:
1548 case 9:
1549 case 11:
1550 if (UTC_day < 1)
1552 UTC_day = 31;
1553 UTC_month--;
1555 else if (UTC_day > 30)
1557 UTC_day = 1;
1558 UTC_month++;
1560 break;
1562 case 5:
1563 case 7:
1564 case 10:
1565 case 12:
1566 if (UTC_day < 1)
1568 UTC_day = 30;
1569 UTC_month--;
1571 else if (UTC_day > 31)
1573 UTC_day = 1;
1574 UTC_month++;
1576 break;
1580 if (UTC_month < 1)
1582 UTC_month = 12;
1583 UTC_year--;
1585 else if (UTC_month > 12)
1587 UTC_month = 1;
1588 UTC_year++;
1591 /* Format it into ISO 8601 */
1592 sprintf(UTCT[0].text, "%d-%02d-%02dT%02d:%02d:%02d", UTC_year, UTC_month, UTC_day, UTC_h, m, s);
1594 IDLog("Local telescope time: %02d:%02d:%02d\n", h, m , s);
1595 IDLog("Telescope SD Time is: %g\n", SDTimeN[0].value);
1596 IDLog("UTC date and time: %s\n", UTCT[0].text);
1599 // Let's send everything to the client
1600 IDSetText(&TimeTP, NULL);
1601 IDSetNumber(&SDTimeNP, NULL);
1605 void APMount::updateLocation()
1608 int dd = 0, mm = 0, err = 0;
1610 if ( (err = getSiteLatitude(&dd, &mm)) < 0)
1611 IDMessage(mydev, "Failed to get site latitude from device.");
1612 else
1614 if (dd > 0)
1615 GeoNP.np[0].value = dd + mm/60.0;
1616 else
1617 GeoNP.np[0].value = dd - mm/60.0;
1619 IDLog("Astro-Physics Latitude: %d:%d\n", dd, mm);
1620 IDLog("INDI Latitude: %g\n", GeoNP.np[0].value);
1623 if ( (err = getSiteLongitude(&dd, &mm)) < 0)
1624 IDMessage(mydev, "Failed to get site longitude from device.");
1625 else
1627 if (dd > 0) GeoNP.np[1].value = 360.0 - (dd + mm/60.0);
1628 else GeoNP.np[1].value = (dd - mm/60.0) * -1.0;
1630 IDLog("Astro-Physics Longitude: %d:%d\n", dd, mm);
1631 IDLog("INDI Longitude: %g\n", GeoNP.np[1].value);
1634 IDSetNumber (&GeoNP, NULL);
1638 void APMount::connectionLost()
1640 PowerSP.s = IPS_IDLE;
1641 IDSetSwitch(&PowerSP, "The connection to the telescope is lost.");
1642 return;
1646 void APMount::connectionResumed()
1648 PowerS[0].s = ISS_ON;
1649 PowerS[1].s = ISS_OFF;
1650 PowerSP.s = IPS_OK;
1652 IDSetSwitch(&PowerSP, "The connection to the telescope has been resumed.");