source: BAORadio/libindi/v1/drivers/telescope/lx200generic.cpp @ 612

Last change on this file since 612 was 490, checked in by campagne, 14 years ago

import libindi (JEC)

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