source: BAORadio/libindi/libindi/drivers/telescope/lx200generic.cpp @ 504

Last change on this file since 504 was 504, checked in by frichard, 13 years ago

-Version 0.8 de libini
-Formule de Marc
-Nouvelles fonctionnalités (goto nom-de l'objet etc...)

File size: 64.5 KB
Line 
1#if 0
2    LX200 Generic
3    Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
4
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.
9
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.
14
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
19#endif
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <stdarg.h>
25#include <math.h>
26#include <unistd.h>
27#include <time.h>
28#include <sys/time.h>
29
30#include "indicom.h"
31#include "lx200driver.h"
32#include "lx200gps.h"
33#include "lx200ap.h"
34#include "lx200classic.h"
35
36#include <config.h>
37
38#ifdef HAVE_NOVA_H
39#include <libnova.h>
40#endif
41
42LX200Generic *telescope = NULL;
43int MaxReticleFlashRate = 3;
44
45/* There is _one_ binary for all LX200 drivers, but each binary is renamed
46** to its device name (i.e. lx200gps, lx200_16..etc). The main function will
47** fetch from std args the binary name and ISInit will create the apporpiate
48** device afterwards. If the binary name does not match any known devices,
49** we simply create a generic device.
50*/
51extern char* me;
52
53#define COMM_GROUP      "Communication"
54#define BASIC_GROUP     "Main Control"
55#define MOTION_GROUP    "Motion Control"
56#define DATETIME_GROUP  "Date/Time"
57#define SITE_GROUP      "Site Management"
58#define FOCUS_GROUP     "Focus Control"
59
60#define LX200_TRACK     0
61#define LX200_SYNC      1
62
63/* Simulation Parameters */
64#define SLEWRATE        1               /* slew rate, degrees/s */
65#define SIDRATE         0.004178        /* sidereal rate, degrees/s */
66
67/* Handy Macros */
68#define currentRA       EquatorialCoordsRN[0].value
69#define currentDEC      EquatorialCoordsRN[1].value
70#define targetRA        EquatorialCoordsWN[0].value
71#define targetDEC       EquatorialCoordsWN[1].value
72
73static void ISPoll(void *);
74static void retryConnection(void *);
75
76/*INDI Propertries */
77
78/**********************************************************************************************/
79/************************************ GROUP: Communication ************************************/
80/**********************************************************************************************/
81
82/********************************************
83 Property: Connection
84*********************************************/
85static ISwitch ConnectS[]               = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}};
86ISwitchVectorProperty ConnectSP         = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, ConnectS, NARRAY(ConnectS), "", 0};
87
88/********************************************
89 Property: Device Port
90*********************************************/
91/*wildi removed static */
92static IText PortT[]                    = {{"PORT", "Port", 0, 0, 0, 0}};
93ITextVectorProperty PortTP      = { mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_IDLE, PortT, NARRAY(PortT), "", 0};
94
95/********************************************
96 Property: Telescope Alignment Mode
97*********************************************/
98static ISwitch AlignmentS []            = {{"Polar", "", ISS_ON, 0, 0}, {"AltAz", "", ISS_OFF, 0, 0}, {"Land", "", ISS_OFF, 0, 0}};
99static ISwitchVectorProperty AlignmentSw= { mydev, "Alignment", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, AlignmentS, NARRAY(AlignmentS), "", 0};
100
101/**********************************************************************************************/
102/************************************ GROUP: Main Control *************************************/
103/**********************************************************************************************/
104
105/********************************************
106 Property: Equatorial Coordinates JNow
107 Perm: Transient WO.
108 Timeout: 120 seconds.
109*********************************************/
110INumber EquatorialCoordsWN[]            = { {"RA",  "RA  H:M:S", "%10.6m",  0., 24., 0., 0., 0, 0, 0}, {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0} };
111INumberVectorProperty EquatorialCoordsWNP= { mydev, "EQUATORIAL_EOD_COORD_REQUEST", "Equatorial JNow", BASIC_GROUP, IP_WO, 120, IPS_IDLE, EquatorialCoordsWN, NARRAY(EquatorialCoordsWN), "", 0};
112
113/********************************************
114 Property: Equatorial Coordinates JNow
115 Perm: RO
116*********************************************/
117INumber EquatorialCoordsRN[]            = { {"RA",  "RA  H:M:S", "%10.6m",  0., 24., 0., 0., 0, 0, 0}, {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0}};
118INumberVectorProperty EquatorialCoordsRNP= { mydev, "EQUATORIAL_EOD_COORD", "Equatorial JNow", BASIC_GROUP, IP_RO, 120, IPS_IDLE, EquatorialCoordsRN, NARRAY(EquatorialCoordsRN), "", 0};
119
120/********************************************
121 Property: On Coord Set
122 Description: This property decides what happens
123             when we receive a new equatorial coord
124             value. We either track, or sync
125             to the new coordinates.
126*********************************************/
127static ISwitch OnCoordSetS[]             = {{"SLEW", "Slew", ISS_ON, 0, 0 }, {"SYNC", "Sync", ISS_OFF, 0 , 0}};
128ISwitchVectorProperty OnCoordSetSP= { mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, OnCoordSetS, NARRAY(OnCoordSetS), "", 0};
129
130/********************************************
131 Property: Abort telescope motion
132*********************************************/
133static ISwitch AbortSlewS[]             = {{"ABORT", "Abort", ISS_OFF, 0, 0 }};
134ISwitchVectorProperty AbortSlewSP= { mydev, "TELESCOPE_ABORT_MOTION", "Abort Slew", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, AbortSlewS, NARRAY(AbortSlewS), "", 0};
135
136/**********************************************************************************************/
137/************************************** GROUP: Motion *****************************************/
138/**********************************************************************************************/
139
140/********************************************
141 Property: Slew Speed
142*********************************************/
143static ISwitch SlewModeS[]              = {{"Max", "", ISS_ON, 0, 0}, {"Find", "", ISS_OFF, 0, 0}, {"Centering", "", ISS_OFF, 0, 0}, {"Guide", "", ISS_OFF, 0 , 0}};
144ISwitchVectorProperty SlewModeSP        = { mydev, "Slew rate", "", MOTION_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SlewModeS, NARRAY(SlewModeS), "", 0};
145
146/********************************************
147 Property: Tracking Mode
148*********************************************/
149static ISwitch TrackModeS[]             = {{ "Default", "", ISS_ON, 0, 0} , { "Lunar", "", ISS_OFF, 0, 0}, {"Manual", "", ISS_OFF, 0, 0}};
150static ISwitchVectorProperty TrackModeSP= { mydev, "Tracking Mode", "", MOTION_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, TrackModeS, NARRAY(TrackModeS), "", 0};
151
152/********************************************
153 Property: Tracking Frequency
154*********************************************/
155static INumber TrackFreqN[]              = {{ "trackFreq", "Freq", "%g", 56.4, 60.1, 0.1, 60.1, 0, 0, 0}};
156static INumberVectorProperty TrackingFreqNP= { mydev, "Tracking Frequency", "", MOTION_GROUP, IP_RW, 0, IPS_IDLE, TrackFreqN, NARRAY(TrackFreqN), "", 0};
157
158/********************************************
159 Property: Movement (Arrow keys on handset). North/South
160*********************************************/
161static ISwitch MovementNSS[]       = {{"MOTION_NORTH", "North", ISS_OFF, 0, 0}, {"MOTION_SOUTH", "South", ISS_OFF, 0, 0}};
162ISwitchVectorProperty MovementNSSP      = { mydev, "TELESCOPE_MOTION_NS", "North/South", MOTION_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, MovementNSS, NARRAY(MovementNSS), "", 0};
163
164/********************************************
165 Property: Movement (Arrow keys on handset). West/East
166*********************************************/
167static ISwitch MovementWES[]       = {{"MOTION_WEST", "West", ISS_OFF, 0, 0}, {"MOTION_EAST", "East", ISS_OFF, 0, 0}};
168ISwitchVectorProperty MovementWESP      = { mydev, "TELESCOPE_MOTION_WE", "West/East", MOTION_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, MovementWES, NARRAY(MovementWES), "", 0};
169
170/********************************************
171 Property: Timed Guide movement. North/South
172*********************************************/
173static INumber GuideNSN[]       = {{"TIMED_GUIDE_N", "North (sec)", "%g", 0, 10, 0.001, 0, 0, 0}, {"TIMED_GUIDE_S", "South (sec)", "%g", 0, 10, 0.001, 0, 0, 0}};
174INumberVectorProperty GuideNSNP      = { mydev, "TELESCOPE_TIMED_GUIDE_NS", "Guide North/South", MOTION_GROUP, IP_RW, 0, IPS_IDLE, GuideNSN, NARRAY(GuideNSN), "", 0};
175
176/********************************************
177 Property: Timed Guide movement. West/East
178*********************************************/
179static INumber GuideWEN[]       = {{"TIMED_GUIDE_W", "West (sec)", "%g", 0, 10, 0.001, 0, 0, 0}, {"TIMED_GUIDE_E", "East (sec)", "%g", 0, 10, 0.001, 0, 0, 0}};
180INumberVectorProperty GuideWENP      = { mydev, "TELESCOPE_TIMED_GUIDE_WE", "Guide West/East", MOTION_GROUP, IP_RW, 0, IPS_IDLE, GuideWEN, NARRAY(GuideWEN), "", 0};
181
182/********************************************
183 Property: Slew Accuracy
184 Desciption: How close the scope have to be with
185             respect to the requested coords for
186             the tracking operation to be successull
187             i.e. returns OK
188*********************************************/
189INumber SlewAccuracyN[] = {
190    {"SlewRA",  "RA (arcmin)", "%g",  0., 60., 1., 3.0, 0, 0, 0},
191    {"SlewkDEC", "Dec (arcmin)", "%g", 0., 60., 1., 3.0, 0, 0, 0},
192};
193INumberVectorProperty SlewAccuracyNP = {mydev, "Slew Accuracy", "", MOTION_GROUP, IP_RW, 0, IPS_IDLE, SlewAccuracyN, NARRAY(SlewAccuracyN), "", 0};
194
195/********************************************
196 Property: Use pulse-guide commands
197 Desciption: Set to on if this mount can support
198             pulse guide commands.  There appears to
199             be no way to query this information from
200             the mount
201*********************************************/
202static ISwitch UsePulseCmdS[]           = {{ "Off", "", ISS_ON, 0, 0} , { "On", "", ISS_OFF, 0, 0}};
203static ISwitchVectorProperty UsePulseCmdSP= { mydev, "Use Pulse Cmd", "", MOTION_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, UsePulseCmdS, NARRAY(UsePulseCmdS), "", 0};
204
205/**********************************************************************************************/
206/************************************** GROUP: Focus ******************************************/
207/**********************************************************************************************/
208
209/********************************************
210 Property: Focus Direction
211*********************************************/
212ISwitch  FocusMotionS[]  = { {"IN", "Focus in", ISS_OFF, 0, 0}, {"OUT", "Focus out", ISS_OFF, 0, 0}};
213ISwitchVectorProperty   FocusMotionSP = {mydev, "FOCUS_MOTION", "Motion", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusMotionS, NARRAY(FocusMotionS), "", 0};
214
215/********************************************
216 Property: Focus Timer
217*********************************************/
218INumber  FocusTimerN[]    = { {"TIMER", "Timer (ms)", "%g", 0., 10000., 1000., 50., 0, 0, 0 }};
219INumberVectorProperty FocusTimerNP = { mydev, "FOCUS_TIMER", "Focus Timer", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusTimerN, NARRAY(FocusTimerN), "", 0};
220
221/********************************************
222 Property: Focus Mode
223*********************************************/
224static ISwitch  FocusModeS[]     = { {"FOCUS_HALT", "Halt", ISS_ON, 0, 0},
225                                     {"FOCUS_SLOW", "Slow", ISS_OFF, 0, 0},
226                                     {"FOCUS_FAST", "Fast", ISS_OFF, 0, 0}};
227static ISwitchVectorProperty FocusModeSP = {mydev, "FOCUS_MODE", "Mode", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusModeS, NARRAY(FocusModeS), "", 0};
228
229/**********************************************************************************************/
230/*********************************** GROUP: Date & Time ***************************************/
231/**********************************************************************************************/
232
233/********************************************
234 Property: UTC Time
235*********************************************/
236static IText TimeT[] = {{"UTC", "UTC", 0, 0, 0, 0}};
237ITextVectorProperty TimeTP = { mydev, "TIME_UTC", "UTC Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, TimeT, NARRAY(TimeT), "", 0};
238
239/********************************************
240 Property: DST Corrected UTC Offfset
241*********************************************/
242static INumber UTCOffsetN[] = {{"OFFSET", "Offset", "%0.3g" , -12.,12.,0.5,0., 0, 0, 0}};
243INumberVectorProperty UTCOffsetNP = { mydev, "TIME_UTC_OFFSET", "UTC Offset", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, UTCOffsetN , NARRAY(UTCOffsetN), "", 0};
244
245/********************************************
246 Property: Sidereal Time
247*********************************************/
248static INumber SDTimeN[] = {{"LST", "Sidereal time", "%10.6m" , 0.,24.,0.,0., 0, 0, 0}};
249INumberVectorProperty SDTimeNP = { mydev, "TIME_LST", "Sidereal Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, SDTimeN, NARRAY(SDTimeN), "", 0};
250
251/**********************************************************************************************/
252/************************************* GROUP: Sites *******************************************/
253/**********************************************************************************************/
254
255/********************************************
256 Property: Site Management
257*********************************************/
258static 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}};
259static ISwitchVectorProperty SitesSP  = { mydev, "Sites", "", SITE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SitesS, NARRAY(SitesS), "", 0};
260
261/********************************************
262 Property: Site Name
263*********************************************/
264static IText   SiteNameT[] = {{"Name", "", 0, 0, 0, 0}};
265static ITextVectorProperty SiteNameTP = { mydev, "Site Name", "", SITE_GROUP, IP_RW, 0 , IPS_IDLE, SiteNameT, NARRAY(SiteNameT), "", 0};
266
267/********************************************
268 Property: Geographical Location
269*********************************************/
270
271static INumber geo[] = {
272    {"LAT",  "Lat.  D:M:S +N", "%10.6m",  -90.,  90., 0., 0., 0, 0, 0},
273    {"LONG", "Long. D:M:S +E", "%10.6m", 0., 360., 0., 0., 0, 0, 0},
274    {"HEIGHT", "Height m", "%10.2f", -300., 6000., 0., 610., 0, 0, 0},
275};
276INumberVectorProperty geoNP = {
277    mydev, "GEOGRAPHIC_COORD", "Geographic Location", SITE_GROUP, IP_RW, 0., IPS_IDLE,
278    geo, NARRAY(geo), "", 0};
279
280/*****************************************************************************************************/
281/**************************************** END PROPERTIES *********************************************/
282/*****************************************************************************************************/
283
284void changeLX200GenericDeviceName(const char * newName)
285{
286  // COMM_GROUP
287  strcpy(ConnectSP.device , newName);
288  strcpy(PortTP.device , newName);
289  strcpy(AlignmentSw.device, newName);
290
291  // BASIC_GROUP
292  strcpy(EquatorialCoordsWNP.device, newName);
293  strcpy(EquatorialCoordsRNP.device, newName);
294  strcpy(OnCoordSetSP.device , newName );
295  strcpy(AbortSlewSP.device , newName );
296
297  // MOTION_GROUP
298  strcpy(SlewModeSP.device , newName );
299  strcpy(TrackModeSP.device , newName );
300  strcpy(TrackingFreqNP.device , newName );
301  strcpy(MovementNSSP.device , newName );
302  strcpy(MovementWESP.device , newName );
303  strcpy(GuideNSNP.device , newName );
304  strcpy(GuideWENP.device , newName );
305  strcpy(SlewAccuracyNP.device, newName);
306  strcpy(UsePulseCmdSP.device, newName);
307
308  // FOCUS_GROUP
309  strcpy(FocusModeSP.device , newName );
310  strcpy(FocusMotionSP.device , newName );
311  strcpy(FocusTimerNP.device, newName);
312
313  // DATETIME_GROUP
314  strcpy(TimeTP.device , newName );
315  strcpy(UTCOffsetNP.device , newName );
316  strcpy(SDTimeNP.device , newName );
317
318  // SITE_GROUP
319  strcpy(SitesSP.device , newName );
320  strcpy(SiteNameTP.device , newName );
321  strcpy(geoNP.device , newName );
322 
323}
324
325void changeAllDeviceNames(const char *newName)
326{
327  changeLX200GenericDeviceName(newName);
328  changeLX200AutostarDeviceName(newName);
329  changeLX200AstroPhysicsDeviceName(newName);
330  changeLX200_16DeviceName(newName);
331  changeLX200ClassicDeviceName(newName);
332  changeLX200GPSDeviceName(newName);
333}
334
335
336/* send client definitions of all properties */
337void ISInit()
338{
339  static int isInit=0;
340  char *envDev = getenv("INDIDEV");
341
342 if (isInit)
343  return;
344
345 isInit = 1;
346 
347  IUSaveText(&PortT[0], "/dev/ttyS0");
348  IUSaveText(&TimeT[0], "YYYY-MM-DDTHH:MM:SS");
349
350  // We need to check if UTCOffset has been set by user or not
351  UTCOffsetN[0].aux0 = (int *) malloc(sizeof(int));
352  *((int *) UTCOffsetN[0].aux0) = 0;
353 
354 
355  if (strstr(me, "indi_lx200classic"))
356  {
357     fprintf(stderr , "initilizaing from LX200 classic device...\n");
358
359     telescope = new LX200Classic();
360     if (envDev != NULL)
361     {
362         changeAllDeviceNames(envDev);
363         telescope->setCurrentDeviceName(envDev);
364     }
365     else
366     {
367        // 1. mydev = device_name
368        changeAllDeviceNames("LX200 Classic");
369        telescope->setCurrentDeviceName("LX200 Classic");
370     }
371
372     MaxReticleFlashRate = 3;
373  }
374
375  else if (strstr(me, "indi_lx200gps"))
376  {
377     fprintf(stderr , "initilizaing from LX200 GPS device...\n");
378
379     // 2. device = sub_class
380     telescope = new LX200GPS();
381
382     if (envDev != NULL)
383     {
384         // 1. mydev = device_name
385         changeAllDeviceNames(envDev);
386         telescope->setCurrentDeviceName(envDev);
387     }
388     else
389     {
390         // 1. mydev = device_name
391         changeAllDeviceNames("LX200 GPS");
392         telescope->setCurrentDeviceName("LX200 GPS");
393     }
394
395
396
397     MaxReticleFlashRate = 9;
398  }
399  else if (strstr(me, "indi_lx200_16"))
400  {
401
402    IDLog("Initilizaing from LX200 16 device...\n");
403
404    // 2. device = sub_class
405   telescope = new LX200_16();
406
407   if (envDev != NULL)
408   {
409       // 1. mydev = device_name
410       changeAllDeviceNames(envDev);
411       telescope->setCurrentDeviceName(envDev);
412   }
413   else
414   {
415       changeAllDeviceNames("LX200 16");
416       telescope->setCurrentDeviceName("LX200 16");
417   }
418
419   MaxReticleFlashRate = 3;
420 }
421 else if (strstr(me, "indi_lx200autostar"))
422 {
423   fprintf(stderr , "initilizaing from autostar device...\n");
424 
425   // 2. device = sub_class
426   telescope = new LX200Autostar();
427
428   if (envDev != NULL)
429   {
430       // 1. change device name
431       changeAllDeviceNames(envDev);
432       telescope->setCurrentDeviceName(envDev);
433   }
434   else
435   {
436       // 1. change device name
437       changeAllDeviceNames("LX200 Autostar");
438       telescope->setCurrentDeviceName("LX200 Autostar");
439   }
440
441   MaxReticleFlashRate = 9;
442 }
443 else if (strstr(me, "indi_lx200ap"))
444 {
445   fprintf(stderr , "initilizaing from ap device...\n");
446 
447
448   // 2. device = sub_class
449   telescope = new LX200AstroPhysics();
450
451   if (envDev != NULL)
452   {
453       // 1. change device name
454       changeAllDeviceNames(envDev);
455       telescope->setCurrentDeviceName(envDev);
456   }
457   else
458   {
459       // 1. change device name
460       changeAllDeviceNames("LX200 Astro-Physics");
461        telescope->setCurrentDeviceName("LX200 Astro-Physics");
462    }
463
464
465   MaxReticleFlashRate = 9;
466 }
467 // be nice and give them a generic device
468 else
469 {
470  telescope = new LX200Generic();
471
472  if (envDev != NULL)
473  {
474      // 1. change device name
475      changeAllDeviceNames(envDev);
476      telescope->setCurrentDeviceName(envDev);
477  }
478  else
479  {
480      // 1. change device name
481      changeAllDeviceNames("LX200 Generic");
482      telescope->setCurrentDeviceName("LX200 Generic");
483  }
484
485 }
486
487}
488
489void ISGetProperties (const char *dev)
490{ ISInit(); telescope->ISGetProperties(dev); IEAddTimer (POLLMS, ISPoll, NULL);}
491void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
492{ ISInit(); telescope->ISNewSwitch(dev, name, states, names, n);}
493void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
494{ ISInit(); telescope->ISNewText(dev, name, texts, names, n);}
495void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
496{ ISInit(); telescope->ISNewNumber(dev, name, values, names, n);}
497void ISPoll (void *p) { telescope->ISPoll(); IEAddTimer (POLLMS, ISPoll, NULL); p=p;}
498void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
499{
500  INDI_UNUSED(dev);
501  INDI_UNUSED(name);
502  INDI_UNUSED(sizes);
503  INDI_UNUSED(blobsizes);
504  INDI_UNUSED(blobs);
505  INDI_UNUSED(formats);
506  INDI_UNUSED(names);
507  INDI_UNUSED(n);
508}
509
510void ISSnoopDevice (XMLEle *root)
511{
512  telescope->ISSnoopDevice(root);
513}
514
515/**************************************************
516*** LX200 Generic Implementation
517***************************************************/
518
519LX200Generic::LX200Generic()
520{
521   currentSiteNum = 1;
522   trackingMode   = LX200_TRACK_DEFAULT;
523   lastSet        = -1;
524   fault          = false;
525   simulation     = false;
526   currentSet     = 0;
527   fd             = -1;
528   GuideNSTID     = 0;
529   GuideWETID     = 0;
530
531   // Children call parent routines, this is the default
532   IDLog("INDI Library v%g\n", INDI_LIBV);
533   IDLog("initilizaing from generic LX200 device...\n");
534   IDLog("Driver Version: 2008-05-21\n");
535 
536   //enableSimulation(true); 
537}
538
539LX200Generic::~LX200Generic()
540{
541}
542
543void LX200Generic::setCurrentDeviceName(const char * devName)
544{
545  strcpy(thisDevice, devName);
546}
547
548void LX200Generic::ISGetProperties(const char *dev)
549{
550
551 if (dev && strcmp (thisDevice, dev))
552    return;
553
554  // COMM_GROUP
555  IDDefSwitch (&ConnectSP, NULL);
556  IDDefText   (&PortTP, NULL);
557  IDDefSwitch (&AlignmentSw, NULL);
558
559  // BASIC_GROUP
560  IDDefNumber (&EquatorialCoordsWNP, NULL);
561  IDDefNumber (&EquatorialCoordsRNP, NULL);
562  IDDefSwitch (&OnCoordSetSP, NULL);
563  IDDefSwitch (&AbortSlewSP, NULL);
564
565  // MOTION_GROUP
566  IDDefNumber (&TrackingFreqNP, NULL);
567  IDDefSwitch (&SlewModeSP, NULL);
568  IDDefSwitch (&TrackModeSP, NULL);
569  IDDefSwitch (&MovementNSSP, NULL);
570  IDDefSwitch (&MovementWESP, NULL);
571  IDDefNumber (&GuideNSNP, NULL );
572  IDDefNumber (&GuideWENP, NULL );
573  IDDefNumber (&SlewAccuracyNP, NULL);
574  IDDefSwitch (&UsePulseCmdSP, NULL);
575
576  // FOCUS_GROUP
577  IDDefSwitch(&FocusModeSP, NULL);
578  IDDefSwitch(&FocusMotionSP, NULL);
579  IDDefNumber(&FocusTimerNP, NULL);
580
581  // DATETIME_GROUP
582  #ifdef HAVE_NOVA_H
583  IDDefText   (&TimeTP, NULL);
584  IDDefNumber(&UTCOffsetNP, NULL);
585  #endif
586
587  IDDefNumber (&SDTimeNP, NULL);
588
589  // SITE_GROUP
590  IDDefSwitch (&SitesSP, NULL);
591  IDDefText   (&SiteNameTP, NULL);
592  IDDefNumber (&geoNP, NULL);
593 
594  /* Send the basic data to the new client if the previous client(s) are already connected. */         
595   if (ConnectSP.s == IPS_OK)
596       getBasicData();
597
598}
599
600void LX200Generic::ISSnoopDevice (XMLEle *root)
601{
602  INDI_UNUSED(root);
603}
604
605void LX200Generic::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
606{
607        int err;
608        IText *tp;
609
610        // ignore if not ours //
611        if (strcmp (dev, thisDevice))
612            return;
613
614        // suppress warning
615        n=n;
616
617        if (!strcmp(name, PortTP.name) )
618        {
619          PortTP.s = IPS_OK;
620          tp = IUFindText( &PortTP, names[0] );
621          if (!tp)
622           return;
623
624          IUSaveText(&PortTP.tp[0], texts[0]);
625          IDSetText (&PortTP, NULL);
626          return;
627        }
628
629        if (!strcmp (name, SiteNameTP.name) )
630        {
631          if (checkPower(&SiteNameTP))
632           return;
633
634          if ( ( err = setSiteName(fd, texts[0], currentSiteNum) < 0) )
635          {
636             handleError(&SiteNameTP, err, "Setting site name");
637             return;
638          }
639             SiteNameTP.s = IPS_OK;
640             tp = IUFindText(&SiteNameTP, names[0]);
641             tp->text = new char[strlen(texts[0])+1];
642             strcpy(tp->text, texts[0]);
643             IDSetText(&SiteNameTP , "Site name updated");
644             return;
645       }
646
647       #ifdef HAVE_NOVA_H
648       if (!strcmp (name, TimeTP.name))
649       {
650          if (checkPower(&TimeTP))
651           return;
652
653         if (simulation)
654         {
655                TimeTP.s = IPS_OK;
656                IUSaveText(&TimeTP.tp[0], texts[0]);
657                IDSetText(&TimeTP, "Simulated time updated.");
658                return;
659         }
660
661         struct ln_date utm;
662         struct ln_zonedate ltm;
663
664        if (*((int *) UTCOffsetN[0].aux0) == 0)
665        {
666                TimeTP.s = IPS_IDLE;
667                IDSetText(&TimeTP, "You must set the UTC Offset property first.");
668                return;
669        }
670
671          if (extractISOTime(texts[0], &utm) < 0)
672          {
673            TimeTP.s = IPS_IDLE;
674            IDSetText(&TimeTP , "Time invalid");
675            return;
676          }
677
678         // update JD
679         JD = ln_get_julian_day(&utm);
680         IDLog("New JD is %f\n", (float) JD);
681
682        ln_date_to_zonedate(&utm, &ltm, UTCOffsetN[0].value*3600.0);
683
684        // Set Local Time
685        if ( ( err = setLocalTime(fd, ltm.hours, ltm.minutes, ltm.seconds) < 0) )
686        {
687                  handleError(&TimeTP, err, "Setting local time");
688                  return;
689        }
690
691        if (!strcmp(dev, "LX200 GPS"))
692        {
693                        if ( ( err = setCalenderDate(fd, utm.days, utm.months, utm.years) < 0) )
694                        {
695                                handleError(&TimeTP, err, "Setting TimeT date.");
696                                return;
697                        }
698        }
699        else
700        {
701                        if ( ( err = setCalenderDate(fd, ltm.days, ltm.months, ltm.years) < 0) )
702                        {
703                                handleError(&TimeTP, err, "Setting local date.");
704                                return;
705                        }
706        }
707       
708        // Everything Ok, save time value       
709        if (IUUpdateText(&TimeTP, texts, names, n) < 0)
710                return;
711
712        TimeTP.s = IPS_OK;
713        IDSetText(&TimeTP , "Time updated to %s, updating planetary data...", texts[0]);
714
715        // Also update telescope's sidereal time
716        getSDTime(fd, &SDTimeN[0].value);
717        IDSetNumber(&SDTimeNP, NULL);
718        }
719        #endif
720}
721
722
723void LX200Generic::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
724{
725        int h =0, m =0, s=0, err;
726        double newRA =0, newDEC =0;
727       
728        // ignore if not ours //
729        if (strcmp (dev, thisDevice))
730            return;
731
732        // Slewing Accuracy
733        if (!strcmp (name, SlewAccuracyNP.name))
734        {
735                if (!IUUpdateNumber(&SlewAccuracyNP, values, names, n))
736                {
737                        SlewAccuracyNP.s = IPS_OK;
738                        IDSetNumber(&SlewAccuracyNP, NULL);
739                        return;
740                }
741               
742                SlewAccuracyNP.s = IPS_ALERT;
743                IDSetNumber(&SlewAccuracyNP, "unknown error while setting tracking precision");
744                return;
745        }
746
747        #ifdef HAVE_NOVA_H
748        // DST Correct TimeT Offset
749        if (!strcmp (name, UTCOffsetNP.name))
750        {
751                if (strcmp(names[0], UTCOffsetN[0].name))
752                {
753                        UTCOffsetNP.s = IPS_ALERT;
754                        IDSetNumber( &UTCOffsetNP , "Unknown element %s for property %s.", names[0], UTCOffsetNP.label);
755                        return;
756                }
757
758                if (!simulation)
759                        if ( ( err = setUTCOffset(fd, (values[0] * -1.0)) < 0) )
760                        {
761                                UTCOffsetNP.s = IPS_ALERT;
762                                IDSetNumber( &UTCOffsetNP , "Setting UTC Offset failed.");
763                                return;
764                        }
765               
766                *((int *) UTCOffsetN[0].aux0) = 1;
767                IUUpdateNumber(&UTCOffsetNP, values, names, n);
768                UTCOffsetNP.s = IPS_OK;
769                IDSetNumber(&UTCOffsetNP, NULL);
770                return;
771        }
772        #endif
773
774        if (!strcmp (name, EquatorialCoordsWNP.name))
775        {
776          int i=0, nset=0;
777
778          if (checkPower(&EquatorialCoordsWNP))
779           return;
780
781            for (nset = i = 0; i < n; i++)
782            {
783                INumber *eqp = IUFindNumber (&EquatorialCoordsWNP, names[i]);
784                if (eqp == &EquatorialCoordsWN[0])
785                {
786                    newRA = values[i];
787                    nset += newRA >= 0 && newRA <= 24.0;
788                } else if (eqp == &EquatorialCoordsWN[1])
789                {
790                    newDEC = values[i];
791                    nset += newDEC >= -90.0 && newDEC <= 90.0;
792                }
793            }
794
795          if (nset == 2)
796          {
797           /*EquatorialCoordsWNP.s = IPS_BUSY;*/
798           char RAStr[32], DecStr[32];
799
800           fs_sexa(RAStr, newRA, 2, 3600);
801           fs_sexa(DecStr, newDEC, 2, 3600);
802         
803           #ifdef INDI_DEBUG
804           IDLog("We received JNOW RA %g - DEC %g\n", newRA, newDEC);
805           IDLog("We received JNOW RA %s - DEC %s\n", RAStr, DecStr);
806           #endif
807
808          if (!simulation)
809           if ( (err = setObjectRA(fd, newRA)) < 0 || ( err = setObjectDEC(fd, newDEC)) < 0)
810           {
811             EquatorialCoordsWNP.s = IPS_ALERT ;
812             IDSetNumber(&EquatorialCoordsWNP, NULL);
813             handleError(&EquatorialCoordsWNP, err, "Setting RA/DEC");
814             return;
815           } 
816           /* wildi In principle this line is according to the discussion */
817           /* In case the telescope is slewing, we have to abort that. No status change here */
818           /* EquatorialCoordsWNP.s = IPS_OK; */
819           IDSetNumber(&EquatorialCoordsWNP, NULL);
820           targetRA  = newRA;
821           targetDEC = newDEC;
822
823           if (handleCoordSet())
824           {
825             EquatorialCoordsWNP.s = IPS_ALERT;
826             IDSetNumber(&EquatorialCoordsWNP, NULL);
827           }
828        } // end nset
829        else
830        {
831                EquatorialCoordsWNP.s = IPS_ALERT;
832                IDSetNumber(&EquatorialCoordsWNP, "RA or Dec missing or invalid");
833        }
834
835            return;
836     } /* end EquatorialCoordsWNP */
837
838        // Update Sidereal Time
839        if ( !strcmp (name, SDTimeNP.name) )
840        {
841          if (checkPower(&SDTimeNP))
842           return;
843
844
845          if (values[0] < 0.0 || values[0] > 24.0)
846          {
847            SDTimeNP.s = IPS_IDLE;
848            IDSetNumber(&SDTimeNP , "Time invalid");
849            return;
850          }
851
852          getSexComponents(values[0], &h, &m, &s);
853          IDLog("Siderial Time is %02d:%02d:%02d\n", h, m, s);
854         
855          if ( ( err = setSDTime(fd, h, m, s) < 0) )
856          {
857            handleError(&SDTimeNP, err, "Setting siderial time"); 
858            return;
859          }
860         
861          SDTimeNP.np[0].value = values[0];
862          SDTimeNP.s = IPS_OK;
863
864          IDSetNumber(&SDTimeNP , "Sidereal time updated to %02d:%02d:%02d", h, m, s);
865
866          return;
867        }
868
869        // Update Geographical Location
870        if (!strcmp (name, geoNP.name))
871        {
872            // new geographic coords
873            double newLong = 0, newLat = 0;
874            int i, nset;
875            char msg[128];
876
877          if (checkPower(&geoNP))
878           return;
879
880
881            for (nset = i = 0; i < n; i++)
882            {
883                INumber *geop = IUFindNumber (&geoNP, names[i]);
884                if (geop == &geo[0])
885                {
886                    newLat = values[i];
887                    nset += newLat >= -90.0 && newLat <= 90.0;
888                } else if (geop == &geo[1])
889                {
890                    newLong = values[i];
891                    nset += newLong >= 0.0 && newLong < 360.0;
892                }
893            }
894
895            if (nset == 2)
896            {
897                char l[32], L[32];
898                geoNP.s = IPS_OK;
899                fs_sexa (l, newLat, 3, 3600);
900                fs_sexa (L, newLong, 4, 3600);
901               
902                if (!simulation)
903                {
904                        if ( ( err = setSiteLongitude(fd, 360.0 - newLong) < 0) )
905                        {       
906                                handleError(&geoNP, err, "Setting site longitude coordinates"); 
907                                return;
908                        }       
909                        if ( ( err = setSiteLatitude(fd, newLat) < 0) )
910                        {
911                                handleError(&geoNP, err, "Setting site latitude coordinates");
912                                return;
913                        }
914                }
915               
916                geoNP.np[0].value = newLat;
917                geoNP.np[1].value = newLong;
918                snprintf (msg, sizeof(msg), "Site location updated to Lat %.32s - Long %.32s", l, L);
919            } else
920            {
921                geoNP.s = IPS_IDLE;
922                strcpy(msg, "Lat or Long missing or invalid");
923            }
924            IDSetNumber (&geoNP, "%s", msg);
925            return;
926        }
927
928        // Update Frequency
929        if ( !strcmp (name, TrackingFreqNP.name) )
930        {
931
932         if (checkPower(&TrackingFreqNP))
933          return;
934
935          IDLog("Trying to set track freq of: %f\n", values[0]);
936
937          if ( ( err = setTrackFreq(fd, values[0])) < 0) 
938          {
939             handleError(&TrackingFreqNP, err, "Setting tracking frequency");
940             return;
941         }
942         
943         TrackingFreqNP.s = IPS_OK;
944         TrackingFreqNP.np[0].value = values[0];
945         IDSetNumber(&TrackingFreqNP, "Tracking frequency set to %04.1f", values[0]);
946         if (trackingMode != LX200_TRACK_MANUAL)
947         {
948              trackingMode = LX200_TRACK_MANUAL;
949              TrackModeS[0].s = ISS_OFF;
950              TrackModeS[1].s = ISS_OFF;
951              TrackModeS[2].s = ISS_ON;
952              TrackModeSP.s   = IPS_OK;
953              selectTrackingMode(fd, trackingMode);
954              IDSetSwitch(&TrackModeSP, NULL);
955         }
956         
957          return;
958        }
959       
960        if (!strcmp(name, FocusTimerNP.name))
961        {
962          if (checkPower(&FocusTimerNP))
963           return;
964           
965          // Don't update if busy
966          if (FocusTimerNP.s == IPS_BUSY)
967           return;
968           
969          IUUpdateNumber(&FocusTimerNP, values, names, n);
970         
971          FocusTimerNP.s = IPS_OK;
972         
973          IDSetNumber(&FocusTimerNP, NULL);
974          IDLog("Setting focus timer to %g\n", FocusTimerN[0].value);
975         
976          return;
977
978        }
979
980        if (!strcmp(name, GuideNSNP.name))
981        {
982          long direction;
983          int duration_msec;
984          int use_pulse_cmd;
985          if (checkPower(&GuideNSNP))
986           return;
987          if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
988          {
989                handleError(&GuideNSNP, err, "Can't guide while moving");
990                return;
991          }
992          if (GuideNSNP.s == IPS_BUSY)
993          {
994                // Already guiding so stop before restarting timer
995                HaltMovement(fd, LX200_NORTH);
996                HaltMovement(fd, LX200_SOUTH);
997          }
998          if (GuideNSTID) {
999                IERmTimer(GuideNSTID);
1000                GuideNSTID = 0;
1001          }
1002          IUUpdateNumber(&GuideNSNP, values, names, n);
1003
1004          if (GuideNSNP.np[0].value > 0) {
1005                duration_msec = GuideNSNP.np[0].value * 1000;
1006                direction = LX200_NORTH;
1007          } else {
1008                duration_msec = GuideNSNP.np[1].value * 1000;
1009                direction = LX200_SOUTH;
1010          }
1011          if (duration_msec <= 0) {
1012                GuideNSNP.s = IPS_IDLE;
1013                IDSetNumber (&GuideNSNP, NULL);
1014                return;
1015          }
1016          use_pulse_cmd = getOnSwitch(&UsePulseCmdSP);
1017          // fprintf(stderr, "Using %s mode to move %dmsec %s\n",
1018          //         use_pulse_cmd ? "Pulse" : "Legacy",
1019          //         duration_msec, direction == LX200_NORTH ? "North" : "South");
1020          if (use_pulse_cmd) {
1021                SendPulseCmd(fd, direction, duration_msec);
1022          } else {
1023                if ( ( err = setSlewMode(fd, LX200_SLEW_GUIDE) < 0) )
1024                {
1025                        handleError(&SlewModeSP, err, "Setting slew mode");
1026                        return;
1027                }
1028                MoveTo(fd, direction);
1029          }
1030          GuideNSTID = IEAddTimer (duration_msec, guideTimeout, (void *)direction);
1031          GuideNSNP.s = IPS_BUSY;
1032          IDSetNumber(&GuideNSNP, NULL);
1033        }
1034        if (!strcmp(name, GuideWENP.name))
1035        {
1036          long direction;
1037          int duration_msec;
1038          int use_pulse_cmd;
1039
1040          if (checkPower(&GuideWENP))
1041           return;
1042          if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
1043          {
1044                handleError(&GuideWENP, err, "Can't guide while moving");
1045                return;
1046          }
1047          if (GuideWENP.s == IPS_BUSY)
1048          {
1049                // Already guiding so stop before restarting timer
1050                HaltMovement(fd, LX200_WEST);
1051                HaltMovement(fd, LX200_EAST);
1052          }
1053          if (GuideWETID) {
1054                IERmTimer(GuideWETID);
1055                GuideWETID = 0;
1056          }
1057          IUUpdateNumber(&GuideWENP, values, names, n);
1058
1059          if (GuideWENP.np[0].value > 0) {
1060                duration_msec = GuideWENP.np[0].value * 1000;
1061                direction = LX200_WEST;
1062          } else {
1063                duration_msec = GuideWENP.np[1].value * 1000;
1064                direction = LX200_EAST;
1065          }
1066          if (duration_msec <= 0) {
1067                GuideWENP.s = IPS_IDLE;
1068                IDSetNumber (&GuideWENP, NULL);
1069                return;
1070          }
1071          use_pulse_cmd = getOnSwitch(&UsePulseCmdSP);
1072          // fprintf(stderr, "Using %s mode to move %dmsec %s\n",
1073          //         use_pulse_cmd ? "Pulse" : "Legacy",
1074          //         duration_msec, direction == LX200_WEST ? "West" : "East");
1075         
1076          if (use_pulse_cmd) {
1077                SendPulseCmd(fd, direction, duration_msec);
1078          } else {
1079                if ( ( err = setSlewMode(fd, LX200_SLEW_GUIDE) < 0) )
1080                {
1081                        handleError(&SlewModeSP, err, "Setting slew mode");
1082                        return;
1083                }
1084                MoveTo(fd, direction);
1085          }
1086          GuideWETID = IEAddTimer (duration_msec, guideTimeout, (void *)direction);
1087          GuideWENP.s = IPS_BUSY;
1088          IDSetNumber(&GuideWENP, NULL);
1089        }
1090}
1091
1092void LX200Generic::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
1093{
1094        int err=0, index=0;
1095        INDI_UNUSED(names);
1096
1097        // ignore if not ours //
1098        if (strcmp (thisDevice, dev))
1099            return;
1100
1101        // FIRST Switch ALWAYS for power
1102        if (!strcmp (name, ConnectSP.name))
1103        {
1104         bool connectionEstablished = (ConnectS[0].s == ISS_ON);
1105         if (IUUpdateSwitch(&ConnectSP, states, names, n) < 0) return;
1106         if ( (connectionEstablished && ConnectS[0].s == ISS_ON) || (!connectionEstablished && ConnectS[1].s == ISS_ON))
1107         {
1108                ConnectSP.s = IPS_OK;
1109                IDSetSwitch(&ConnectSP, NULL);
1110                return;
1111         }
1112         connectTelescope();
1113         return;
1114        }
1115
1116        // Coord set
1117        if (!strcmp(name, OnCoordSetSP.name))
1118        {
1119          if (checkPower(&OnCoordSetSP))
1120           return;
1121
1122          if (IUUpdateSwitch(&OnCoordSetSP, states, names, n) < 0) return;
1123          currentSet = getOnSwitch(&OnCoordSetSP);
1124          OnCoordSetSP.s = IPS_OK;
1125          IDSetSwitch(&OnCoordSetSP, NULL);
1126        }
1127       
1128        // Abort Slew
1129        if (!strcmp (name, AbortSlewSP.name))
1130        {
1131          if (checkPower(&AbortSlewSP))
1132          {
1133            AbortSlewSP.s = IPS_IDLE;
1134            IDSetSwitch(&AbortSlewSP, NULL);
1135            return;
1136          }
1137         
1138          IUResetSwitch(&AbortSlewSP);
1139          if (abortSlew(fd) < 0)
1140          {
1141                AbortSlewSP.s = IPS_ALERT;
1142                IDSetSwitch(&AbortSlewSP, NULL);
1143                return;
1144          }
1145
1146            if (EquatorialCoordsWNP.s == IPS_BUSY)
1147            {
1148                AbortSlewSP.s = IPS_OK;
1149                EquatorialCoordsWNP.s       = IPS_IDLE;
1150                IDSetSwitch(&AbortSlewSP, "Slew aborted.");
1151                IDSetNumber(&EquatorialCoordsWNP, NULL);
1152            }
1153            else if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
1154            {
1155                MovementNSSP.= MovementWESP.s =  IPS_IDLE; 
1156       
1157                AbortSlewSP.s = IPS_OK;         
1158                EquatorialCoordsWNP.s       = IPS_IDLE;
1159                IUResetSwitch(&MovementNSSP);
1160                IUResetSwitch(&MovementWESP);
1161                IUResetSwitch(&AbortSlewSP);
1162
1163                IDSetSwitch(&AbortSlewSP, "Slew aborted.");
1164                IDSetSwitch(&MovementNSSP, NULL);
1165                IDSetSwitch(&MovementWESP, NULL);
1166                IDSetNumber(&EquatorialCoordsWNP, NULL);
1167            }
1168            else if (GuideNSNP.s == IPS_BUSY || GuideWENP.s == IPS_BUSY)
1169            {
1170                GuideNSNP.= GuideWENP.s =  IPS_IDLE; 
1171                GuideNSN[0].value = GuideNSN[1].value = 0.0;
1172                GuideWEN[0].value = GuideWEN[1].value = 0.0;
1173                if (GuideNSTID) {
1174                        IERmTimer(GuideNSTID);
1175                        GuideNSTID = 0;
1176                }
1177                if (GuideWETID) {
1178                        IERmTimer(GuideWETID);
1179                        GuideNSTID = 0;
1180                }
1181
1182                AbortSlewSP.s = IPS_OK;
1183                EquatorialCoordsWNP.s       = IPS_IDLE;
1184                IUResetSwitch(&AbortSlewSP);
1185
1186                IDSetSwitch(&AbortSlewSP, "Guide aborted.");
1187                IDSetNumber(&GuideNSNP, NULL);
1188                IDSetNumber(&GuideWENP, NULL);
1189                IDSetNumber(&EquatorialCoordsWNP, NULL);
1190            }
1191            else
1192            {
1193                AbortSlewSP.s = IPS_OK;
1194                IDSetSwitch(&AbortSlewSP, NULL);
1195            }
1196
1197            return;
1198        }
1199
1200        // Alignment
1201        if (!strcmp (name, AlignmentSw.name))
1202        {
1203          if (checkPower(&AlignmentSw))
1204           return;
1205
1206          if (IUUpdateSwitch(&AlignmentSw, states, names, n) < 0) return;
1207          index = getOnSwitch(&AlignmentSw);
1208
1209          if ( ( err = setAlignmentMode(fd, index) < 0) )
1210          {
1211             handleError(&AlignmentSw, err, "Setting alignment");
1212             return;
1213          }
1214         
1215          AlignmentSw.s = IPS_OK;
1216          IDSetSwitch (&AlignmentSw, NULL);
1217          return;
1218
1219        }
1220
1221        // Sites
1222        if (!strcmp (name, SitesSP.name))
1223        {
1224          int dd=0, mm=0;
1225
1226          if (checkPower(&SitesSP))
1227           return;
1228
1229          if (IUUpdateSwitch(&SitesSP, states, names, n) < 0) return;
1230          currentSiteNum = getOnSwitch(&SitesSP) + 1;
1231         
1232          if ( ( err = selectSite(fd, currentSiteNum) < 0) )
1233          {
1234              handleError(&SitesSP, err, "Selecting sites");
1235              return;
1236          }
1237
1238          if ( ( err = getSiteLatitude(fd, &dd, &mm) < 0))
1239          {
1240              handleError(&SitesSP, err, "Selecting sites");
1241              return;
1242          }
1243
1244          if (dd > 0) geoNP.np[0].value = dd + mm / 60.0;
1245          else geoNP.np[0].value = dd - mm / 60.0;
1246         
1247          if ( ( err = getSiteLongitude(fd, &dd, &mm) < 0))
1248          {
1249                handleError(&SitesSP, err, "Selecting sites");
1250                return;
1251          }
1252         
1253          if (dd > 0) geoNP.np[1].value = 360.0 - (dd + mm / 60.0);
1254          else geoNP.np[1].value = (dd - mm / 60.0) * -1.0;
1255         
1256          getSiteName(fd, SiteNameTP.tp[0].text, currentSiteNum);
1257
1258          IDLog("Selecting site %d\n", currentSiteNum);
1259         
1260          geoNP.s = SiteNameTP.s = SitesSP.s = IPS_OK;
1261
1262          IDSetNumber (&geoNP, NULL);
1263          IDSetText   (&SiteNameTP, NULL);
1264          IDSetSwitch (&SitesSP, NULL);
1265          return;
1266        }
1267
1268        // Focus Motion
1269        if (!strcmp (name, FocusMotionSP.name))
1270        {
1271          if (checkPower(&FocusMotionSP))
1272           return;
1273
1274          // If mode is "halt"
1275          if (FocusModeS[0].s == ISS_ON)
1276          {
1277            FocusMotionSP.s = IPS_IDLE;
1278            IDSetSwitch(&FocusMotionSP, NULL);
1279            return;
1280          }
1281         
1282          if (IUUpdateSwitch(&FocusMotionSP, states, names, n) < 0) return;
1283          index = getOnSwitch(&FocusMotionSP);
1284         
1285          if ( ( err = setFocuserMotion(fd, index) < 0) )
1286          {
1287             handleError(&FocusMotionSP, err, "Setting focuser speed");
1288             return;
1289          }
1290
1291          FocusMotionSP.s = IPS_BUSY;
1292         
1293          // with a timer
1294          if (FocusTimerN[0].value > 0) 
1295          {
1296             FocusTimerNP.= IPS_BUSY;
1297             IEAddTimer(50, LX200Generic::updateFocusTimer, this);
1298          }
1299         
1300          IDSetSwitch(&FocusMotionSP, NULL);
1301          return;
1302        }
1303
1304        // Slew mode
1305        if (!strcmp (name, SlewModeSP.name))
1306        {
1307          if (checkPower(&SlewModeSP))
1308           return;
1309
1310          if (IUUpdateSwitch(&SlewModeSP, states, names, n) < 0) return;
1311          index = getOnSwitch(&SlewModeSP);
1312           
1313          if ( ( err = setSlewMode(fd, index) < 0) )
1314          {
1315              handleError(&SlewModeSP, err, "Setting slew mode");
1316              return;
1317          }
1318         
1319          SlewModeSP.s = IPS_OK;
1320          IDSetSwitch(&SlewModeSP, NULL);
1321          return;
1322        }
1323
1324        // Movement (North/South)
1325        if (!strcmp (name, MovementNSSP.name))
1326        {
1327          if (checkPower(&MovementNSSP))
1328           return;
1329          if (GuideNSNP.s == IPS_BUSY || GuideWENP.s == IPS_BUSY)
1330          {
1331              handleError(&MovementNSSP, err, "Can't move while guiding");
1332              return;
1333          }
1334
1335         int last_move=-1;
1336         int current_move = -1;
1337
1338        // -1 means all off previously
1339         last_move = getOnSwitch(&MovementNSSP);
1340
1341         if (IUUpdateSwitch(&MovementNSSP, states, names, n) < 0)
1342                return;
1343
1344        current_move = getOnSwitch(&MovementNSSP);
1345
1346        // Previosuly active switch clicked again, so let's stop.
1347        if (current_move == last_move)
1348        {
1349                HaltMovement(fd, (current_move == 0) ? LX200_NORTH : LX200_SOUTH);
1350                IUResetSwitch(&MovementNSSP);
1351                MovementNSSP.s = IPS_IDLE;
1352                IDSetSwitch(&MovementNSSP, NULL);
1353                return;
1354        }
1355
1356        #ifdef INDI_DEBUG
1357        IDLog("Current Move: %d - Previous Move: %d\n", current_move, last_move);
1358        #endif
1359
1360        // 0 (North) or 1 (South)
1361        last_move      = current_move;
1362
1363        // Correction for LX200 Driver: North 0 - South 3
1364        current_move = (current_move == 0) ? LX200_NORTH : LX200_SOUTH;
1365
1366        if ( ( err = MoveTo(fd, current_move) < 0) )
1367        {
1368                         handleError(&MovementNSSP, err, "Setting motion direction");
1369                        return;
1370        }
1371       
1372          MovementNSSP.s = IPS_BUSY;
1373          IDSetSwitch(&MovementNSSP, "Moving toward %s", (current_move == LX200_NORTH) ? "North" : "South");
1374          return;
1375        }
1376
1377        // Movement (West/East)
1378        if (!strcmp (name, MovementWESP.name))
1379        {
1380          if (checkPower(&MovementWESP))
1381           return;
1382          if (GuideNSNP.s == IPS_BUSY || GuideWENP.s == IPS_BUSY)
1383          {
1384              handleError(&MovementWESP, err, "Can't move while guiding");
1385              return;
1386          }
1387
1388         int last_move=-1;
1389         int current_move = -1;
1390
1391        // -1 means all off previously
1392         last_move = getOnSwitch(&MovementWESP);
1393
1394         if (IUUpdateSwitch(&MovementWESP, states, names, n) < 0)
1395                return;
1396
1397        current_move = getOnSwitch(&MovementWESP);
1398
1399        // Previosuly active switch clicked again, so let's stop.
1400        if (current_move == last_move)
1401        {
1402                HaltMovement(fd, (current_move ==0) ? LX200_WEST : LX200_EAST);
1403                IUResetSwitch(&MovementWESP);
1404                MovementWESP.s = IPS_IDLE;
1405                IDSetSwitch(&MovementWESP, NULL);
1406                return;
1407        }
1408
1409        #ifdef INDI_DEBUG
1410        IDLog("Current Move: %d - Previous Move: %d\n", current_move, last_move);
1411        #endif
1412
1413        // 0 (West) or 1 (East)
1414        last_move      = current_move;
1415
1416        // Correction for LX200 Driver: West 1 - East 2
1417        current_move = (current_move == 0) ? LX200_WEST : LX200_EAST;
1418
1419        if ( ( err = MoveTo(fd, current_move) < 0) )
1420        {
1421                         handleError(&MovementWESP, err, "Setting motion direction");
1422                        return;
1423        }
1424       
1425          MovementWESP.s = IPS_BUSY;
1426          IDSetSwitch(&MovementWESP, "Moving toward %s", (current_move == LX200_WEST) ? "West" : "East");
1427          return;
1428        }
1429
1430        // Tracking mode
1431        if (!strcmp (name, TrackModeSP.name))
1432        {
1433          if (checkPower(&TrackModeSP))
1434           return;
1435
1436          IUResetSwitch(&TrackModeSP);
1437          IUUpdateSwitch(&TrackModeSP, states, names, n);
1438          trackingMode = getOnSwitch(&TrackModeSP);
1439         
1440          if ( ( err = selectTrackingMode(fd, trackingMode) < 0) )
1441          {
1442                 handleError(&TrackModeSP, err, "Setting tracking mode.");
1443                 return;
1444          }
1445         
1446          getTrackFreq(fd, &TrackFreqN[0].value);
1447          TrackModeSP.s = IPS_OK;
1448          IDSetNumber(&TrackingFreqNP, NULL);
1449          IDSetSwitch(&TrackModeSP, NULL);
1450          return;
1451        }
1452
1453        // Focus speed
1454        if (!strcmp (name, FocusModeSP.name))
1455        {
1456          if (checkPower(&FocusModeSP))
1457           return;
1458
1459          IUResetSwitch(&FocusModeSP);
1460          IUUpdateSwitch(&FocusModeSP, states, names, n);
1461
1462          index = getOnSwitch(&FocusModeSP);
1463
1464          /* disable timer and motion */
1465          if (index == 0)
1466          {
1467            IUResetSwitch(&FocusMotionSP);
1468            FocusMotionSP.s = IPS_IDLE;
1469            FocusTimerNP.= IPS_IDLE;
1470            IDSetSwitch(&FocusMotionSP, NULL);
1471            IDSetNumber(&FocusTimerNP, NULL);
1472          }
1473           
1474          setFocuserSpeedMode(fd, index);
1475          FocusModeSP.s = IPS_OK;
1476          IDSetSwitch(&FocusModeSP, NULL);
1477          return;
1478        }
1479
1480        // Pulse-Guide command support
1481        if (!strcmp (name, UsePulseCmdSP.name))
1482        {
1483          if (checkPower(&UsePulseCmdSP))
1484           return;
1485
1486          IUResetSwitch(&UsePulseCmdSP);
1487          IUUpdateSwitch(&UsePulseCmdSP, states, names, n);
1488
1489          UsePulseCmdSP.s = IPS_OK;
1490          IDSetSwitch(&UsePulseCmdSP, NULL);
1491          return;
1492        }
1493}
1494
1495void LX200Generic::handleError(ISwitchVectorProperty *svp, int err, const char *msg)
1496{
1497 
1498  svp->s = IPS_ALERT;
1499 
1500  /* First check to see if the telescope is connected */
1501    if (check_lx200_connection(fd))
1502    {
1503      /* The telescope is off locally */
1504      ConnectS[0].s = ISS_OFF;
1505      ConnectS[1].s = ISS_ON;
1506      ConnectSP.s = IPS_BUSY;
1507      IDSetSwitch(&ConnectSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1508     
1509      IDSetSwitch(svp, NULL);
1510      IEAddTimer(10000, retryConnection, &fd);
1511      return;
1512    }
1513   
1514   /* If the error is a time out, then the device doesn't support this property or busy*/
1515      if (err == -2)
1516      {
1517       svp->s = IPS_ALERT;
1518       IDSetSwitch(svp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1519      }
1520      else
1521    /* Changing property failed, user should retry. */
1522       IDSetSwitch( svp , "%s failed.", msg);
1523       
1524       fault = true;
1525}
1526
1527void LX200Generic::handleError(INumberVectorProperty *nvp, int err, const char *msg)
1528{
1529 
1530  nvp->s = IPS_ALERT;
1531 
1532  /* First check to see if the telescope is connected */
1533    if (check_lx200_connection(fd))
1534    {
1535      /* The telescope is off locally */
1536      ConnectS[0].s = ISS_OFF;
1537      ConnectS[1].s = ISS_ON;
1538      ConnectSP.s = IPS_BUSY;
1539      IDSetSwitch(&ConnectSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1540     
1541      IDSetNumber(nvp, NULL);
1542      IEAddTimer(10000, retryConnection, &fd);
1543      return;
1544    }
1545   
1546   /* If the error is a time out, then the device doesn't support this property */
1547      if (err == -2)
1548      {
1549       nvp->s = IPS_ALERT;
1550       IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1551      }
1552      else
1553    /* Changing property failed, user should retry. */
1554       IDSetNumber( nvp , "%s failed.", msg);
1555       
1556       fault = true;
1557}
1558
1559void LX200Generic::handleError(ITextVectorProperty *tvp, int err, const char *msg)
1560{
1561 
1562  tvp->s = IPS_ALERT;
1563 
1564  /* First check to see if the telescope is connected */
1565    if (check_lx200_connection(fd))
1566    {
1567      /* The telescope is off locally */
1568      ConnectS[0].s = ISS_OFF;
1569      ConnectS[1].s = ISS_ON;
1570      ConnectSP.s = IPS_BUSY;
1571      IDSetSwitch(&ConnectSP, "Telescope is not responding to commands, will retry in 10 seconds.");
1572     
1573      IDSetText(tvp, NULL);
1574      IEAddTimer(10000, retryConnection, &fd);
1575      return;
1576    }
1577   
1578   /* If the error is a time out, then the device doesn't support this property */
1579      if (err == -2)
1580      {
1581       tvp->s = IPS_ALERT;
1582       IDSetText(tvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
1583      }
1584       
1585      else
1586    /* Changing property failed, user should retry. */
1587       IDSetText( tvp , "%s failed.", msg);
1588       
1589       fault = true;
1590}
1591
1592 void LX200Generic::correctFault()
1593 {
1594 
1595   fault = false;
1596   IDMessage(thisDevice, "Telescope is online.");
1597   
1598 }
1599
1600bool LX200Generic::isTelescopeOn(void)
1601{
1602  //if (simulation) return true;
1603 
1604  return (ConnectSP.sp[0].s == ISS_ON);
1605}
1606
1607static void retryConnection(void * p)
1608{
1609  int fd = *( (int *) p);
1610 
1611  if (check_lx200_connection(fd))
1612  {
1613    ConnectSP.s = IPS_IDLE;
1614    IDSetSwitch(&ConnectSP, "The connection to the telescope is lost.");
1615    return;
1616  }
1617 
1618  ConnectS[0].s = ISS_ON;
1619  ConnectS[1].s = ISS_OFF;
1620  ConnectSP.s = IPS_OK;
1621   
1622  IDSetSwitch(&ConnectSP, "The connection to the telescope has been resumed.");
1623
1624}
1625
1626void LX200Generic::updateFocusTimer(void *p)
1627{
1628   int err=0;
1629
1630    switch (FocusTimerNP.s)
1631    {
1632
1633      case IPS_IDLE:
1634           break;
1635             
1636      case IPS_BUSY:
1637      IDLog("Focus Timer Value is %g\n", FocusTimerN[0].value);
1638            FocusTimerN[0].value-=50;
1639           
1640            if (FocusTimerN[0].value <= 0)
1641            {
1642              IDLog("Focus Timer Expired\n");
1643              if ( ( err = setFocuserSpeedMode(telescope->fd, 0) < 0) )
1644              {
1645                telescope->handleError(&FocusModeSP, err, "setting focuser mode");
1646                IDLog("Error setting focuser mode\n");
1647                return;
1648              } 
1649         
1650             
1651              FocusMotionSP.s = IPS_IDLE;
1652              FocusTimerNP.= IPS_OK;
1653              FocusModeSP.s   = IPS_OK;
1654             
1655              IUResetSwitch(&FocusMotionSP);
1656              IUResetSwitch(&FocusModeSP);
1657              FocusModeS[0].s = ISS_ON;
1658             
1659              IDSetSwitch(&FocusModeSP, NULL);
1660              IDSetSwitch(&FocusMotionSP, NULL);
1661            }
1662           
1663         IDSetNumber(&FocusTimerNP, NULL);
1664
1665          if (FocusTimerN[0].value > 0)
1666                IEAddTimer(50, LX200Generic::updateFocusTimer, p);
1667            break;
1668           
1669       case IPS_OK:
1670            break;
1671           
1672        case IPS_ALERT:
1673            break;
1674     }
1675
1676}
1677
1678void LX200Generic::guideTimeout(void *p)
1679{
1680    long direction = (long)p;
1681    int use_pulse_cmd;
1682
1683    use_pulse_cmd = telescope->getOnSwitch(&UsePulseCmdSP);
1684    if (direction == -1)
1685    {
1686        HaltMovement(telescope->fd, LX200_NORTH);
1687        HaltMovement(telescope->fd, LX200_SOUTH);
1688        HaltMovement(telescope->fd, LX200_EAST);
1689        HaltMovement(telescope->fd, LX200_WEST);
1690        IERmTimer(telescope->GuideNSTID);
1691        IERmTimer(telescope->GuideWETID);
1692       
1693    }
1694    else if (! use_pulse_cmd)
1695    {
1696        HaltMovement(telescope->fd, direction);
1697    }
1698    if (direction == LX200_NORTH || direction == LX200_SOUTH || direction == -1)
1699    {
1700        GuideNSNP.np[0].value = 0;
1701        GuideNSNP.np[1].value = 0;
1702        GuideNSNP.s = IPS_IDLE;
1703        telescope->GuideNSTID = 0;
1704        IDSetNumber(&GuideNSNP, NULL);
1705    }
1706    if (direction == LX200_WEST || direction == LX200_EAST || direction == -1)
1707    {
1708        GuideWENP.np[0].value = 0;
1709        GuideWENP.np[1].value = 0;
1710        GuideWENP.s = IPS_IDLE;
1711        telescope->GuideWETID = 0;
1712        IDSetNumber(&GuideWENP, NULL);
1713    }
1714}
1715
1716void LX200Generic::ISPoll()
1717{
1718        double dx, dy;
1719        /*static int okCounter = 3;*/
1720        int err=0;
1721       
1722        if (!isTelescopeOn())
1723         return;
1724
1725        if (simulation)
1726        {
1727                mountSim();
1728                return;
1729        }
1730
1731        if ( (err = getLX200RA(fd, &currentRA)) < 0 || (err = getLX200DEC(fd, &currentDEC)) < 0)
1732        {
1733          EquatorialCoordsRNP.s = IPS_ALERT;
1734          IDSetNumber(&EquatorialCoordsRNP, NULL);
1735          handleError(&EquatorialCoordsRNP, err, "Getting RA/DEC");
1736          return;
1737        }
1738       
1739        if (fault)
1740          correctFault();
1741
1742        EquatorialCoordsRNP.s = IPS_OK;
1743
1744        if ( fabs(lastRA - currentRA) > (SlewAccuracyN[0].value/(60.0*15.0)) || fabs(lastDEC - currentDEC) > (SlewAccuracyN[1].value/60.0))
1745        {
1746                lastRA  = currentRA;
1747                lastDEC = currentDEC;
1748                IDSetNumber (&EquatorialCoordsRNP, NULL);
1749        }
1750
1751        switch (EquatorialCoordsWNP.s)
1752        {
1753        case IPS_IDLE:
1754        break;
1755
1756        case IPS_BUSY:
1757            dx = targetRA - currentRA;
1758            dy = targetDEC - currentDEC;
1759
1760            // Wait until acknowledged or within threshold
1761            if ( fabs(dx) <= (SlewAccuracyN[0].value/(60.0*15.0)) && fabs(dy) <= (SlewAccuracyN[1].value/60.0))
1762            {
1763               lastRA  = currentRA;
1764               lastDEC = currentDEC;
1765               
1766               EquatorialCoordsWNP.s = IPS_OK;
1767               IDSetNumber(&EquatorialCoordsWNP, "Slew is complete, target locked...");
1768               
1769            break;
1770
1771        case IPS_OK:
1772        break;
1773
1774
1775        case IPS_ALERT:
1776            break;
1777        }
1778}
1779
1780}
1781// wildi nothing changed in LX200Generic::mountSim
1782void LX200Generic::mountSim ()
1783{
1784        static struct timeval ltv;
1785        struct timeval tv;
1786        double dt, da, dx;
1787        int nlocked;
1788
1789        /* update elapsed time since last poll, don't presume exactly POLLMS */
1790        gettimeofday (&tv, NULL);
1791       
1792        if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1793            ltv = tv;
1794           
1795        dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec)/1e6;
1796        ltv = tv;
1797        da = SLEWRATE*dt;
1798
1799        /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
1800        switch (EquatorialCoordsWNP.s)
1801        {
1802       
1803        /* #1 State is idle, update telesocpe at sidereal rate */
1804        case IPS_IDLE:
1805            /* RA moves at sidereal, Dec stands still */
1806            currentRA += (SIDRATE*dt/15.);
1807           
1808           IDSetNumber(&EquatorialCoordsRNP, NULL);
1809
1810            break;
1811
1812        case IPS_BUSY:
1813            /* slewing - nail it when both within one pulse @ SLEWRATE */
1814            nlocked = 0;
1815
1816            dx = targetRA - currentRA;
1817           
1818            if (fabs(dx) <= da)
1819            {
1820                currentRA = targetRA;
1821                nlocked++;
1822            }
1823            else if (dx > 0)
1824                currentRA += da/15.;
1825            else 
1826                currentRA -= da/15.;
1827           
1828
1829            dx = targetDEC - currentDEC;
1830            if (fabs(dx) <= da)
1831            {
1832                currentDEC = targetDEC;
1833                nlocked++;
1834            }
1835            else if (dx > 0)
1836              currentDEC += da;
1837            else
1838              currentDEC -= da;
1839
1840            if (nlocked == 2)
1841            {
1842                EquatorialCoordsRNP.s = IPS_OK;
1843                EquatorialCoordsWNP.s = IPS_OK;
1844                IDSetNumber(&EquatorialCoordsWNP, "Now tracking");
1845                IDSetNumber(&EquatorialCoordsRNP, NULL);
1846            } else
1847                IDSetNumber(&EquatorialCoordsRNP, NULL);
1848
1849            break;
1850
1851        case IPS_OK:
1852            /* tracking */
1853           IDSetNumber(&EquatorialCoordsRNP, NULL);
1854            break;
1855
1856        case IPS_ALERT:
1857            break;
1858        }
1859
1860}
1861
1862void LX200Generic::getBasicData()
1863{
1864
1865  int err;
1866  #ifdef HAVE_NOVA_H
1867  struct tm *timep;
1868  time_t ut;
1869  time (&ut);
1870  timep = gmtime (&ut);
1871  strftime (TimeTP.tp[0].text, strlen(TimeTP.tp[0].text), "%Y-%m-%dT%H:%M:%S", timep);
1872
1873  IDLog("PC UTC time is %s\n", TimeTP.tp[0].text);
1874  #endif
1875
1876  getAlignment();
1877 
1878  checkLX200Format(fd);
1879 
1880  if ( (err = getTimeFormat(fd, &timeFormat)) < 0)
1881     IDMessage(thisDevice, "Failed to retrieve time format from device.");
1882  else
1883  {
1884    timeFormat = (timeFormat == 24) ? LX200_24 : LX200_AM;
1885    // We always do 24 hours
1886    if (timeFormat != LX200_24)
1887      err = toggleTimeFormat(fd);
1888  }
1889// wildi proposal
1890  if ( (err = getLX200RA(fd, &targetRA)) < 0 || (err = getLX200DEC(fd, &targetDEC)) < 0)
1891  {
1892     EquatorialCoordsRNP.s = IPS_ALERT;
1893     IDSetNumber(&EquatorialCoordsRNP, NULL);
1894     handleError(&EquatorialCoordsRNP, err, "Getting RA/DEC");
1895     return;
1896  }
1897       
1898  if (fault)
1899        correctFault();
1900
1901//  getLX200RA(fd, &targetRA);
1902//  getLX200DEC(fd, &targetDEC);
1903
1904  EquatorialCoordsRNP.np[0].value = targetRA;
1905  EquatorialCoordsRNP.np[1].value = targetDEC;
1906 
1907  EquatorialCoordsRNP.s = IPS_OK;
1908  IDSetNumber (&EquatorialCoordsRNP, NULL); 
1909 
1910  SiteNameT[0].text = new char[64];
1911 
1912  if ( (err = getSiteName(fd, SiteNameT[0].text, currentSiteNum)) < 0)
1913    IDMessage(thisDevice, "Failed to get site name from device");
1914  else
1915    IDSetText   (&SiteNameTP, NULL);
1916 
1917  if ( (err = getTrackFreq(fd, &TrackFreqN[0].value)) < 0)
1918     IDMessage(thisDevice, "Failed to get tracking frequency from device.");
1919  else
1920     IDSetNumber (&TrackingFreqNP, NULL);
1921     
1922  /*updateLocation();
1923  updateTime();*/
1924 
1925}
1926
1927int LX200Generic::handleCoordSet()
1928{
1929
1930  int  err;
1931  char syncString[256];
1932  char RAStr[32], DecStr[32];
1933
1934  switch (currentSet)
1935  {
1936    // Slew
1937    case LX200_TRACK:
1938          lastSet = LX200_TRACK;
1939          if (EquatorialCoordsWNP.s == IPS_BUSY)
1940          {
1941             #ifdef INDI_DEBUG
1942             IDLog("Aboring Slew\n");
1943             #endif
1944             if (abortSlew(fd) < 0)
1945             {
1946                AbortSlewSP.s = IPS_ALERT;
1947                IDSetSwitch(&AbortSlewSP, NULL);
1948                slewError(err);
1949                return (-1);
1950             }
1951
1952             AbortSlewSP.s = IPS_OK;
1953             EquatorialCoordsWNP.s       = IPS_IDLE;
1954             IDSetSwitch(&AbortSlewSP, "Slew aborted.");
1955             IDSetNumber(&EquatorialCoordsWNP, NULL);
1956
1957             if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
1958             {
1959                MovementNSSP.= MovementWESP.s =  IPS_IDLE; 
1960                EquatorialCoordsWNP.s       = IPS_IDLE;
1961                IUResetSwitch(&MovementNSSP);
1962                IUResetSwitch(&MovementWESP);
1963                IUResetSwitch(&AbortSlewSP);
1964
1965                IDSetSwitch(&MovementNSSP, NULL);
1966                IDSetSwitch(&MovementWESP, NULL);
1967              }
1968
1969        // sleep for 100 mseconds
1970        usleep(100000);
1971        }
1972
1973        if ((err = Slew(fd))) /* Slew reads the '0', that is not the end of the slew */
1974        {
1975            IDMessage(mydev "ERROR Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1976            slewError(err);
1977            return  -1;
1978        }
1979
1980        EquatorialCoordsWNP.s = IPS_BUSY;
1981        fs_sexa(RAStr, targetRA, 2, 3600);
1982        fs_sexa(DecStr, targetDEC, 2, 3600);
1983        IDSetNumber(&EquatorialCoordsWNP, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
1984        #ifdef INDI_DEBUG
1985        IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1986        #endif
1987        break;
1988
1989    // Sync
1990    case LX200_SYNC:
1991          lastSet = LX200_SYNC;
1992           
1993        if (!simulation)
1994          if ( ( err = Sync(fd, syncString) < 0) )
1995          {
1996                EquatorialCoordsWNP.s = IPS_ALERT;
1997                IDSetNumber(&EquatorialCoordsWNP , "Synchronization failed.");
1998                return (-1);
1999          }
2000
2001          EquatorialCoordsWNP.s = IPS_OK;
2002          IDLog("Synchronization successful %s\n", syncString);
2003          IDSetNumber(&EquatorialCoordsWNP, "Synchronization successful.");
2004          break;
2005         
2006   }
2007   
2008   return (0);
2009
2010}
2011
2012int LX200Generic::getOnSwitch(ISwitchVectorProperty *sp)
2013{
2014 for (int i=0; i < sp->nsp ; i++)
2015     if (sp->sp[i].s == ISS_ON)
2016      return i;
2017
2018 return -1;
2019}
2020
2021
2022int LX200Generic::checkPower(ISwitchVectorProperty *sp)
2023{
2024  if (simulation) return 0;
2025 
2026  if (ConnectSP.s != IPS_OK)
2027  {
2028    if (!strcmp(sp->label, ""))
2029        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->name);
2030    else
2031        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->label);
2032       
2033    sp->s = IPS_IDLE;
2034    IDSetSwitch(sp, NULL);
2035    return -1;
2036  }
2037
2038  return 0;
2039}
2040
2041int LX200Generic::checkPower(INumberVectorProperty *np)
2042{
2043  if (simulation) return 0;
2044 
2045  if (ConnectSP.s != IPS_OK)
2046  {
2047    if (!strcmp(np->label, ""))
2048        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->name);
2049    else
2050        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->label);
2051       
2052    np->s = IPS_IDLE;
2053    IDSetNumber(np, NULL);
2054    return -1;
2055  }
2056
2057  return 0;
2058
2059}
2060
2061int LX200Generic::checkPower(ITextVectorProperty *tp)
2062{
2063
2064  if (simulation) return 0;
2065 
2066  if (ConnectSP.s != IPS_OK)
2067  {
2068    if (!strcmp(tp->label, ""))
2069        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->name);
2070    else
2071        IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->label);
2072       
2073    tp->s = IPS_IDLE;
2074    IDSetText(tp, NULL);
2075    return -1;
2076  }
2077
2078  return 0;
2079
2080}
2081
2082void LX200Generic::connectTelescope()
2083{
2084     switch (ConnectSP.sp[0].s)
2085     {
2086      case ISS_ON: 
2087       
2088        if (simulation)
2089        {
2090          ConnectSP.s = IPS_OK;
2091          IDSetSwitch (&ConnectSP, "Simulated telescope is online.");
2092          //updateTime();
2093          return;
2094        }
2095       
2096         if (tty_connect(PortTP.tp[0].text, 9600, 8, 0, 1, &fd) != TTY_OK)
2097         {
2098           ConnectS[0].s = ISS_OFF;
2099           ConnectS[1].s = ISS_ON;
2100           IDSetSwitch (&ConnectSP, "Error connecting to port %s. Make sure you have BOTH write and read permission to your port.\n", PortTP.tp[0].text);
2101           return;
2102         }
2103         if (check_lx200_connection(fd))
2104         {   
2105           ConnectS[0].s = ISS_OFF;
2106           ConnectS[1].s = ISS_ON;
2107           IDSetSwitch (&ConnectSP, "Error connecting to Telescope. Telescope is offline.");
2108           return;
2109         }
2110
2111        #ifdef INDI_DEBUG
2112        IDLog("Telescope test successfful.\n");
2113        #endif
2114
2115        *((int *) UTCOffsetN[0].aux0) = 0;
2116        ConnectSP.s = IPS_OK;
2117        IDSetSwitch (&ConnectSP, "Telescope is online. Retrieving basic data...");
2118        getBasicData();
2119        break;
2120
2121     case ISS_OFF:
2122         ConnectS[0].s = ISS_OFF;
2123         ConnectS[1].s = ISS_ON;
2124         ConnectSP.s = IPS_IDLE;
2125         IDSetSwitch (&ConnectSP, "Telescope is offline.");
2126         IDLog("Telescope is offline.");
2127         tty_disconnect(fd);
2128         break;
2129
2130    }
2131
2132}
2133
2134void LX200Generic::slewError(int slewCode)
2135{
2136    EquatorialCoordsWNP.s = IPS_ALERT;
2137
2138    if (slewCode == 1)
2139        IDSetNumber(&EquatorialCoordsWNP, "Object below horizon.");
2140    else if (slewCode == 2)
2141        IDSetNumber(&EquatorialCoordsWNP, "Object below the minimum elevation limit.");
2142    else
2143        IDSetNumber(&EquatorialCoordsWNP, "Slew failed.");
2144
2145}
2146
2147void LX200Generic::getAlignment()
2148{
2149
2150   if (ConnectSP.s != IPS_OK)
2151    return;
2152
2153   signed char align = ACK(fd);
2154   if (align < 0)
2155   {
2156     IDSetSwitch (&AlignmentSw, "Failed to get telescope alignment.");
2157     return;
2158   }
2159
2160   AlignmentS[0].s = ISS_OFF;
2161   AlignmentS[1].s = ISS_OFF;
2162   AlignmentS[2].s = ISS_OFF;
2163
2164    switch (align)
2165    {
2166      case 'P': AlignmentS[0].s = ISS_ON;
2167                break;
2168      case 'A': AlignmentS[1].s = ISS_ON;
2169                break;
2170      case 'L': AlignmentS[2].s = ISS_ON;
2171                break;
2172    }
2173
2174    AlignmentSw.s = IPS_OK;
2175    IDSetSwitch (&AlignmentSw, NULL);
2176    IDLog("ACK success %c\n", align);
2177}
2178
2179void LX200Generic::enableSimulation(bool enable)
2180{
2181   simulation = enable;
2182   
2183   if (simulation)
2184     IDLog("Warning: Simulation is activated.\n");
2185   else
2186     IDLog("Simulation is disabled.\n");
2187}
2188
2189void LX200Generic::updateTime()
2190{
2191  #ifdef HAVE_NOVA_H
2192  char cdate[32];
2193  double ctime;
2194  int h, m, s, lx200_utc_offset=0;
2195  int day, month, year, result;
2196  struct tm ltm;
2197  struct tm utm;
2198  time_t time_epoch;
2199 
2200  if (simulation)
2201  {
2202    sprintf(TimeT[0].text, "%d-%02d-%02dT%02d:%02d:%02d", 1979, 6, 25, 3, 30, 30);
2203    IDLog("Telescope ISO date and time: %s\n", TimeT[0].text);
2204    IDSetText(&TimeTP, NULL);
2205    return;
2206  }
2207
2208  getUTCOffset(fd, &lx200_utc_offset);
2209
2210  // LX200 TimeT Offset is defined at the number of hours added to LOCAL TIME to get TimeT. This is contrary to the normal definition.
2211  UTCOffsetN[0].value = lx200_utc_offset*-1;
2212
2213  // We got a valid value for UTCOffset now
2214  *((int *) UTCOffsetN[0].aux0) = 1; 
2215
2216  #ifdef INDI_DEBUG
2217  IDLog("Telescope TimeT Offset: %g\n", UTCOffsetN[0].value);
2218  #endif
2219
2220  getLocalTime24(fd, &ctime);
2221  getSexComponents(ctime, &h, &m, &s);
2222
2223  if ( (result = getSDTime(fd, &SDTimeN[0].value)) < 0)
2224    IDMessage(thisDevice, "Failed to retrieve siderial time from device.");
2225 
2226  getCalenderDate(fd, cdate);
2227  result = sscanf(cdate, "%d/%d/%d", &year, &month, &day);
2228  if (result != 3) return;
2229
2230  // Let's fill in the local time
2231  ltm.tm_sec = s;
2232  ltm.tm_min = m;
2233  ltm.tm_hour = h;
2234  ltm.tm_mday = day;
2235  ltm.tm_mon = month - 1;
2236  ltm.tm_year = year - 1900;
2237
2238  // Get time epoch
2239  time_epoch = mktime(&ltm);
2240
2241  // Convert to TimeT
2242  time_epoch -= (int) (UTCOffsetN[0].value * 60.0 * 60.0);
2243
2244  // Get UTC (we're using localtime_r, but since we shifted time_epoch above by UTCOffset, we should be getting the real UTC time)
2245  localtime_r(&time_epoch, &utm);
2246
2247  /* Format it into ISO 8601 */
2248  strftime(cdate, 32, "%Y-%m-%dT%H:%M:%S", &utm);
2249  IUSaveText(&TimeT[0], cdate);
2250 
2251  #ifdef INDI_DEBUG
2252  IDLog("Telescope Local Time: %02d:%02d:%02d\n", h, m , s);
2253  IDLog("Telescope SD Time is: %g\n", SDTimeN[0].value);
2254  IDLog("Telescope UTC Time: %s\n", TimeT[0].text);
2255  #endif
2256
2257  // Let's send everything to the client
2258  IDSetText(&TimeTP, NULL);
2259  IDSetNumber(&SDTimeNP, NULL);
2260  IDSetNumber(&UTCOffsetNP, NULL);
2261  #endif
2262
2263}
2264
2265void LX200Generic::updateLocation()
2266{
2267
2268 int dd = 0, mm = 0, err = 0;
2269
2270 if (simulation)
2271        return;
2272 
2273 if ( (err = getSiteLatitude(fd, &dd, &mm)) < 0)
2274    IDMessage(thisDevice, "Failed to get site latitude from device.");
2275  else
2276  {
2277    if (dd > 0)
2278        geoNP.np[0].value = dd + mm/60.0;
2279    else
2280        geoNP.np[0].value = dd - mm/60.0;
2281 
2282      IDLog("Autostar Latitude: %d:%d\n", dd, mm);
2283      IDLog("INDI Latitude: %g\n", geoNP.np[0].value);
2284  }
2285 
2286  if ( (err = getSiteLongitude(fd, &dd, &mm)) < 0)
2287    IDMessage(thisDevice, "Failed to get site longitude from device.");
2288  else
2289  {
2290    if (dd > 0) geoNP.np[1].value = 360.0 - (dd + mm/60.0);
2291    else geoNP.np[1].value = (dd - mm/60.0) * -1.0;
2292   
2293    IDLog("Autostar Longitude: %d:%d\n", dd, mm);
2294    IDLog("INDI Longitude: %g\n", geoNP.np[1].value);
2295  }
2296 
2297  IDSetNumber (&geoNP, NULL);
2298
2299}
2300
Note: See TracBrowser for help on using the repository browser.