3 Copyright (C
) 2004 Francois
Meyer (dulle @ free
.fr
)
4 Remi Petitdemange
for the temma protocol
5 Reference site is http
://dulle.free.fr/alidade/indi.php
7 This library is free software
; you can redistribute it
and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation
; either
10 version
2.1 of the License
, or (at your option
) any later version
.
12 This library is distributed in the hope that it will be useful
,
13 but WITHOUT ANY WARRANTY
; without even the implied warranty of
14 MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE
. See the GNU
15 Lesser General Public License
for more details
.
17 You should have received a copy of the GNU Lesser General Public
18 License along with
this library
; if not, write to the Free Software
19 Foundation
, Inc
., 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
35 #include "indidevapi.h"
36 #include "eventloop.h"
38 #include "temmadriver.h"
41 char errormes
[][128]={
43 "Error reading from io port",
44 "Error writing to io port",
45 "Unrecognized message"
50 int read_ret
, write_ret
;
51 unsigned char buffer
[256];
52 unsigned char buffer2
[256];
56 /* Initlization routine */
57 static void mountInit()
59 static int inited
; /* set once mountInit is called */
64 /* setting default comm port */
65 PortT
->text
= realloc(PortT
->text
, 10);
66 TemmaNoteT
[0].text
= realloc( TemmaNoteT
[0].text
, 64);
67 TemmaNoteT
[1].text
= realloc( TemmaNoteT
[1].text
, 64);
69 if (!PortT
->text
|| !TemmaNoteT
[0].text
|| !TemmaNoteT
[1].text
){
70 fprintf(stderr
,"Memory allocation error");
73 strcpy(PortT
->text
, "/dev/ttyS0");
74 strcpy(TemmaNoteT
[0].text
, "Experimental Driver");
75 strcpy(TemmaNoteT
[1].text
, "http://dulle.free.fr/alidade/indi.php");
81 void ISGetProperties (const char *dev
) {
82 if (dev
&& strcmp (mydev
, dev
))
87 IDDefSwitch (&powSw
, NULL
);
88 IDDefNumber (&eqTemma
, NULL
);
89 IDDefNumber (&eqNum
, NULL
);
90 IDDefSwitch (&OnCoordSetSw
, NULL
);
91 IDDefSwitch (&abortSlewSw
, NULL
);
92 IDDefText (&TemmaNoteTP
, NULL
);
93 IDDefSwitch (&RAmotorSw
, NULL
);
94 IDDefSwitch (&trackmodeSw
, NULL
);
95 IDDefText (&Port
, NULL
);
96 IDDefText (&TemmaVersion
, NULL
);
97 IDDefNumber (&Time
, NULL
);
98 IDDefNumber (&SDTime
, NULL
);
99 IDDefNumber (&cometNum
, NULL
);
100 IDDefNumber (&geoNum
, NULL
);
103 void ISNewBLOB (const char *dev
, const char *name
, int sizes
[], char *blobs
[], char *formats
[], char *names
[], int n
)
105 dev
=dev
;name
=name
;sizes
=sizes
;blobs
=blobs
;formats
=formats
;names
=names
;n
=n
;
108 void ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
) {
111 if (strcmp (dev
, mydev
))
114 if (!strcmp (name
, Port
.name
)) {
115 IUSaveText(PortT
, texts
[0]);
117 IDSetText (&Port
, NULL
);
122 /* client is sending us a new value for a Numeric vector property */
123 void ISNewNumber (const char *dev
, const char *name
, double values
[], char *names
[], int n
) {
124 /* ignore if not ours */
125 if (strcmp (dev
, mydev
))
128 if (!strcmp (name
, eqNum
.name
)) {
129 /* new equatorial target coords */
130 /*double newra = 0, newdec = 0;*/
133 /* Check power, if it is off, then return */
134 if (power
[0].s
!= ISS_ON
)
137 IDSetNumber(&eqNum
, "Power is off");
141 for (nset
= i
= 0; i
< n
; i
++) {
142 /* Find numbers with the passed names in the eqNum property */
143 INumber
*eqp
= IUFindNumber (&eqNum
, names
[i
]);
145 /* If the number found is Right ascension (eq[0]) then process it */
148 currentRA
= (values
[i
]);
149 nset
+= currentRA
>= 0 && currentRA
<= 24;
151 /* Otherwise, if the number found is Declination (eq[1]) then process it */
152 else if (eqp
== &eq
[1]) {
153 currentDec
= (values
[i
]);
154 nset
+= currentDec
>= -90 && currentDec
<= 90;
158 /* Did we process the two numbers? */
160 /*char r[32], d[32];*/
162 /* Set the mount state to BUSY */
165 if (SLEWSW
==ISS_ON
|| TRACKSW
==ISS_ON
){
166 IDSetNumber(&eqNum
, "Moving to RA Dec %f %f", currentRA
,
172 IDSetNumber(&eqNum
, "Syncing to RA Dec %f %f", currentRA
, currentDec
);
173 set_TemmaCurrentpos();
176 IDSetNumber(&eqNum
, "Synced");
178 /* We didn't process the two number correctly, report an error */
180 /* Set property state to idle */
183 IDSetNumber(&eqNum
, "RA or Dec absent or bogus.");
189 /* client is sending us a new value for a Switch property */
190 void ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
194 /* ignore if not ours */
195 if (strcmp (dev
, mydev
))
198 if (!strcmp (name
, powSw
.name
)) {
199 sp
= IUFindSwitch (&powSw
, names
[0]);
203 fprintf(stderr
,"new state %s\n",names
[0]);
206 if (!strcmp(names
[0],"CONNECT")) {
209 if (!strcmp(names
[0],"DISCONNECT")) {
214 if (!strcmp (name
, abortSlewSw
.name
)) {
217 IDSetSwitch (&abortSlewSw
, "Abort slew");
220 IDSetSwitch (&abortSlewSw
, "Power is off");
224 if (!strcmp (name
, RAmotorSw
.name
)) {
226 sp
= IUFindSwitch (&RAmotorSw
, names
[0]);
230 fprintf(stderr
,"new state %s\n",names
[0]);
234 RAmotorSw
.s
= IPS_BUSY
;
235 if (!strcmp(names
[0],"RUN")) {
236 set_TemmaStandbyState(0);
239 if (!strcmp(names
[0],"STOP")) {
240 set_TemmaStandbyState(1);
243 if(get_TemmaStandbyState(buffer
)){
244 RAmotorSw
.s
= IPS_IDLE
;
245 IDSetSwitch (&RAmotorSw
, "Error writing to port");
250 IDSetSwitch (&RAmotorSw
, "Power is off");
254 if (!strcmp (name
, OnCoordSetSw
.name
)) {
256 IUResetSwitches(&OnCoordSetSw
);
257 sp
= IUFindSwitch (&OnCoordSetSw
, names
[0]);
261 fprintf(stderr
,"new state %s\n",names
[0]);
265 IUResetSwitches(&OnCoordSetSw
);
266 IUUpdateSwitches(&OnCoordSetSw
, states
, names
, n
);
267 /* currentSet = getOnSwitch(&OnCoordSetSw); */
268 OnCoordSetSw
.s
= IPS_OK
;
269 IDSetSwitch(&OnCoordSetSw
, NULL
);
272 IDSetSwitch (&OnCoordSetSw
, "Power is off");
277 double calcLST(char *strlst
){
285 gmtime_r(&computertime
, &gmt
);
288 currentUTC
=(double)gmt
.tm_hour
+(double)gmt
.tm_min
/60+(double)gmt
.tm_sec
/3600;
293 lst
=(gmst
-longitude
)/15;
295 lst
=(lst
/24-(int)(lst
/24))*24;
299 sprintf(strlst
,"%.2d%.2d%.2d", (int)lst
,
301 ((int)(lst
*3600))%60);
303 IDSetNumber (&SDTime
, NULL
);
304 IDSetNumber (&Time
, NULL
);
310 /* update the mount over time */
311 void readMountcurrentpos (void *p
)
316 get_TemmaCurrentpos(result
);
319 /* This is a temporary workaround to allow
320 clients with only one eq property to work */
325 IEAddTimer (POLLMS
, readMountcurrentpos
, NULL
);
330 static void connectMount () {
331 fprintf(stderr
,"opening mount port %s\n",PortT
->text
);
332 if (power
[0].s
== ISS_ON
){
333 if (Port
.s
!= IPS_OK
){
335 power
[0].s
= ISS_OFF
;
337 IDSetSwitch (&powSw
, "Port not set.");
341 if (TemmaConnect(PortT
->text
)==0){
342 IDSetText (&Port
, "Port is opened.");
343 if( !get_TemmaVERSION(buffer
)){
346 power
[1].s
= ISS_OFF
;
347 snprintf(buffer2
,sizeof( buffer2
),"%s",buffer
+4);
349 IDSetText(&TemmaVersion
, "Temma version set");
350 TemmaVersionT
->text
= realloc(TemmaVersionT
->text
, strlen(buffer2
)+1);
351 if (!TemmaVersionT
->text
){
352 fprintf(stderr
,"Memory allocation error");
355 IDSetSwitch (&powSw
, "Mount is ready");
356 IDSetSwitch (&powSw
, VERSION
);
358 strcpy(TemmaVersionT
->text
, buffer2
);
359 TemmaVersion
.s
= IPS_OK
;
360 IDSetText (&TemmaVersion
, NULL
);
361 IDLog("%s", buffer2
);
362 /* start timer to read mount coords */
363 IEAddTimer (POLLMS
, readMountcurrentpos
, NULL
);
367 power
[0].s
= ISS_OFF
;
369 IDSetText(&Port
, "Com error");
370 IDSetSwitch (&powSw
, "Port not set.");
373 if(get_TemmaStandbyState(answer
)){
374 IDSetSwitch (&RAmotorSw
, "Error writing to port");
380 power
[0].s
= ISS_OFF
;
383 IDSetSwitch (&powSw
, "Failed to open port.");
389 static void disconnectMount () {
390 fprintf(stderr
,"closing mount port %s\n",PortT
->text
);
391 if (power
[1].s
== ISS_ON
){
392 if (Port
.s
!= IPS_OK
){
394 power
[0].s
= ISS_OFF
;
396 IDSetSwitch (&powSw
, "Port not set.");
402 power
[0].s
= ISS_OFF
;
404 IDSetSwitch (&powSw
, "Port is closed.");
408 fprintf(stderr
, "Already disconnected \n");
412 int TemmaConnect(const char *device
) {
413 fprintf(stderr
, "Connecting to device %s\n", device
);
415 if (openPort(device
) < 0){
416 fprintf(stderr
, "Error connecting to device %s\n", device
);
424 int TemmaDisconnect() {
425 fprintf(stderr
, "Disconnected.\n");
431 int set_CometTracking(int RArate
, int DECrate
){
435 RA
: Adjust Sidereal time by seconds per Day
437 DEC
: Adjust DEC tracking by Minutes Per Day
438 valeur DEC min
=-600 /max
=+600 (+/-10 deg
/ jour
)
441 LM
+120,+30 would slow the RA speed by
86164/86284 and
442 the Dec would track at
30 Minutes a day
.
443 To stop tracking either send a LM0
,0 (or a PS
444 sauf erreur on constate en faite l inverse en RA
445 retour Vsideral
=> LM0
,0 ou LM
+0,+0
447 char local_buffer
[16];
465 snprintf(local_buffer
, sizeof( local_buffer
), "%+6d,%+5d", RArate
, DECrate
);
466 set_TemmaCometTracking(local_buffer
);
471 int TemmaabortSlew() {
472 if (portWrite("PS") < 0)
485 set_TemmaLST(buffer
);
487 dec
=fabs(currentDec
);
495 snprintf(buffer
, sizeof(buffer
),"%.2d%.2d%.2d%c%.2d%.2d%.1d",
496 (int)currentRA
,(int)(currentRA
*(double)60)%60,((int)(currentRA
*(double)6000))%100,sign
,
497 (int)dec
,(int)(dec
*(double)60)%60,((int)(dec
*(double)600))%10);
498 fprintf(stderr
,"Goto %s\n", buffer
);
500 snprintf(command
,14,"P%s",buffer
);
502 fprintf(stderr
,"Goto command:%s\n", command
);
505 portRead(buffer
,-1,TEMMA_TIMEOUT
);
513 int extractRA(char *buf
){
521 temmaRA
=((double)h
+((double)m
+ (double)s
/60)/60);
522 IDSetNumber (&eqTemma
, NULL
);
523 /* fprintf(stderr,"extractRA: %s %d %d %d %d %lf\n",buf,r,h,m,s,dra);*/
527 int extractDEC(char *buf
){
535 temmaDec
=((double)d
+((double)m
+ (double)s
/60)/60);
539 IDSetNumber (&eqTemma
, NULL
);
542 /* fprintf(stderr,"extractDEC: %s %d %d %d %d %lf\n",buf,dec,d,m,s,ddec);*/
547 int get_TemmaCurrentpos(char *local_buffer
){
550 if (portWrite("E") < 0)
553 if(portRead(local_buffer
,-1,TEMMA_TIMEOUT
)==SUCCESS
){
554 if(strstr(local_buffer
, "E")==local_buffer
){
555 strncpy(buf
,local_buffer
+1,6);
559 strncpy(buf
,local_buffer
+7,6);
572 int set_TemmaCurrentpos(void) {
583 dec
=fabs(currentDec
);
591 sprintf(buffer
,"%.2d%.2d%.2d%c%.2d%.2d%.1d",
592 (int)currentRA
,(int)(currentRA
*(double)60)%60,((int)(currentRA
*(double)6000))%100,sign
,
593 (int)dec
,(int)(dec
*(double)60)%60,((int)(dec
*(double)600))%10);
594 fprintf(stderr
,"sync to %s %f %f\n", buffer
,currentRA
,dec
);
596 snprintf(buf
, sizeof(buf
), "D%s",buffer
);
601 portRead(buffer
,-1,TEMMA_TIMEOUT
);
610 int do_TemmaSLEW(char mode
){
611 /*char command[16];*/
613 sprintf(buffer
,"M%c",mode
); /* see bit definition in Temmadriver.h */
615 if (portWrite(buffer
) < 0)
621 int get_TemmaVERSION(char *local_buffer
){
624 if ((err
=portWrite("v")) < 0){
628 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
629 if(strstr(local_buffer
, "ver")==local_buffer
){
636 int get_TemmaGOTOstatus(char *local_buffer
){
642 if (portWrite("s") < 0)
645 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
646 if(strstr(local_buffer
, "s")==local_buffer
){
653 int get_TemmaBOTHcorrspeed(char *local_buffer
){
654 if (portWrite("lg") < 0)
656 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
657 if(strstr(local_buffer
, "lg")==local_buffer
){
664 int get_TemmaDECcorrspeed(char *local_buffer
){
665 if (portWrite("lb") < 0)
667 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
668 if(strstr(local_buffer
, "lb")==local_buffer
){
675 int set_TemmaDECcorrspeed(char *local_buffer
){
678 snprintf(command
, 4, "LB%s",local_buffer
);
680 if (portWrite(command
) < 0)
685 int get_TemmaRAcorrspeed(char *local_buffer
){
686 if (portWrite("la") < 0)
689 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
690 if(strstr(local_buffer
, "la")==local_buffer
){
697 int set_TemmaRAcorrspeed(char *local_buffer
){
700 snprintf(command
, 4,"LA%s",local_buffer
);
702 if (portWrite(command
) < 0)
707 int get_TemmaLatitude(char *local_buffer
){
708 if (portWrite("i") < 0)
710 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
711 if(local_buffer
[0]=='i'){
718 int set_TemmaLatitude(char *local_buffer
){
731 sprintf(command
,"I%c%.2d%.2d%.1d", sign
,
733 (int)(lat
*(double)60)%60,
734 ((int)(lat
*(double)600))%10);
736 if (portWrite(command
) < 0)
741 int get_TemmaLST(char *local_buffer
){
742 if (portWrite("g") < 0)
745 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
746 if(local_buffer
[0]=='g'){
754 int set_TemmaLST(char *local_buffer
){
756 snprintf(command
,7,"T%s",local_buffer
);
758 if (portWrite(command
) < 0)
764 int get_TemmaCometTracking(char *local_buffer
){
765 if (portWrite("lm") < 0)
767 portRead(local_buffer
,-1,TEMMA_TIMEOUT
);
768 if(strstr(local_buffer
, "lm")==local_buffer
){
775 int set_TemmaStandbyState(int on
){
777 return portWrite("STN-ON");
780 return portWrite("STN-OFF");
785 int get_TemmaStandbyState(unsigned char *local_buffer
){
789 if ((nb
=portWrite("STN-COD")) < 0){
790 IDSetSwitch (&RAmotorSw
, "I/O error when asking RAmotor status");
794 if((status
=portRead(local_buffer
,-1,TEMMA_TIMEOUT
)==SUCCESS
)){
795 if(strstr(local_buffer
, "stn")==local_buffer
){
797 if (strstr(local_buffer
,"on")){ /* stanby on */
798 RAmotorSw
.s
= IPS_OK
;
799 RAmotor
[0].s
= ISS_OFF
;
800 RAmotor
[1].s
= ISS_ON
;
801 IDSetSwitch (&RAmotorSw
, "RA motor is off.");
804 if (strstr(local_buffer
,"off")){ /* stanby off */
805 RAmotorSw
.s
= IPS_OK
;
806 RAmotor
[0].s
= ISS_ON
;
807 RAmotor
[1].s
= ISS_OFF
;
808 IDSetSwitch (&RAmotorSw
, "RA motor is on.");
811 RAmotorSw
.s
= IPS_OK
;
812 IDSetSwitch (&RAmotorSw
, "I/O error when getting RAmotor status");
818 if (status
<=ETIMEOUT
&& status
>=ECOMMAND
){
819 IDSetSwitch(&RAmotorSw
, "%s", errormes
[ETIMEOUT
- status
]);
825 int set_TemmaCometTracking(char *local_buffer
){
828 snprintf(command
,15,"LM%s",local_buffer
);
829 if (portWrite(command
) < 0)
834 int set_TemmaSolarRate(void){
835 if (portWrite("LK") < 0)
840 int set_TemmaStellarRate(void){
841 if (portWrite("LL") < 0)
847 int switch_Temmamountside(void){
848 if (portWrite("PT") < 0)
853 /**********************************************************************
855 **********************************************************************/
857 int openPort(const char *portID
) {
858 struct termios ttyOptions
;
860 if ( (fd
= open(portID
, O_RDWR
)) == -1)
862 memset(&ttyOptions
, 0, sizeof(ttyOptions
));
863 tcgetattr(fd
, &ttyOptions
);
865 /* 8 bit, enable read */
866 ttyOptions
.c_cflag
|= CS8
;
868 ttyOptions
.c_cflag
|= PARENB
;
869 ttyOptions
.c_cflag
&= ~PARODD
;
870 ttyOptions
.c_cflag
&= ~CSTOPB
;
871 ttyOptions
.c_cflag
|= CRTSCTS
;
874 cfsetispeed(&ttyOptions
, B19200
);
875 cfsetospeed(&ttyOptions
, B19200
);
877 /* set input/output flags */
878 ttyOptions
.c_iflag
= IGNBRK
;
880 /* Read at least one byte */
881 ttyOptions
.c_cc
[VMIN
] = 1;
882 ttyOptions
.c_cc
[VTIME
] = 5;
885 ttyOptions
.c_lflag
= 0;
886 ttyOptions
.c_oflag
= 0;
889 tcsetattr(fd
, TCSANOW
, &ttyOptions
);
891 /* flush the channel */
892 tcflush(fd
, TCIOFLUSH
);
896 int portWrite(char * buf
) {
897 int nbytes
=strlen(buf
); /*, totalBytesWritten;*/
898 int bytesWritten
= 0;
901 bytesWritten
= write(fd
, buf
, nbytes
);
902 bytesWritten
+= write(fd
, "\r\n", 2);
903 /*fprintf(stderr,"portwrite :%d octets %s\n", bytesWritten, buf);*/
905 if (bytesWritten
!=nbytes
+2){
906 perror("write error: ");
907 IDLog("Error writing to port");
910 return (bytesWritten
);
913 int portRead(char *buf
, int nbytes
, int timeout
) {
915 A very basic finite state machine monitors
917 state 0 : read regular bytes
918 state 1 : just read a \n, waiting for a \r
919 state 2 : read a \n and a \r, command is over.
921 Not sure it is useful here but I use a more
922 sophisticated version of this with a GPS receiver
923 and it is robust and reliable
925 We return a null terminated string.
928 int bytesRead
= 0, state
=0, /*i=0,*/ current
=0;
929 /*int totalBytesRead = 0;*/
932 if ( (err
= TemmareadOut(timeout
)) ){
935 IDLog("Error: timeout while reading");
940 while (read(fd
,buf
+bytesRead
,1)==1){
941 /* fprintf(stderr,"%c",buf[bytesRead]); */
945 if(buf
[bytesRead
]==13)
950 if(buf
[bytesRead
]==10)
953 if(buf
[bytesRead
]==13)
974 int TemmareadOut(int timeout
) {
980 FD_SET(fd
, &readout
);
982 /* wait for 'timeout' seconds */
986 /* Wait till we have a change in the fd status */
987 retval
= select (fd
+1, &readout
, NULL
, NULL
, &tv
);
989 /* Return 0 on successful fd change */
992 /* Return -1 due to an error */
993 else if (retval
== EREAD
)
995 /* Return -2 if time expires before anything interesting happens */