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
32 #include "lx200driver.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;
47 static inline double timezoneOffset()
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.
55 return timezone
/ (60 * 60);
61 daylight
= tm
->tm_isdst
;
62 return -(tm
->tm_gmtoff
) / (60 * 60);
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
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
91 static void ISPoll(void *);
92 static void retryConnection(void *);
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 */
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};
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};
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};
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};
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
);
180 strcpy(eqNum
.device
, newName
);
181 strcpy(OnCoordSetSw
.device
, newName
);
182 strcpy(abortSlewSw
.device
, newName
);
183 strcpy(ParkSP
.device
, newName
);
186 strcpy(SlewModeSw
.device
, newName
);
187 strcpy(TrackModeSw
.device
, newName
);
188 strcpy(TrackingFreq
.device
, newName
);
189 strcpy(MovementSw
.device
, newName
);
192 strcpy(FocusSpeedSw
.device
, newName
);
193 strcpy(FocusMotionSw
.device
, newName
);
194 strcpy(FocusTimerNP
.device
, newName
);
197 strcpy(Time
.device
, newName
);
198 strcpy(SDTime
.device
, newName
);
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 */
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
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()
310 trackingMode
= LX200_TRACK_DEFAULT
;
320 lastMove
[0] = lastMove
[1] = lastMove
[2] = lastMove
[3] = 0;
325 utp
->tm_year
+= 1900;
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
))
350 IDDefSwitch (&PowerSP
, NULL
);
351 IDDefText (&Port
, NULL
);
352 IDDefSwitch (&AlignmentSw
, NULL
);
355 IDDefNumber (&eqNum
, NULL
);
356 IDDefSwitch (&OnCoordSetSw
, NULL
);
357 IDDefSwitch (&abortSlewSw
, NULL
);
358 IDDefSwitch (&ParkSP
, NULL
);
361 IDDefNumber (&TrackingFreq
, NULL
);
362 IDDefSwitch (&SlewModeSw
, NULL
);
363 IDDefSwitch (&TrackModeSw
, NULL
);
364 IDDefSwitch (&MovementSw
, NULL
);
367 IDDefSwitch(&FocusSpeedSw
, NULL
);
368 IDDefSwitch(&FocusMotionSw
, NULL
);
369 IDDefNumber(&FocusTimerNP
, NULL
);
372 IDDefText (&Time
, NULL
);
373 IDDefNumber (&SDTime
, NULL
);
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
)
386 void LX200Generic::ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
)
389 struct tm
*ltp
= new tm
;
393 localtime_r (<ime
, ltp
);
396 // ignore if not ours //
397 if (strcmp (dev
, thisDevice
))
403 if (!strcmp(name
, Port
.name
) )
406 tp
= IUFindText( &Port
, names
[0] );
410 tp
->text
= new char[strlen(texts
[0])+1];
411 strcpy(tp
->text
, texts
[0]);
412 IDSetText (&Port
, NULL
);
416 if (!strcmp (name
, SiteName
.name
) )
418 if (checkPower(&SiteName
))
421 if ( ( err
= setSiteName(texts
[0], currentSiteNum
) < 0) )
423 handleError(&SiteName
, err
, "Setting site name");
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");
434 if (!strcmp (name
, Time
.name
))
436 if (checkPower(&Time
))
439 if (extractISOTime(texts
[0], &utm
) < 0)
442 IDSetText(&Time
, "Time invalid");
448 ltp
->tm_year
+= 1900;
451 /*dayDiff = utm.tm_mday - ltp->tm_mday;
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;*/
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) )
469 IDSetText( &Time
, "Setting UTC Offset failed.");
473 if ( ( err
= setLocalTime(ltp
->tm_hour
, ltp
->tm_min
, ltp
->tm_sec
) < 0) )
475 handleError(&Time
, err
, "Setting local time");
479 tp
= IUFindText(&Time
, names
[0]);
482 tp
->text
= new char[strlen(texts
[0])+1];
483 strcpy(tp
->text
, texts
[0]);
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]);
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.");
510 if ( ( err
= setCalenderDate(ltp
->tm_mday
, ltp
->tm_mon
, ltp
->tm_year
) < 0) )
512 handleError(&Time
, err
, "Setting local date.");
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
))
531 if (!strcmp (name
, eqNum
.name
))
535 if (checkPower(&eqNum
))
538 for (nset
= i
= 0; i
< n
; i
++)
540 INumber
*eqp
= IUFindNumber (&eqNum
, names
[i
]);
544 nset
+= newRA
>= 0 && newRA
<= 24.0;
545 } else if (eqp
== &eq
[1])
548 nset
+= newDEC
>= -90.0 && newDEC
<= 90.0;
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");
577 /*eqNum.s = IPS_BUSY;*/
581 if (MovementSw
.s
== IPS_BUSY
)
583 for (int i
=0; i
< 4; i
++)
586 MovementS
[i
].s
= ISS_OFF
;
589 MovementSw
.s
= IPS_IDLE
;
590 IDSetSwitch(&MovementSw
, NULL
);
593 if (handleCoordSet())
596 IDSetNumber(&eqNum
, NULL
);
603 IDSetNumber(&eqNum
, "RA or Dec missing or invalid");
609 if ( !strcmp (name
, SDTime
.name
) )
611 if (checkPower(&SDTime
))
615 if (values
[0] < 0.0 || values
[0] > 24.0)
618 IDSetNumber(&SDTime
, "Time invalid");
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");
631 SDTime
.np
[0].value
= values
[0];
634 IDSetNumber(&SDTime
, "Sidereal time updated to %02d:%02d:%02d", h
, m
, s
);
639 if (!strcmp (name
, geoNum
.name
))
641 // new geographic coords
642 double newLong
= 0, newLat
= 0;
646 if (checkPower(&geoNum
))
650 for (nset
= i
= 0; i
< n
; i
++)
652 INumber
*geop
= IUFindNumber (&geoNum
, names
[i
]);
656 nset
+= newLat
>= -90.0 && newLat
<= 90.0;
657 } else if (geop
== &geo
[1])
660 nset
+= newLong
>= 0.0 && newLong
< 360.0;
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");
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
);
684 strcpy(msg
, "Lat or Long missing or invalid");
686 IDSetNumber (&geoNum
, "%s", msg
);
690 if ( !strcmp (name
, TrackingFreq
.name
) )
693 if (checkPower(&TrackingFreq
))
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");
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
);
721 if (!strcmp(name
, FocusTimerNP
.name
))
723 if (checkPower(&FocusTimerNP
))
726 // Don't update if busy
727 if (FocusTimerNP
.s
== IPS_BUSY
)
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
);
743 void LX200Generic::ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
747 char combinedDir
[64];
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
))
761 // FIRST Switch ALWAYS for power
762 if (!strcmp (name
, PowerSP
.name
))
764 IUResetSwitches(&PowerSP
);
765 IUUpdateSwitches(&PowerSP
, states
, names
, n
);
771 if (!strcmp(name
, OnCoordSetSw
.name
))
773 if (checkPower(&OnCoordSetSw
))
776 IUResetSwitches(&OnCoordSetSw
);
777 IUUpdateSwitches(&OnCoordSetSw
, states
, names
, n
);
778 currentSet
= getOnSwitch(&OnCoordSetSw
);
779 OnCoordSetSw
.s
= IPS_OK
;
780 IDSetSwitch(&OnCoordSetSw
, NULL
);
784 if (!strcmp(name
, ParkSP
.name
))
786 if (checkPower(&ParkSP
))
792 if ( (err
= getSDTime(&STime
[0].value
)) < 0)
794 handleError(&ParkSP
, err
, "Get siderial time");
798 if (AlignmentS
[0].s
== ISS_ON
)
800 targetRA
= STime
[0].value
;
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
);
818 IDSetSwitch(&ParkSP
, "You can only park the telescope in Polar or AltAz modes.");
822 IDSetNumber(&SDTime
, NULL
);
824 currentSet
= LX200_PARK
;
829 if (!strcmp (name
, abortSlewSw
.name
))
831 if (checkPower(&abortSlewSw
))
833 abortSlewSw
.s
= IPS_IDLE
;
834 IDSetSwitch(&abortSlewSw
, NULL
);
838 IUResetSwitches(&abortSlewSw
);
841 if (eqNum
.s
== IPS_BUSY
)
843 abortSlewSw
.s
= IPS_OK
;
845 IDSetSwitch(&abortSlewSw
, "Slew aborted.");
846 IDSetNumber(&eqNum
, NULL
);
848 else if (MovementSw
.s
== IPS_BUSY
)
851 for (int i
=0; i
< 4; i
++)
854 MovementSw
.s
= IPS_IDLE
;
855 abortSlewSw
.s
= IPS_OK
;
857 IUResetSwitches(&MovementSw
);
858 IUResetSwitches(&abortSlewSw
);
859 IDSetSwitch(&abortSlewSw
, "Slew aborted.");
860 IDSetSwitch(&MovementSw
, NULL
);
861 IDSetNumber(&eqNum
, NULL
);
865 IUResetSwitches(&MovementSw
);
866 abortSlewSw
.s
= IPS_OK
;
867 IDSetSwitch(&abortSlewSw
, NULL
);
874 if (!strcmp (name
, AlignmentSw
.name
))
876 if (checkPower(&AlignmentSw
))
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");
889 AlignmentSw
.s
= IPS_OK
;
890 IDSetSwitch (&AlignmentSw
, NULL
);
896 if (!strcmp (name
, SitesSw
.name
))
898 if (checkPower(&SitesSw
))
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");
911 if ( ( err
= getSiteLatitude(&dd
, &mm
) < 0))
913 handleError(&SitesSw
, err
, "Selecting sites");
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");
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
);
942 if (!strcmp (name
, FocusSpeedSw
.name
))
944 if (checkPower(&FocusSpeedSw
))
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");
958 /* disable timer and motion */
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
);
975 if (!strcmp (name
, FocusMotionSw
.name
))
977 if (checkPower(&FocusMotionSw
))
980 IUResetSwitches(&FocusMotionSw
);
982 // If speed is "halt"
983 if (FocusSpeedS
[0].s
== ISS_ON
)
985 FocusMotionSw
.s
= IPS_IDLE
;
986 IDSetSwitch(&FocusMotionSw
, NULL
);
990 IUUpdateSwitches(&FocusMotionSw
, states
, names
, n
);
991 index
= getOnSwitch(&FocusMotionSw
);
994 if ( ( err
= setFocuserMotion(index
) < 0) )
996 handleError(&FocusMotionSw
, err
, "Setting focuser speed");
1001 FocusMotionSw
.s
= IPS_BUSY
;
1004 if (FocusTimerN
[0].value
> 0)
1005 FocusTimerNP
.s
= IPS_BUSY
;
1007 IDSetSwitch(&FocusMotionSw
, NULL
);
1012 if (!strcmp (name
, SlewModeSw
.name
))
1014 if (checkPower(&SlewModeSw
))
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");
1027 SlewModeSw
.s
= IPS_OK
;
1028 IDSetSwitch(&SlewModeSw
, NULL
);
1033 if (!strcmp (name
, MovementSw
.name
))
1035 if (checkPower(&MovementSw
))
1039 IUUpdateSwitches(&MovementSw
, states
, names
, n
);
1040 swp
= IUFindSwitch(&MovementSw
, names
[0]);
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;
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
])
1063 for (int i
=0; i
< 4; i
++)
1066 IUResetSwitches(&MovementSw
);
1067 MovementSw
.s
= IPS_IDLE
;
1068 IDSetSwitch(&MovementSw
, "Slew aborted.");
1072 // East/West movement is illegal
1073 if (lastMove
[LX200_EAST
] && lastMove
[LX200_WEST
])
1076 for (int i
=0; i
< 4; i
++)
1079 IUResetSwitches(&MovementSw
);
1080 MovementSw
.s
= IPS_IDLE
;
1081 IDSetSwitch(&MovementSw
, "Slew aborted.");
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");
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
]);
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");
1117 strcpy(combinedDir
, Direction
[index
]);
1119 IDSetSwitch(&MovementSw
, "Moving %s...", combinedDir
);
1125 if (!strcmp (name
, TrackModeSw
.name
))
1127 if (checkPower(&TrackModeSw
))
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.");
1140 getTrackFreq(&TrackFreq
[0].value
);
1141 TrackModeSw
.s
= IPS_OK
;
1142 IDSetNumber(&TrackingFreq
, NULL
);
1143 IDSetSwitch(&TrackModeSw
, NULL
);
1149 void LX200Generic::handleError(ISwitchVectorProperty
*svp
, int err
, const char *msg
)
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
);
1168 /* If the error is a time out, then the device doesn't support this property or busy*/
1172 IDSetSwitch(svp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
1175 /* Changing property failed, user should retry. */
1176 IDSetSwitch( svp
, "%s failed.", msg
);
1181 void LX200Generic::handleError(INumberVectorProperty
*nvp
, int err
, const char *msg
)
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
);
1200 /* If the error is a time out, then the device doesn't support this property */
1204 IDSetNumber(nvp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
1207 /* Changing property failed, user should retry. */
1208 IDSetNumber( nvp
, "%s failed.", msg
);
1213 void LX200Generic::handleError(ITextVectorProperty
*tvp
, int err
, const char *msg
)
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
);
1232 /* If the error is a time out, then the device doesn't support this property */
1236 IDSetText(tvp
, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg
);
1240 /* Changing property failed, user should retry. */
1241 IDSetText( tvp
, "%s failed.", msg
);
1246 void LX200Generic::correctFault()
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
)
1265 if (testTelescope())
1267 PowerSP
.s
= IPS_IDLE
;
1268 IDSetSwitch(&PowerSP
, "The connection to the telescope is lost.");
1272 PowerS
[0].s
= ISS_ON
;
1273 PowerS
[1].s
= ISS_OFF
;
1276 IDSetSwitch(&PowerSP
, "The connection to the telescope has been resumed.");
1280 void LX200Generic::ISPoll()
1283 /*static int okCounter = 3;*/
1286 if (!isTelescopeOn())
1292 getLX200RA(¤tRA
);
1293 getLX200DEC(¤tDEC
);
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
);
1304 getLX200RA(¤tRA
);
1305 getLX200DEC(¤tDEC
);
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
;
1328 IDSetNumber (&eqNum
, NULL
);
1333 OnCoordSetSw
.sp
[0].s
= ISS_ON
;
1334 IDSetSwitch (&OnCoordSetSw
, "Slew is complete.");
1338 OnCoordSetSw
.sp
[1].s
= ISS_ON
;
1339 IDSetSwitch (&OnCoordSetSw
, "Slew is complete. Tracking...");
1346 if (setSlewMode(LX200_SLEW_GUIDE
) < 0)
1348 handleError(&eqNum
, err
, "Setting slew mode");
1352 IUResetSwitches(&SlewModeSw
);
1353 SlewModeS
[LX200_SLEW_GUIDE
].s
= ISS_ON
;
1354 IDSetSwitch(&SlewModeSw
, NULL
);
1357 IUResetSwitches(&MovementSw
);
1358 MovementS
[LX200_EAST
].s
= ISS_ON
;
1359 MovementSw
.s
= IPS_BUSY
;
1360 for (int i
=0; i
< 4; i
++)
1362 lastMove
[LX200_EAST
] = 1;
1363 IDSetSwitch(&MovementSw
, NULL
);
1366 IDSetSwitch (&ParkSP
, "Park is complete. Turn off the telescope now.");
1372 eqNum
.np
[0].value
= currentRA
;
1373 eqNum
.np
[1].value
= currentDEC
;
1374 IDSetNumber (&eqNum
, NULL
);
1380 /*if (--okCounter >= 0)
1383 // Activate again in 3 seconds
1386 if ( (err
= getLX200RA(¤tRA
)) < 0 || (err
= getLX200DEC(¤tDEC
)) < 0)
1388 handleError(&eqNum
, err
, "Getting RA/DEC");
1395 if ( (currentRA
!= lastRA
) || (currentDEC
!= lastDEC
))
1397 eqNum
.np
[0].value
= lastRA
= currentRA
;
1398 eqNum
.np
[1].value
= lastDEC
= currentDEC
;
1399 IDSetNumber (&eqNum
, NULL
);
1408 switch (MovementSw
.s
)
1413 getLX200RA(¤tRA
);
1414 getLX200DEC(¤tDEC
);
1415 /*apparentCoord( JD, (double) J2000, ¤tRA, ¤tDEC);*/
1416 eqNum
.np
[0].value
= currentRA
;
1417 eqNum
.np
[1].value
= currentDEC
;
1419 IDSetNumber (&eqNum
, NULL
);
1427 switch (FocusTimerNP
.s
)
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");
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
);
1471 void LX200Generic::getBasicData()
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
);
1487 if ( (err
= getTimeFormat(&timeFormat
)) < 0)
1488 IDMessage(thisDevice
, "Failed to retrieve time format from device.");
1491 timeFormat
= (timeFormat
== 24) ? LX200_24
: LX200_AM
;
1492 // We always do 24 hours
1493 if (timeFormat
!= LX200_24
)
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");
1510 IDSetText (&SiteName
, NULL
);
1512 if ( (err
= getTrackFreq(&TrackFreq
[0].value
)) < 0)
1513 IDMessage(thisDevice
, "Failed to get tracking frequency from device.");
1515 IDSetNumber (&TrackingFreq
, NULL
);
1523 int LX200Generic::handleCoordSet()
1527 char syncString
[256];
1528 char RAStr
[32], DecStr
[32];
1531 IDLog("In Handle Coord Set()\n");
1538 lastSet
= LX200_SLEW
;
1539 if (eqNum
.s
== IPS_BUSY
)
1541 IDLog("Aboring Slew\n");
1544 // sleep for 100 mseconds
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
);
1563 IDLog("We're in LX200_TRACK\n");
1564 if (eqNum
.s
== IPS_BUSY
)
1566 IDLog("Aboring Slew\n");
1569 // sleep for 200 mseconds
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
);
1589 fs_sexa(RAStr
, targetRA
, 2, 3600);
1590 fs_sexa(DecStr
, targetDEC
, 2, 3600);
1592 IDSetNumber(&eqNum
, "Slewing to JNow RA %s - DEC %s", RAStr
, DecStr
);
1593 IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr
, DecStr
);
1597 IDLog("Tracking called, but tracking threshold not reached yet.\n");
1599 eqNum
.np
[0].value
= currentRA
;
1600 eqNum
.np
[1].value
= currentDEC
;
1602 if (lastSet
!= LX200_TRACK
)
1603 IDSetNumber(&eqNum
, "Tracking...");
1605 IDSetNumber(&eqNum
, NULL
);
1607 lastSet
= LX200_TRACK
;
1612 lastSet
= LX200_SYNC
;
1615 if ( ( err
= Sync(syncString
) < 0) )
1617 IDSetNumber( &eqNum
, "Synchronization failed.");
1622 IDLog("Synchronization successful %s\n", syncString
);
1623 IDSetNumber(&eqNum
, "Synchronization successful.");
1627 // Set RA to LST and DEC to 0 degrees, slew, then change to 'guide' slew after slew is complete.
1629 if (eqNum
.s
== IPS_BUSY
)
1633 // sleep for 200 mseconds
1643 ParkSP
.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.");
1656 int LX200Generic::getOnSwitch(ISwitchVectorProperty
*sp
)
1658 for (int i
=0; i
< sp
->nsp
; i
++)
1659 if (sp
->sp
[i
].s
== ISS_ON
)
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
);
1675 IDMessage (thisDevice
, "Cannot change property %s while the telescope is offline.", sp
->label
);
1678 IDSetSwitch(sp
, NULL
);
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
);
1695 IDMessage (thisDevice
, "Cannot change property %s while the telescope is offline.", np
->label
);
1698 IDSetNumber(np
, NULL
);
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
);
1716 IDMessage (thisDevice
, "Cannot change property %s while the telescope is offline.", tp
->label
);
1719 IDSetText(tp
, NULL
);
1727 void LX200Generic::powerTelescope()
1729 switch (PowerSP
.sp
[0].s
)
1736 IDSetSwitch (&PowerSP
, "Simulated telescope is online.");
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
);
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.");
1756 IDLog("telescope test successfful\n");
1758 IDSetSwitch (&PowerSP
, "Telescope is online. Retrieving basic data...");
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.");
1775 void LX200Generic::slewError(int slewCode
)
1777 OnCoordSetSw
.s
= IPS_IDLE
;
1778 ParkSP
.s
= IPS_IDLE
;
1779 IDSetSwitch(&ParkSP
, NULL
);
1782 IDSetSwitch (&OnCoordSetSw
, "Object below horizon.");
1783 else if (slewCode
== 2)
1784 IDSetSwitch (&OnCoordSetSw
, "Object below the minimum elevation limit.");
1786 IDSetSwitch (&OnCoordSetSw
, "Slew failed.");
1791 void LX200Generic::getAlignment()
1794 if (PowerSP
.s
!= IPS_OK
)
1797 signed char align
= ACK();
1800 IDSetSwitch (&AlignmentSw
, "Failed to get telescope alignment.");
1804 AlignmentS
[0].s
= ISS_OFF
;
1805 AlignmentS
[1].s
= ISS_OFF
;
1806 AlignmentS
[2].s
= ISS_OFF
;
1810 case 'P': AlignmentS
[0].s
= ISS_ON
;
1812 case 'A': AlignmentS
[1].s
= ISS_ON
;
1814 case 'L': AlignmentS
[2].s
= ISS_ON
;
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
;
1828 IDLog("Warning: Simulation is activated.\n");
1830 IDLog("Simulation is disabled.\n");
1833 void LX200Generic::updateTime()
1839 int day
, month
, year
, result
;
1840 int UTC_h
, UTC_month
, UTC_year
, UTC_day
, daysInFeb
;
1845 UTCOffset
= timezoneOffset();
1846 IDLog("Daylight: %s - TimeZone: %g\n", daylight
? "Yes" : "No", UTCOffset
);
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
);
1857 getLocalTime24(&ctime
);
1858 getSexComponents(ctime
, &h
, &m
, &s
);
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;
1872 if (year
% 100 == 0)
1874 if (year
% 400 == 0)
1885 daysInFeb
= leapYear
? 29 : 28;
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
;
1901 else if (UTC_h
> 24)
1916 else if (UTC_day
> 31)
1929 else if (UTC_day
> daysInFeb
)
1939 UTC_day
= daysInFeb
;
1942 else if (UTC_day
> 31)
1958 else if (UTC_day
> 30)
1974 else if (UTC_day
> 31)
1988 else if (UTC_month
> 12)
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.");
2018 geoNum
.np
[0].value
= dd
+ mm
/60.0;
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.");
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
);