3 Copyright (C
) 2003 Jasem
Mutlaq (mutlaqja@ikarustech
.com
)
5 This library is free software
; you can redistribute it
and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation
; either
8 version
2.1 of the License
, or (at your option
) any later version
.
10 This library is distributed in the hope that it will be useful
,
11 but WITHOUT ANY WARRANTY
; without even the implied warranty of
12 MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE
. See the GNU
13 Lesser General Public License
for more details
.
15 You should have received a copy of the GNU Lesser General Public
16 License along with
this library
; if not, write to the Free Software
17 Foundation
, Inc
., 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
29 #include "celestronprotocol.h"
30 #include "celestrongps.h"
32 #define RA_THRESHOLD 0.01
33 #define DEC_THRESHOLD 0.05
34 #define mydev "Celestron GPS"
36 CelestronGPS
*telescope
= NULL
;
39 /* There is _one_ binary for all LX200 drivers, but each binary is renamed
40 ** to its device name (i.e. lx200gps, lx200_16..etc). The main function will
41 ** fetch from std args the binary name and ISInit will create the apporpiate
42 ** device afterwards. If the binary name does not match any known devices,
43 ** we simply create a generic device
47 #define COMM_GROUP "Communication"
48 #define BASIC_GROUP "Main Control"
49 #define MOVE_GROUP "Movement Control"
51 static void ISPoll(void *);
54 static ISwitch PowerS
[] = {{"CONNECT" , "Connect" , ISS_OFF
, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON
, 0, 0}};
55 static ISwitch SlewModeS
[] = {{"Slew", "", ISS_ON
, 0, 0}, {"Find", "", ISS_OFF
, 0, 0}, {"Centering", "", ISS_OFF
, 0, 0}, {"Guide", "", ISS_OFF
, 0, 0}};
56 static ISwitch OnCoordSetS
[] = {{"SLEW", "Slew", ISS_ON
, 0 , 0}, {"TRACK", "Track", ISS_OFF
, 0, 0}, {"SYNC", "Sync", ISS_OFF
, 0, 0}};
57 static ISwitch abortSlewS
[] = {{"ABORT", "Abort", ISS_OFF
, 0, 0}};
59 static ISwitch MovementS
[] = {{"N", "North", ISS_OFF
, 0, 0}, {"W", "West", ISS_OFF
, 0, 0}, {"E", "East", ISS_OFF
, 0, 0}, {"S", "South", ISS_OFF
, 0, 0}};
61 /* equatorial position */
62 static INumber eq
[] = {
63 {"RA", "RA H:M:S", "%10.6m", 0., 24., 0., 0., 0, 0, 0},
64 {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0},
66 //TODO decide appropiate TIME_OUT
67 static INumberVectorProperty eqNum
= {
68 mydev
, "EQUATORIAL_EOD_COORD", "Equatorial JNow", BASIC_GROUP
, IP_RW
, 0, IPS_IDLE
,
69 eq
, NARRAY(eq
), "", 0};
71 /* Fundamental group */
72 static ISwitchVectorProperty PowerSw
= { mydev
, "CONNECTION" , "Connection", COMM_GROUP
, IP_RW
, ISR_1OFMANY
, 0, IPS_IDLE
, PowerS
, NARRAY(PowerS
), "", 0};
73 static IText PortT
[] = {{"PORT", "Port", 0, 0, 0, 0}};
74 static ITextVectorProperty Port
= { mydev
, "DEVICE_PORT", "Ports", COMM_GROUP
, IP_RW
, 0, IPS_IDLE
, PortT
, NARRAY(PortT
), "", 0};
77 static ISwitchVectorProperty OnCoordSetSw
= { mydev
, "ON_COORD_SET", "On Set", BASIC_GROUP
, IP_RW
, ISR_1OFMANY
, 0, IPS_IDLE
, OnCoordSetS
, NARRAY(OnCoordSetS
), "", 0};
78 static ISwitchVectorProperty abortSlewSw
= { mydev
, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP
, IP_RW
, ISR_1OFMANY
, 0, IPS_IDLE
, abortSlewS
, NARRAY(abortSlewS
), "", 0};
79 static ISwitchVectorProperty SlewModeSw
= { mydev
, "Slew rate", "", MOVE_GROUP
, IP_RW
, ISR_1OFMANY
, 0, IPS_IDLE
, SlewModeS
, NARRAY(SlewModeS
), "", 0};
81 static ISwitchVectorProperty MovementSw
= { mydev
, "MOVEMENT", "Move toward", MOVE_GROUP
, IP_RW
, ISR_1OFMANY
, 0, IPS_IDLE
, MovementS
, NARRAY(MovementS
), "", 0};
84 /* send client definitions of all properties */
94 PortT
[0].text
= strcpy(new char[32], "/dev/ttyS0");
96 telescope
= new CelestronGPS();
98 IEAddTimer (POLLMS
, ISPoll
, NULL
);
101 void ISGetProperties (const char *dev
)
102 { ISInit(); telescope
->ISGetProperties(dev
);}
103 void ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
104 { ISInit(); telescope
->ISNewSwitch(dev
, name
, states
, names
, n
);}
105 void ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
)
106 { ISInit(); telescope
->ISNewText(dev
, name
, texts
, names
, n
);}
107 void ISNewNumber (const char *dev
, const char *name
, double values
[], char *names
[], int n
)
108 { ISInit(); telescope
->ISNewNumber(dev
, name
, values
, names
, n
);}
109 void ISPoll (void *p
) { telescope
->ISPoll(); IEAddTimer (POLLMS
, ISPoll
, NULL
); p
=p
;}
110 void ISNewBLOB (const char */
*dev*/
, const char */
*name*/
, int */
*sizes
[]*/
, char **/
*blobs
[]*/
, char **/
*formats
[]*/
, char **/
*names
[]*/
, int /*n*/)
113 /**************************************************
114 *** LX200 Generic Implementation
115 ***************************************************/
117 CelestronGPS::CelestronGPS()
120 targetRA
= lastRA
= 0;
121 targetDEC
= lastDEC
= 0;
124 lastMove
[0] = lastMove
[1] = lastMove
[2] = lastMove
[3] = 0;
128 // Children call parent routines, this is the default
129 IDLog("initilizaing from Celeston GPS device...\n");
133 void CelestronGPS::ISGetProperties(const char *dev
)
136 if (dev
&& strcmp (mydev
, dev
))
140 IDDefSwitch (&PowerSw
, NULL
);
141 IDDefText (&Port
, NULL
);
144 IDDefNumber (&eqNum
, NULL
);
145 IDDefSwitch (&OnCoordSetSw
, NULL
);
146 IDDefSwitch (&abortSlewSw
, NULL
);
147 IDDefSwitch (&SlewModeSw
, NULL
);
150 IDDefSwitch (&MovementSw
, NULL
);
152 /* Send the basic data to the new client if the previous client(s) are already connected. */
153 if (PowerSw
.s
== IPS_OK
)
158 void CelestronGPS::ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
)
164 // ignore if not ours //
165 if (strcmp (dev
, mydev
))
168 if (!strcmp(name
, Port
.name
) )
172 tp
= IUFindText( &Port
, names
[0] );
176 tp
->text
= new char[strlen(texts
[0])+1];
177 strcpy(tp
->text
, texts
[0]);
178 IDSetText (&Port
, NULL
);
183 int CelestronGPS::handleCoordSet()
187 char RAStr
[32], DecStr
[32];
195 if (eqNum
.s
== IPS_BUSY
)
198 // sleep for 500 mseconds
202 if ((i
= SlewToCoords(targetRA
, targetDEC
)))
209 fs_sexa(RAStr
, targetRA
, 2, 3600);
210 fs_sexa(DecStr
, targetDEC
, 2, 3600);
211 IDSetNumber(&eqNum
, "Slewing to JNOW RA %s - DEC %s", RAStr
, DecStr
);
212 IDLog("Slewing to JNOW RA %s - DEC %s", RAStr
, DecStr
);
218 if (eqNum
.s
== IPS_BUSY
)
221 // sleep for 500 mseconds
225 if ( (fabs ( targetRA
- currentRA
) >= TRACKING_THRESHOLD
) ||
226 (fabs (targetDEC
- currentDEC
) >= TRACKING_THRESHOLD
))
229 IDLog("Exceeded Tracking threshold, will attempt to slew to the new target.\n");
230 IDLog("targetRA is %g, currentRA is %g\n", targetRA
, currentRA
);
231 IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC
, currentDEC
);
233 if (( i
= SlewToCoords(targetRA
, targetDEC
)))
239 fs_sexa(RAStr
, targetRA
, 2, 3600);
240 fs_sexa(DecStr
, targetDEC
, 2, 3600);
242 IDSetNumber(&eqNum
, "Slewing to JNow RA %s - DEC %s", RAStr
, DecStr
);
243 IDLog("Slewing to JNOW RA %s - DEC %s", RAStr
, DecStr
);
247 IDLog("Tracking called, but tracking threshold not reached yet.\n");
249 eqNum
.np
[0].value
= currentRA
;
250 eqNum
.np
[1].value
= currentDEC
;
252 IDSetNumber(&eqNum
, "Tracking...");
254 IDSetNumber(&eqNum
, NULL
);
262 OnCoordSetSw
.s
= IPS_OK
;
263 SyncToCoords(targetRA
, targetDEC
);
265 IDSetNumber(&eqNum
, "Synchronization successful.");
273 void CelestronGPS::ISNewNumber (const char *dev
, const char *name
, double values
[], char *names
[], int n
)
275 double newRA
=0, newDEC
=0;
277 // ignore if not ours //
278 if (strcmp (dev
, mydev
))
287 if (!strcmp (name
, eqNum
.name
))
291 if (checkPower(&eqNum
))
294 for (nset
= i
= 0; i
< n
; i
++)
296 INumber
*eqp
= IUFindNumber (&eqNum
, names
[i
]);
300 nset
+= newRA
>= 0 && newRA
<= 24.0;
301 } else if (eqp
== &eq
[1])
304 nset
+= newDEC
>= -90.0 && newDEC
<= 90.0;
310 //eqNum.s = IPS_BUSY;
318 IDLog("We recevined JNOW RA %f - DEC %f\n", newRA
, newDEC
);;
319 /*apparentCoord( (double) J2000, JD, &newRA, &newDEC);
320 IDLog("Processed to RA %f - DEC %f\n", newRA, newDEC);*/
322 //eqNum.np[0].value = values[0];
323 //eqNum.np[1].value = values[1];
327 if (MovementSw
.s
== IPS_BUSY
)
329 for (int i
=0; i
< 4; i
++)
332 MovementS
[i
].s
= ISS_OFF
;
335 MovementSw
.s
= IPS_IDLE
;
336 IDSetSwitch(&MovementSw
, NULL
);
339 if (handleCoordSet())
342 IDSetNumber(&eqNum
, NULL
);
348 IDSetNumber(&eqNum
, "RA or Dec missing or invalid.");
355 void CelestronGPS::ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
364 //IDLog("in new Switch with Device= %s and Property= %s and #%d items\n", dev, name,n);
365 //IDLog("SolarSw name is %s\n", SolarSw.name);
367 // ignore if not ours //
368 if (strcmp (dev
, mydev
))
371 // FIRST Switch ALWAYS for power
372 if (!strcmp (name
, PowerSw
.name
))
374 IUResetSwitches(&PowerSw
);
375 IUUpdateSwitches(&PowerSw
, states
, names
, n
);
380 if (!strcmp(name
, OnCoordSetSw
.name
))
382 if (checkPower(&OnCoordSetSw
))
385 IUResetSwitches(&OnCoordSetSw
);
386 IUUpdateSwitches(&OnCoordSetSw
, states
, names
, n
);
387 currentSet
= getOnSwitch(&OnCoordSetSw
);
391 if (!strcmp (name
, abortSlewSw
.name
))
393 if (checkPower(&abortSlewSw
))
395 abortSlewSw
.s
= IPS_IDLE
;
396 IDSetSwitch(&abortSlewSw
, NULL
);
400 IUResetSwitches(&abortSlewSw
);
403 if (eqNum
.s
== IPS_BUSY
)
405 abortSlewSw
.s
= IPS_OK
;
407 IDSetSwitch(&abortSlewSw
, "Slew aborted.");
408 IDSetNumber(&eqNum
, NULL
);
410 else if (MovementSw
.s
== IPS_BUSY
)
413 for (int i
=0; i
< 4; i
++)
416 MovementSw
.s
= IPS_IDLE
;
417 abortSlewSw
.s
= IPS_OK
;
419 IUResetSwitches(&MovementSw
);
420 IUResetSwitches(&abortSlewSw
);
421 IDSetSwitch(&abortSlewSw
, "Slew aborted.");
422 IDSetSwitch(&MovementSw
, NULL
);
423 IDSetNumber(&eqNum
, NULL
);
427 IUResetSwitches(&MovementSw
);
428 abortSlewSw
.s
= IPS_IDLE
;
429 IDSetSwitch(&abortSlewSw
, NULL
);
436 if (!strcmp (name
, SlewModeSw
.name
))
438 if (checkPower(&SlewModeSw
))
441 IUResetSwitches(&SlewModeSw
);
442 IUUpdateSwitches(&SlewModeSw
, states
, names
, n
);
443 index
= getOnSwitch(&SlewModeSw
);
446 SlewModeSw
.s
= IPS_OK
;
447 IDSetSwitch(&SlewModeSw
, NULL
);
452 if (!strcmp (name
, MovementSw
.name
))
454 if (checkPower(&MovementSw
))
458 IUUpdateSwitches(&MovementSw
, states
, names
, n
);
459 swp
= IUFindSwitch(&MovementSw
, names
[0]);
464 IUResetSwitches(&MovementSw
);
465 MovementSw
.s
= IPS_IDLE
;
466 IDSetSwitch(&MovementSw
, NULL
);
469 if (swp
== &MovementS
[0]) index
= 0;
470 else if (swp
== &MovementS
[1]) index
= 1;
471 else if (swp
== &MovementS
[2]) index
= 2;
474 lastMove
[index
] = lastMove
[index
] == 0 ? 1 : 0;
475 if (lastMove
[index
] == 0)
476 MovementS
[index
].s
= ISS_OFF
;
478 // North/South movement is illegal
479 if (lastMove
[NORTH
] && lastMove
[SOUTH
])
482 for (int i
=0; i
< 4; i
++)
485 IUResetSwitches(&MovementSw
);
486 MovementSw
.s
= IPS_IDLE
;
487 IDSetSwitch(&MovementSw
, "Slew aborted.");
491 // East/West movement is illegal
492 if (lastMove
[EAST
] && lastMove
[WEST
])
495 for (int i
=0; i
< 4; i
++)
498 IUResetSwitches(&MovementSw
);
499 MovementSw
.s
= IPS_IDLE
;
500 IDSetSwitch(&MovementSw
, "Slew aborted.");
504 //IDLog("We have switch %d \n ", index);
505 //IDLog("NORTH: %d -- WEST: %d -- EAST: %d -- SOUTH %d\n", lastMove[0], lastMove[1], lastMove[2], lastMove[3]);
507 if (lastMove
[index
] == 1)
512 if (!lastMove
[0] && !lastMove
[1] && !lastMove
[2] && !lastMove
[3])
513 MovementSw
.s
= IPS_IDLE
;
515 if (lastMove
[index
] == 0)
516 IDSetSwitch(&MovementSw
, "Moving toward %s aborted.", Direction
[index
]);
519 MovementSw
.s
= IPS_BUSY
;
520 IDSetSwitch(&MovementSw
, "Moving %s...", Direction
[index
]);
528 int CelestronGPS::getOnSwitch(ISwitchVectorProperty
*sp
)
530 for (int i
=0; i
< sp
->nsp
; i
++)
531 if (sp
->sp
[i
].s
== ISS_ON
)
538 int CelestronGPS::checkPower(ISwitchVectorProperty
*sp
)
540 if (PowerSw
.s
!= IPS_OK
)
542 if (!strcmp(sp
->label
, ""))
543 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", sp
->name
);
545 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", sp
->label
);
548 IDSetSwitch(sp
, NULL
);
555 int CelestronGPS::checkPower(INumberVectorProperty
*np
)
557 if (PowerSw
.s
!= IPS_OK
)
559 if (!strcmp(np
->label
, ""))
560 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", np
->name
);
562 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", np
->label
);
565 IDSetNumber(np
, NULL
);
571 int CelestronGPS::checkPower(ITextVectorProperty
*tp
)
574 if (PowerSw
.s
!= IPS_OK
)
576 if (!strcmp(tp
->label
, ""))
577 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", tp
->name
);
579 IDMessage (mydev
, "Cannot change property %s while the telescope is offline.", tp
->label
);
590 void CelestronGPS::ISPoll()
593 double currentRA
, currentDEC
;
599 if (PowerSw
.s
!= IPS_OK
)
602 currentDEC
= GetDec();
604 if ( fabs (currentRA
- lastRA
) > 0.01 || fabs (currentDEC
- lastDEC
) > 0.01)
606 eqNum
.np
[0].value
= currentRA
;
607 eqNum
.np
[1].value
= currentDEC
;
609 lastDEC
= currentDEC
;
610 IDSetNumber (&eqNum
, NULL
);
617 currentDEC
= GetDec();
618 dx
= targetRA
- currentRA
;
619 dy
= targetDEC
- currentDEC
;
621 IDLog("targetRA is %f, currentRA is %f\n", (float) targetRA
, (float) currentRA
);
622 IDLog("targetDEC is %f, currentDEC is %f\n****************************\n", (float) targetDEC
, (float) currentDEC
);
624 eqNum
.np
[0].value
= currentRA
;
625 eqNum
.np
[1].value
= currentDEC
;
627 status
= CheckCoords(targetRA
, targetDEC
);
629 // Wait until acknowledged or within 3.6', change as desired.
632 case 0: /* goto in progress */
633 IDSetNumber (&eqNum
, NULL
);
635 case 1: /* goto complete within tolerance */
636 case 2: /* goto complete but outside tolerance */
637 currentRA
= targetRA
;
638 currentDEC
= targetDEC
;
640 /*apparentCoord( JD, (double) J2000, ¤tRA, ¤tDEC);*/
642 eqNum
.np
[0].value
= currentRA
;
643 eqNum
.np
[1].value
= currentDEC
;
649 IUResetSwitches(&OnCoordSetSw
);
650 OnCoordSetSw
.sp
[0].s
= ISS_ON
;
651 IDSetNumber (&eqNum
, "Slew is complete");
655 IUResetSwitches(&OnCoordSetSw
);
656 OnCoordSetSw
.sp
[1].s
= ISS_ON
;
657 IDSetNumber (&eqNum
, "Slew is complete. Tracking...");
660 IDSetSwitch (&OnCoordSetSw
, NULL
);
666 if (PowerSw
.s
!= IPS_OK
)
669 currentDEC
= GetDec();
671 if ( fabs (currentRA
- lastRA
) > 0.01 || fabs (currentDEC
- lastDEC
) > 0.01)
674 eqNum
.np
[0].value
= currentRA
;
675 eqNum
.np
[1].value
= currentDEC
;
677 lastDEC
= currentDEC
;
678 IDSetNumber (&eqNum
, NULL
);
688 switch (MovementSw
.s
)
694 currentDEC
= GetDec();
696 /*apparentCoord( JD, (double) J2000, ¤tRA, ¤tDEC);*/
699 eqNum
.np
[0].value
= currentRA
;
700 eqNum
.np
[1].value
= currentDEC
;
702 IDSetNumber (&eqNum
, NULL
);
713 void CelestronGPS::getBasicData()
717 targetDEC
= GetDec();
719 eqNum
.np
[0].value
= targetRA
;
720 eqNum
.np
[1].value
= targetDEC
;
722 IDSetNumber(&eqNum
, NULL
);
726 void CelestronGPS::powerTelescope()
729 switch (PowerSw
.sp
[0].s
)
733 if (ConnectTel(Port
.tp
[0].text
) < 0)
735 PowerS
[0].s
= ISS_OFF
;
736 PowerS
[1].s
= ISS_ON
;
737 IDSetSwitch (&PowerSw
, "Error connecting to port %s", Port
.tp
[0].text
);
742 IDSetSwitch (&PowerSw
, "Telescope is online. Retrieving basic data...");
747 IDSetSwitch (&PowerSw
, "Telescope is offline.");
748 IDLog("Telescope is offline.");
755 void CelestronGPS::slewError(int slewCode
)
762 IDSetNumber (&eqNum
, "Invalid newDec in SlewToCoords");
765 IDSetNumber (&eqNum
, "RA count overflow in SlewToCoords");
768 IDSetNumber (&eqNum
, "Dec count overflow in SlewToCoords");
771 IDSetNumber (&eqNum
, "No acknowledgement from telescope after SlewToCoords");
774 IDSetNumber (&eqNum
, "Unknown error");