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
32 #include "lx200driver.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;
46 static inline double timezoneOffset()
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.
54 return timezone
/ (60 * 60);
60 daylight
= tm
->tm_isdst
;
61 return -(tm
->tm_gmtoff
) / (60 * 60);
65 APMount
*telescope
= NULL
;
66 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
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
93 static void ISPoll(void *);
94 static void retryConnection(void *);
99 /* send client definitions of all properties */
109 telescope
= new APMount();
110 IEAddTimer (POLLMS
, ISPoll
, NULL
);
113 void ISGetProperties (const char *dev
)
116 telescope
->ISGetProperties(dev
);
119 void ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
122 telescope
->ISNewSwitch(dev
, name
, states
, names
, n
);
125 void ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
)
128 telescope
->ISNewText(dev
, name
, texts
, names
, n
);
131 void ISNewNumber (const char *dev
, const char *name
, double values
[], char *names
[], int n
)
134 telescope
->ISNewNumber(dev
, name
, values
, names
, n
);
137 void ISPoll (void */
*p*/
)
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 /**************************************************
148 ***************************************************/
168 lastMove
[0] = lastMove
[1] = lastMove
[2] = lastMove
[3] = 0;
173 utp
->tm_year
+= 1900;
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
))
257 IDDefSwitch(&PowerSP
, NULL
);
258 IDDefText(&PortTP
, NULL
);
259 IDDefSwitch(&AlignmentSP
, NULL
);
260 IDDefText(&TimeTP
, NULL
);
261 IDDefNumber(&SDTimeNP
, NULL
);
262 IDDefNumber(&GeoNP
, NULL
);
265 IDDefNumber(&EqNP
, NULL
);
266 IDDefSwitch(&OnCoordSetSP
, NULL
);
267 IDDefSwitch(&AbortSlewSP
, NULL
);
268 IDDefSwitch(&ParkSP
, NULL
);
271 IDDefSwitch(&TrackModeSP
, NULL
);
272 IDDefSwitch(&MovementSP
, NULL
);
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
)
285 void APMount::ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int /*n*/)
288 struct tm
*ltp
= new tm
;
292 localtime_r (<ime
, ltp
);
295 // ignore if not ours
296 if (strcmp (dev
, mydev
))
300 if (!strcmp(name
, PortTP
.name
) )
303 tp
= IUFindText( &PortTP
, names
[0] );
307 IUSaveText(tp
, texts
[0]);
308 IDSetText (&PortTP
, NULL
);
313 if (!strcmp (name
, TimeTP
.name
))
315 if (checkPower(&TimeTP
))
318 if (extractISOTime(texts
[0], &utm
) < 0)
321 IDSetText(&TimeTP
, "Time invalid");
327 ltp
->tm_year
+= 1900;
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.");
345 if ( ( err
= setLocalTime(ltp
->tm_hour
, ltp
->tm_min
, ltp
->tm_sec
) < 0) )
347 handleError(&TimeTP
, err
, "Setting local time");
351 tp
= IUFindText(&TimeTP
, names
[0]);
354 IUSaveText(tp
, texts
[0]);
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]);
372 if ( ( err
= setCalenderDate(ltp
->tm_mday
, ltp
->tm_mon
, ltp
->tm_year
) < 0) )
374 handleError(&TimeTP
, err
, "Setting local date.");
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
))
392 if (!strcmp (name
, EqNP
.name
))
396 if (checkPower(&EqNP
))
399 for (nset
= i
= 0; i
< n
; i
++)
401 INumber
*eqp
= IUFindNumber (&EqNP
, names
[i
]);
405 nset
+= newRA
>= 0 && newRA
<= 24.0;
406 } else if (eqp
== &EqN
[1])
409 nset
+= newDEC
>= -90.0 && newDEC
<= 90.0;
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");
432 if (MovementSP
.s
== IPS_BUSY
)
434 for (int i
=0; i
< 4; i
++)
437 MovementS
[i
].s
= ISS_OFF
;
440 MovementSP
.s
= IPS_IDLE
;
441 IDSetSwitch(&MovementSP
, NULL
);
444 if (handleCoordSet())
447 IDSetNumber(&EqNP
, NULL
);
454 IDSetNumber(&EqNP
, "RA or Dec missing or invalid");
461 if ( !strcmp (name
, SDTimeNP
.name
) )
464 if (checkPower(&SDTimeNP
))
467 if (values
[0] < 0.0 || values
[0] > 24.0)
469 SDTimeNP
.s
= IPS_IDLE
;
470 IDSetNumber(&SDTimeNP
, "Time invalid");
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");
483 SDTimeNP
.np
[0].value
= values
[0];
486 IDSetNumber(&SDTimeNP
, "Sidereal time updated to %02d:%02d:%02d", h
, m
, s
);
491 // Geographical location
492 if (!strcmp (name
, GeoNP
.name
))
494 // new geographic coords
495 double newLong
= 0, newLat
= 0;
499 if (checkPower(&GeoNP
))
502 for (nset
= i
= 0; i
< n
; i
++)
504 INumber
*geop
= IUFindNumber (&GeoNP
, names
[i
]);
505 if (geop
== &GeoN
[0])
508 nset
+= newLat
>= -90.0 && newLat
<= 90.0;
509 } else if (geop
== &GeoN
[1])
512 nset
+= newLong
>= 0.0 && newLong
< 360.0;
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");
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
);
536 strcpy(msg
, "Lat or Long missing or invalid");
538 IDSetNumber (&GeoNP
, "%s", msg
);
543 if (!strcmp(name
, FocusTimerNP
.name
))
545 if (checkPower(&FocusTimerNP
))
548 // Don't update if busy
549 if (FocusTimerNP
.s
== IPS_BUSY
)
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
);
565 void APMount::ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
569 char combinedDir
[64];
572 // ignore if not ours //
573 if (strcmp (mydev
, dev
))
577 if (!strcmp (name
, PowerSP
.name
))
579 IUResetSwitches(&PowerSP
);
580 IUUpdateSwitches(&PowerSP
, states
, names
, n
);
586 if (!strcmp(name
, OnCoordSetSP
.name
))
588 if (checkPower(&OnCoordSetSP
))
591 IUResetSwitches(&OnCoordSetSP
);
592 IUUpdateSwitches(&OnCoordSetSP
, states
, names
, n
);
593 currentSet
= getOnSwitch(&OnCoordSetSP
);
594 OnCoordSetSP
.s
= IPS_OK
;
595 IDSetSwitch(&OnCoordSetSP
, NULL
);
599 if (!strcmp(name
, ParkSP
.name
))
601 if (checkPower(&ParkSP
))
604 IUResetSwitches(&ParkSP
);
605 IUUpdateSwitches(&ParkSP
, states
, names
, n
);
606 index
= getOnSwitch(&ParkSP
);
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);
631 IDSetSwitch(&ParkSP
, (index
== 0) ? "Parking..." : "Unparking...");
636 if (!strcmp (name
, AbortSlewSP
.name
))
638 if (checkPower(&AbortSlewSP
))
640 AbortSlewSP
.s
= IPS_IDLE
;
641 IDSetSwitch(&AbortSlewSP
, NULL
);
645 IUResetSwitches(&AbortSlewSP
);
648 if (EqNP
.s
== IPS_BUSY
)
650 AbortSlewSP
.s
= IPS_OK
;
652 IDSetSwitch(&AbortSlewSP
, "Slew aborted.");
653 IDSetNumber(&EqNP
, NULL
);
655 else if (MovementSP
.s
== IPS_BUSY
)
658 for (int i
=0; i
< 4; i
++)
661 MovementSP
.s
= IPS_IDLE
;
662 AbortSlewSP
.s
= IPS_OK
;
664 IUResetSwitches(&MovementSP
);
665 IUResetSwitches(&AbortSlewSP
);
666 IDSetSwitch(&AbortSlewSP
, "Slew aborted.");
667 IDSetSwitch(&MovementSP
, NULL
);
668 IDSetNumber(&EqNP
, NULL
);
672 IUResetSwitches(&MovementSP
);
673 AbortSlewSP
.s
= IPS_OK
;
674 IDSetSwitch(&AbortSlewSP
, NULL
);
681 if (!strcmp (name
, AlignmentSP
.name
))
683 if (checkPower(&AlignmentSP
))
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");
696 AlignmentSP
.s
= IPS_OK
;
697 IDSetSwitch (&AlignmentSP
, NULL
);
703 if (!strcmp (name
, FocusSpeedSP
.name
))
705 if (checkPower(&FocusSpeedSP
))
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");
721 /* disable timer and motion */
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
);
738 if (!strcmp (name
, FocusMotionSP
.name
))
740 if (checkPower(&FocusMotionSP
))
743 IUResetSwitches(&FocusMotionSP
);
745 // If speed is "halt"
746 if (FocusSpeedS
[0].s
== ISS_ON
)
748 FocusMotionSP
.s
= IPS_IDLE
;
749 IDSetSwitch(&FocusMotionSP
, NULL
);
753 IUUpdateSwitches(&FocusMotionSP
, states
, names
, n
);
754 index
= getOnSwitch(&FocusMotionSP
);
756 if ( ( err
= setFocuserMotion(index
) < 0) )
758 handleError(&FocusMotionSP
, err
, "Setting focuser speed");
762 FocusMotionSP
.s
= IPS_BUSY
;
765 if (FocusTimerN
[0].value
> 0)
766 FocusTimerNP
.s
= IPS_BUSY
;
768 IDSetSwitch(&FocusMotionSP
, NULL
);
773 if (!strcmp (name
, MovementSP
.name
))
775 if (checkPower(&MovementSP
))
779 IUUpdateSwitches(&MovementSP
, states
, names
, n
);
780 swp
= IUFindSwitch(&MovementSP
, names
[0]);
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;
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
])
803 for (int i
=0; i
< 4; i
++)
806 IUResetSwitches(&MovementSP
);
807 MovementSP
.s
= IPS_IDLE
;
808 IDSetSwitch(&MovementSP
, "Slew aborted.");
812 // East/West movement is illegal
813 if (lastMove
[LX200_EAST
] && lastMove
[LX200_WEST
])
816 for (int i
=0; i
< 4; i
++)
819 IUResetSwitches(&MovementSP
);
820 MovementSP
.s
= IPS_IDLE
;
821 IDSetSwitch(&MovementSP
, "Slew aborted.");
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");
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
]);
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");
857 strcpy(combinedDir
, Direction
[index
]);
859 IDSetSwitch(&MovementSP
, "Moving %s...", combinedDir
);
865 if (!strcmp (name
, TrackModeSP
.name
))
867 if (checkPower(&TrackModeSP
))
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.");
881 TrackModeSP
.s
= IPS_OK
;
882 IDSetSwitch(&TrackModeSP
, NULL
);
888 void APMount::handleError(ISwitchVectorProperty
*svp
, int err
, const char *msg
)
893 /* First check to see if the telescope is connected */
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
);
907 /* If the error is a time out, then the device doesn't support this property or busy*/
911 IDSetSwitch(svp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
914 /* Changing property failed, user should retry. */
915 IDSetSwitch( svp
, "%s failed.", msg
);
920 void APMount::handleError(INumberVectorProperty
*nvp
, int err
, const char *msg
)
925 /* First check to see if the telescope is connected */
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
);
939 /* If the error is a time out, then the device doesn't support this property */
943 IDSetNumber(nvp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
946 /* Changing property failed, user should retry. */
947 IDSetNumber( nvp
, "%s failed.", msg
);
952 void APMount::handleError(ITextVectorProperty
*tvp
, int err
, const char *msg
)
957 /* First check to see if the telescope is connected */
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
);
971 /* If the error is a time out, then the device doesn't support this property */
975 IDSetText(tvp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
979 /* Changing property failed, user should retry. */
980 IDSetText( tvp
, "%s failed.", msg
);
985 void APMount::correctFault()
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
)
1005 telescope
->connectionLost();
1007 telescope
->connectionResumed();
1010 void APMount::ISPoll()
1015 if (!isTelescopeOn())
1021 getLX200RA(¤tRA
);
1022 getLX200DEC(¤tDEC
);
1024 if ( fabs (currentRA
- lastRA
) > 0.01 || fabs (currentDEC
- lastDEC
) > 0.01)
1027 lastDEC
= currentDEC
;
1028 IDSetNumber (&EqNP
, NULL
);
1033 getLX200RA(¤tRA
);
1034 getLX200DEC(¤tDEC
);
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
)
1046 lastDEC
= currentDEC
;
1047 IUResetSwitches(&OnCoordSetSP
);
1048 OnCoordSetSP
.s
= IPS_OK
;
1050 IDSetNumber (&EqNP
, NULL
);
1055 OnCoordSetSP
.sp
[0].s
= ISS_ON
;
1056 IDSetSwitch (&OnCoordSetSP
, "Slew is complete.");
1060 OnCoordSetSP
.sp
[1].s
= ISS_ON
;
1061 IDSetSwitch (&OnCoordSetSP
, "Slew is complete. Tracking...");
1069 IDSetNumber (&EqNP
, NULL
);
1074 if ( (err
= getLX200RA(¤tRA
)) < 0 || (err
= getLX200DEC(¤tDEC
)) < 0)
1076 handleError(&EqNP
, err
, "Getting RA/DEC");
1083 if ( (currentRA
!= lastRA
) || (currentDEC
!= lastDEC
))
1086 lastDEC
= currentDEC
;
1087 IDSetNumber (&EqNP
, NULL
);
1096 switch (MovementSP
.s
)
1101 getLX200RA(¤tRA
);
1102 getLX200DEC(¤tDEC
);
1103 IDSetNumber (&EqNP
, NULL
);
1111 switch (FocusTimerNP
.s
)
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");
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
);
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
1164 timeFormat
= LX200_24
;
1166 // #3 Get current RA/DEC
1167 getLX200RA(¤tRA
);
1168 getLX200DEC(¤tDEC
);
1169 targetRA
= currentRA
;
1170 targetDEC
= currentDEC
;
1172 IDSetNumber (&EqNP
, NULL
);
1178 int APMount::handleCoordSet()
1182 char syncString
[256];
1183 char RAStr
[32], DecStr
[32];
1191 lastSet
= LX200_SLEW
;
1192 if (EqNP
.s
== IPS_BUSY
)
1194 IDLog("Aboring Slew\n");
1197 // sleep for 100 mseconds
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
);
1216 IDLog("We're in LX200_TRACK\n");
1217 if (EqNP
.s
== IPS_BUSY
)
1219 IDLog("Aboring Slew\n");
1222 // sleep for 200 mseconds
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
);
1242 fs_sexa(RAStr
, targetRA
, 2, 3600);
1243 fs_sexa(DecStr
, targetDEC
, 2, 3600);
1245 IDSetNumber(&EqNP
, "Slewing to JNow RA %s - DEC %s", RAStr
, DecStr
);
1246 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr
, DecStr
);
1250 IDLog("Tracking called, but tracking threshold not reached yet.\n");
1252 EqNP
.np
[0].value
= currentRA
;
1253 EqNP
.np
[1].value
= currentDEC
;
1255 if (lastSet
!= LX200_TRACK
)
1256 IDSetNumber(&EqNP
, "Tracking...");
1258 IDSetNumber(&EqNP
, NULL
);
1260 lastSet
= LX200_TRACK
;
1265 lastSet
= LX200_SYNC
;
1268 if ( ( err
= Sync(syncString
) < 0) )
1270 IDSetNumber( &EqNP
, "Synchronization failed.");
1275 IDLog("Synchronization successful %s\n", syncString
);
1276 IDSetNumber(&EqNP
, "Synchronization successful.");
1284 int APMount::getOnSwitch(ISwitchVectorProperty
*sp
)
1286 for (int i
=0; i
< sp
->nsp
; i
++)
1287 if (sp
->sp
[i
].s
== ISS_ON
)
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
);
1303 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", sp
->label
);
1306 IDSetSwitch(sp
, NULL
);
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
);
1323 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", np
->label
);
1326 IDSetNumber(np
, NULL
);
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
);
1344 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", tp
->label
);
1347 IDSetText(tp
, NULL
);
1355 void APMount::powerTelescope()
1357 switch (PowerSP
.sp
[0].s
)
1364 IDSetSwitch (&PowerSP
, "Simulated telescope is online.");
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
);
1379 PowerS
[0].s
= ISS_OFF
;
1380 PowerS
[1].s
= ISS_ON
;
1381 IDSetSwitch (&PowerSP
, "Error connecting to Telescope. Telescope is offline.");
1385 IDLog("telescope test successfful\n");
1387 IDSetSwitch (&PowerSP
, "Telescope is online. Retrieving basic data...");
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.");
1404 void APMount::slewError(int slewCode
)
1406 OnCoordSetSP
.s
= IPS_IDLE
;
1407 ParkSP
.s
= IPS_IDLE
;
1408 IDSetSwitch(&ParkSP
, NULL
);
1411 IDSetSwitch (&OnCoordSetSP
, "Object below horizon.");
1412 else if (slewCode
== 2)
1413 IDSetSwitch (&OnCoordSetSP
, "Object below the minimum elevation limit.");
1415 IDSetSwitch (&OnCoordSetSP
, "Slew failed.");
1420 void APMount::enableSimulation(bool enable
)
1422 simulation
= enable
;
1425 IDLog("Warning: Simulation is activated.\n");
1427 IDLog("Simulation is disabled.\n");
1430 void APMount::updateTime()
1436 int day
, month
, year
, result
;
1437 int UTC_h
, UTC_month
, UTC_year
, UTC_day
, daysInFeb
;
1442 UTCOffset
= timezoneOffset();
1443 IDLog("Daylight: %s - TimeZone: %g\n", daylight
? "Yes" : "No", UTCOffset
);
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
);
1454 getLocalTime24(&ctime
);
1455 getSexComponents(ctime
, &h
, &m
, &s
);
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;
1469 if (year
% 100 == 0)
1471 if (year
% 400 == 0)
1482 daysInFeb
= leapYear
? 29 : 28;
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
;
1498 else if (UTC_h
> 24)
1513 else if (UTC_day
> 31)
1526 else if (UTC_day
> daysInFeb
)
1536 UTC_day
= daysInFeb
;
1539 else if (UTC_day
> 31)
1555 else if (UTC_day
> 30)
1571 else if (UTC_day
> 31)
1585 else if (UTC_month
> 12)
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.");
1615 GeoNP
.np
[0].value
= dd
+ mm
/60.0;
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.");
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.");
1646 void APMount::connectionResumed()
1648 PowerS
[0].s
= ISS_ON
;
1649 PowerS
[1].s
= ISS_OFF
;
1652 IDSetSwitch(&PowerSP
, "The connection to the telescope has been resumed.");