#if 0 Temma INDI driver Copyright (C) 2004 Francois Meyer (dulle @ free.fr) Remi Petitdemange for the temma protocol Reference site is http://dulle.free.fr/alidade/indi.php This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #endif #include #include #include #include #include #include #include #include #include #include #include "indicom.h" #include "indidevapi.h" #include "eventloop.h" #include "indicom.h" #include "temmadriver.h" char errormes[][128]={ "I/O Timeout", "Error reading from io port", "Error writing to io port", "Unrecognized message" }; char answer[32]; int fd; int read_ret, write_ret; unsigned char buffer[256]; unsigned char buffer2[256]; #if 1 /* Initlization routine */ static void mountInit() { static int inited; /* set once mountInit is called */ if (inited) return; /* setting default comm port */ PortT->text = realloc(PortT->text, 10); TemmaNoteT[0].text = realloc( TemmaNoteT[0].text, 64); TemmaNoteT[1].text = realloc( TemmaNoteT[1].text, 64); if (!PortT->text || !TemmaNoteT[0].text || !TemmaNoteT[1].text){ fprintf(stderr,"Memory allocation error"); return; } strcpy(PortT->text, "/dev/ttyS0"); strcpy(TemmaNoteT[0].text, "Experimental Driver"); strcpy(TemmaNoteT[1].text, "http://dulle.free.fr/alidade/indi.php"); inited = 1; } #endif void ISGetProperties (const char *dev) { if (dev && strcmp (mydev, dev)) return; mountInit(); IDDefSwitch (&powSw, NULL); IDDefNumber (&eqTemma, NULL); IDDefNumber (&eqNum, NULL); IDDefSwitch (&OnCoordSetSw, NULL); IDDefSwitch (&abortSlewSw, NULL); IDDefText (&TemmaNoteTP, NULL); IDDefSwitch (&RAmotorSw, NULL); IDDefSwitch (&trackmodeSw, NULL); IDDefText (&Port, NULL); IDDefText (&TemmaVersion, NULL); IDDefNumber (&Time, NULL); IDDefNumber (&SDTime, NULL); IDDefNumber (&cometNum, NULL); IDDefNumber (&geoNum, NULL); } void ISNewBLOB (const char *dev, const char *name, int sizes[], char *blobs[], char *formats[], char *names[], int n) { dev=dev;name=name;sizes=sizes;blobs=blobs;formats=formats;names=names;n=n; } void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n) { /*IText *tp;*/ if (strcmp (dev, mydev)) return; if (!strcmp (name, Port.name)) { IUSaveText(PortT, texts[0]); Port.s = IPS_OK; IDSetText (&Port, NULL); } return; } /* client is sending us a new value for a Numeric vector property */ void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) { /* ignore if not ours */ if (strcmp (dev, mydev)) return; if (!strcmp (name, eqNum.name)) { /* new equatorial target coords */ /*double newra = 0, newdec = 0;*/ int i, nset; /* Check power, if it is off, then return */ if (power[0].s != ISS_ON) { eqNum.s = IPS_IDLE; IDSetNumber(&eqNum, "Power is off"); return; } for (nset = i = 0; i < n; i++) { /* Find numbers with the passed names in the eqNum property */ INumber *eqp = IUFindNumber (&eqNum, names[i]); /* If the number found is Right ascension (eq[0]) then process it */ if (eqp == &eq[0]) { currentRA = (values[i]); nset += currentRA >= 0 && currentRA <= 24; } /* Otherwise, if the number found is Declination (eq[1]) then process it */ else if (eqp == &eq[1]) { currentDec = (values[i]); nset += currentDec >= -90 && currentDec <= 90; } } /* end for */ /* Did we process the two numbers? */ if (nset == 2) { /*char r[32], d[32];*/ /* Set the mount state to BUSY */ eqNum.s = IPS_BUSY; if (SLEWSW==ISS_ON || TRACKSW==ISS_ON){ IDSetNumber(&eqNum, "Moving to RA Dec %f %f", currentRA, currentDec); do_TemmaGOTO(); } if (SYNCSW==ISS_ON){ IDSetNumber(&eqNum, "Syncing to RA Dec %f %f", currentRA, currentDec); set_TemmaCurrentpos(); } eqNum.s = IPS_OK; IDSetNumber(&eqNum, "Synced"); } /* We didn't process the two number correctly, report an error */ else { /* Set property state to idle */ eqNum.s = IPS_IDLE; IDSetNumber(&eqNum, "RA or Dec absent or bogus."); } return; } } /* client is sending us a new value for a Switch property */ void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) { ISwitch *sp; /* ignore if not ours */ if (strcmp (dev, mydev)) return; if (!strcmp (name, powSw.name)) { sp = IUFindSwitch (&powSw, names[0]); if (!sp) return; fprintf(stderr,"new state %s\n",names[0]); sp->s = states[0]; if (!strcmp(names[0],"CONNECT")) { connectMount(); } if (!strcmp(names[0],"DISCONNECT")) { disconnectMount(); } } if (!strcmp (name, abortSlewSw.name)) { if (POWSW){ TemmaabortSlew(); IDSetSwitch (&abortSlewSw, "Abort slew"); } else { IDSetSwitch (&abortSlewSw, "Power is off"); } } if (!strcmp (name, RAmotorSw.name)) { if (POWSW){ sp = IUFindSwitch (&RAmotorSw, names[0]); if (!sp) return; fprintf(stderr,"new state %s\n",names[0]); sp->s = states[0]; RAmotorSw.s = IPS_BUSY; if (!strcmp(names[0],"RUN")) { set_TemmaStandbyState(0); } if (!strcmp(names[0],"STOP")) { set_TemmaStandbyState(1); } if(get_TemmaStandbyState(buffer)){ RAmotorSw.s = IPS_IDLE; IDSetSwitch (&RAmotorSw, "Error writing to port"); return; } } else { IDSetSwitch (&RAmotorSw, "Power is off"); } } if (!strcmp (name, OnCoordSetSw.name)) { if (POWSW){ IUResetSwitches(&OnCoordSetSw); sp = IUFindSwitch (&OnCoordSetSw, names[0]); if (!sp) return; fprintf(stderr,"new state %s\n",names[0]); sp->s = states[0]; IUResetSwitches(&OnCoordSetSw); IUUpdateSwitches(&OnCoordSetSw, states, names, n); /* currentSet = getOnSwitch(&OnCoordSetSw); */ OnCoordSetSw.s = IPS_OK; IDSetSwitch(&OnCoordSetSw, NULL); } else { IDSetSwitch (&OnCoordSetSw, "Power is off"); } } } double calcLST(char *strlst){ time_t computertime; struct tm gmt; double jd, gmst; double lst; /*int a,b;*/ time(&computertime); gmtime_r(&computertime, &gmt); gmt.tm_mon+=1; gmt.tm_year+=1900; currentUTC=(double)gmt.tm_hour+(double)gmt.tm_min/60+(double)gmt.tm_sec/3600; jd=UTtoJD(&gmt); gmst=JDtoGMST(jd); lst=(gmst-longitude)/15; lst=(lst/24-(int)(lst/24))*24; if(lst>=24) lst-=24; sprintf(strlst,"%.2d%.2d%.2d", (int)lst, ((int)(lst*60))%60, ((int)(lst*3600))%60); currentLST=lst; IDSetNumber (&SDTime, NULL); IDSetNumber (&Time, NULL); return 0; } #if 1 /* update the mount over time */ void readMountcurrentpos (void *p) { char result[32]; if(POWSW){ get_TemmaCurrentpos(result); calcLST(result); /* This is a temporary workaround to allow clients with only one eq property to work */ currentRA=temmaRA; currentDec=temmaDec; /* again */ IEAddTimer (POLLMS, readMountcurrentpos, NULL); } } #endif static void connectMount () { fprintf(stderr,"opening mount port %s\n",PortT->text); if (power[0].s == ISS_ON){ if (Port.s != IPS_OK){ powSw.s = IPS_IDLE; power[0].s = ISS_OFF; IDSetSwitch (&powSw, "Port not set."); return; } else { if (TemmaConnect(PortT->text)==0){ IDSetText (&Port, "Port is opened."); if( !get_TemmaVERSION(buffer)){ powSw.s = IPS_OK; power[0].s = ISS_ON; power[1].s = ISS_OFF; snprintf(buffer2,sizeof( buffer2 ),"%s",buffer+4); IDSetText(&TemmaVersion , "Temma version set"); TemmaVersionT->text = realloc(TemmaVersionT->text, strlen(buffer2)+1); if (!TemmaVersionT->text){ fprintf(stderr,"Memory allocation error"); return; } IDSetSwitch (&powSw, "Mount is ready"); IDSetSwitch (&powSw, VERSION); strcpy(TemmaVersionT->text, buffer2); TemmaVersion.s = IPS_OK; IDSetText (&TemmaVersion, NULL); IDLog("%s", buffer2); /* start timer to read mount coords */ IEAddTimer (POLLMS, readMountcurrentpos, NULL); } else { powSw.s = IPS_IDLE; power[0].s = ISS_OFF; power[1].s = ISS_ON; IDSetText(&Port , "Com error"); IDSetSwitch (&powSw, "Port not set."); } if(get_TemmaStandbyState(answer)){ IDSetSwitch (&RAmotorSw, "Error writing to port"); return; } } else { powSw.s = IPS_IDLE; power[0].s = ISS_OFF; power[1].s = ISS_ON; IDSetSwitch (&powSw, "Failed to open port."); } } } } static void disconnectMount () { fprintf(stderr,"closing mount port %s\n",PortT->text); if (power[1].s == ISS_ON){ if (Port.s != IPS_OK){ powSw.s = IPS_IDLE; power[0].s = ISS_OFF; IDSetSwitch (&powSw, "Port not set."); return; } else { TemmaDisconnect(); powSw.s = IPS_IDLE; power[0].s = ISS_OFF; IDSetSwitch (&powSw, "Port is closed."); } } else { fprintf(stderr, "Already disconnected \n"); } } int TemmaConnect(const char *device) { fprintf(stderr, "Connecting to device %s\n", device); if (openPort(device) < 0){ fprintf(stderr, "Error connecting to device %s\n", device); return -1; } else{ return 0; } } int TemmaDisconnect() { fprintf(stderr, "Disconnected.\n"); close(fd); return 0; } int set_CometTracking(int RArate, int DECrate){ #if 0 Set Comet Tracking LM+/-99999,+/-9999 RA : Adjust Sidereal time by seconds per Day DEC : Adjust DEC tracking by Minutes Per Day valeur DEC min=-600 /max=+600 (+/-10 deg / jour) Example: LM+120,+30 would slow the RA speed by 86164/86284 and the Dec would track at 30 Minutes a day. To stop tracking either send a LM0,0 (or a PS sauf erreur on constate en faite l inverse en RA retour Vsideral => LM0,0 ou LM+0,+0 #endif char local_buffer[16]; if (RArate<-21541){ RArate=-21541; } if (RArate>21541){ RArate=21541; } if (DECrate<-600){ DECrate=-600; } if (DECrate>600){ DECrate=600; } snprintf(local_buffer, sizeof( local_buffer ), "%+6d,%+5d", RArate, DECrate); set_TemmaCometTracking(local_buffer); return 0; } int TemmaabortSlew() { if (portWrite("PS") < 0) return -1; return 0; } int do_TemmaGOTO() { /* Temma Sync */ char command[16]; char sign; double dec; calcLST(buffer); set_TemmaLST(buffer); dec=fabs(currentDec); if (currentDec>0){ sign='+'; } else { sign='-'; } snprintf(buffer, sizeof(buffer),"%.2d%.2d%.2d%c%.2d%.2d%.1d", (int)currentRA,(int)(currentRA*(double)60)%60,((int)(currentRA*(double)6000))%100,sign, (int)dec,(int)(dec*(double)60)%60,((int)(dec*(double)600))%10); fprintf(stderr,"Goto %s\n", buffer); snprintf(command,14,"P%s",buffer); buffer[14]=0; fprintf(stderr,"Goto command:%s\n", command); portWrite(command); portRead(buffer,-1,TEMMA_TIMEOUT); if(command[0]=='R'){ return 0; } else return -1; } int extractRA(char *buf){ int r,h,m,s; /*double dra;*/ r=atoi(buf); h=r/10000; m=r/100-100*h; s=(r%100)*.6; temmaRA=((double)h+((double)m + (double)s/60)/60); IDSetNumber (&eqTemma, NULL); /* fprintf(stderr,"extractRA: %s %d %d %d %d %lf\n",buf,r,h,m,s,dra);*/ return 0; } int extractDEC(char *buf){ int dec,d,m,s; /*double ddec;*/ dec=atoi(buf+1); d=dec/1000; m=dec/10-100*d; s=(dec%10)*6; temmaDec=((double)d+((double)m + (double)s/60)/60); if (*buf=='-') temmaDec*=-1; IDSetNumber (&eqTemma, NULL); /* fprintf(stderr,"extractDEC: %s %d %d %d %d %lf\n",buf,dec,d,m,s,ddec);*/ return 0; } int get_TemmaCurrentpos(char *local_buffer){ char buf[16]; if (portWrite("E") < 0) return -1; if(portRead(local_buffer,-1,TEMMA_TIMEOUT)==SUCCESS){ if(strstr(local_buffer, "E")==local_buffer){ strncpy(buf,local_buffer+1,6); buf[6]=0; extractRA(buf); strncpy(buf,local_buffer+7,6); buf[6]=0; extractDEC(buf); return 0; } else { return -1; } } return 0; } int set_TemmaCurrentpos(void) { /* Temma Sync */ char buf[16], sign; double dec; calcLST(buf); set_TemmaLST(buf); portWrite("Z"); calcLST(buf); set_TemmaLST(buf); dec=fabs(currentDec); if (currentDec>0){ sign='+'; } else { sign='-'; } sprintf(buffer,"%.2d%.2d%.2d%c%.2d%.2d%.1d", (int)currentRA,(int)(currentRA*(double)60)%60,((int)(currentRA*(double)6000))%100,sign, (int)dec,(int)(dec*(double)60)%60,((int)(dec*(double)600))%10); fprintf(stderr,"sync to %s %f %f\n", buffer,currentRA,dec); snprintf(buf, sizeof(buf), "D%s",buffer); buf[13]=0; portWrite(buf); *buffer=0; portRead(buffer,-1,TEMMA_TIMEOUT); if(buffer[0]=='R'){ return 0; } else{ return -1; } } int do_TemmaSLEW(char mode){ /*char command[16];*/ sprintf(buffer,"M%c",mode); /* see bit definition in Temmadriver.h */ if (portWrite(buffer) < 0) return -1; return 0; } int get_TemmaVERSION(char *local_buffer){ int err; if ((err=portWrite("v")) < 0){ return err; } portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "ver")==local_buffer){ return 0; } else return EREAD; } int get_TemmaGOTOstatus(char *local_buffer){ /* 0 no ongoing goto 1 ongoing Goto -1 error */ if (portWrite("s") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "s")==local_buffer){ return 0; } else return -1; } int get_TemmaBOTHcorrspeed(char *local_buffer){ if (portWrite("lg") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "lg")==local_buffer){ return 0; } else return -1; } int get_TemmaDECcorrspeed(char *local_buffer){ if (portWrite("lb") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "lb")==local_buffer){ return 0; } else return -1; } int set_TemmaDECcorrspeed(char *local_buffer){ char command[16]; snprintf(command, 4, "LB%s",local_buffer); if (portWrite(command) < 0) return -1; return 0; } int get_TemmaRAcorrspeed(char *local_buffer){ if (portWrite("la") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "la")==local_buffer){ return 0; } else return -1; } int set_TemmaRAcorrspeed(char *local_buffer){ char command[16]; snprintf(command, 4,"LA%s",local_buffer); if (portWrite(command) < 0) return -1; return 0; } int get_TemmaLatitude(char *local_buffer){ if (portWrite("i") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(local_buffer[0]=='i'){ return 0; } else return -1; } int set_TemmaLatitude(char *local_buffer){ char command[16]; double lat; char sign; lat=fabs(latitude); if (latitude>0){ sign='+'; } else { sign='-'; } sprintf(command,"I%c%.2d%.2d%.1d", sign, (int)lat, (int)(lat*(double)60)%60, ((int)(lat*(double)600))%10); if (portWrite(command) < 0) return -1; return 0; } int get_TemmaLST(char *local_buffer){ if (portWrite("g") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(local_buffer[0]=='g'){ return 0; } else return -1; } int set_TemmaLST(char *local_buffer){ char command[16]; snprintf(command,7,"T%s",local_buffer); if (portWrite(command) < 0) return -1; return 0; } int get_TemmaCometTracking(char *local_buffer){ if (portWrite("lm") < 0) return -1; portRead(local_buffer,-1,TEMMA_TIMEOUT); if(strstr(local_buffer, "lm")==local_buffer){ return 0; } else return -1; } int set_TemmaStandbyState(int on){ if (on){ return portWrite("STN-ON"); } else{ return portWrite("STN-OFF"); } return 0; } int get_TemmaStandbyState(unsigned char *local_buffer){ int nb; int status; if ((nb=portWrite("STN-COD")) < 0){ IDSetSwitch (&RAmotorSw, "I/O error when asking RAmotor status"); return -1; } if((status=portRead(local_buffer,-1,TEMMA_TIMEOUT)==SUCCESS)){ if(strstr(local_buffer, "stn")==local_buffer){ local_buffer[7]=0; if (strstr(local_buffer,"on")){ /* stanby on */ RAmotorSw.s = IPS_OK; RAmotor[0].s = ISS_OFF; RAmotor[1].s = ISS_ON; IDSetSwitch (&RAmotorSw, "RA motor is off."); } else{ if (strstr(local_buffer,"off")){ /* stanby off */ RAmotorSw.s = IPS_OK; RAmotor[0].s = ISS_ON; RAmotor[1].s = ISS_OFF; IDSetSwitch (&RAmotorSw, "RA motor is on."); } else { RAmotorSw.s = IPS_OK; IDSetSwitch (&RAmotorSw, "I/O error when getting RAmotor status"); } } } return 0; } if (status<=ETIMEOUT && status >=ECOMMAND){ IDSetSwitch(&RAmotorSw, "%s", errormes[ETIMEOUT - status]); } return -1; } int set_TemmaCometTracking(char *local_buffer){ char command[16]; snprintf(command,15,"LM%s",local_buffer); if (portWrite(command) < 0) return -1; return 0; } int set_TemmaSolarRate(void){ if (portWrite("LK") < 0) return -1; return 0; } int set_TemmaStellarRate(void){ if (portWrite("LL") < 0) return -1; return 0; } int switch_Temmamountside(void){ if (portWrite("PT") < 0) return -1; return 0; } /********************************************************************** * Comm **********************************************************************/ int openPort(const char *portID) { struct termios ttyOptions; if ( (fd = open(portID, O_RDWR)) == -1) return -1; memset(&ttyOptions, 0, sizeof(ttyOptions)); tcgetattr(fd, &ttyOptions); /* 8 bit, enable read */ ttyOptions.c_cflag |= CS8; /* parity */ ttyOptions.c_cflag |= PARENB; ttyOptions.c_cflag &= ~PARODD; ttyOptions.c_cflag &= ~CSTOPB; ttyOptions.c_cflag |= CRTSCTS; /* set baud rate */ cfsetispeed(&ttyOptions, B19200); cfsetospeed(&ttyOptions, B19200); /* set input/output flags */ ttyOptions.c_iflag = IGNBRK; /* Read at least one byte */ ttyOptions.c_cc[VMIN] = 1; ttyOptions.c_cc[VTIME] = 5; /* Misc. */ ttyOptions.c_lflag = 0; ttyOptions.c_oflag = 0; /* set attributes */ tcsetattr(fd, TCSANOW, &ttyOptions); /* flush the channel */ tcflush(fd, TCIOFLUSH); return (fd); } int portWrite(char * buf) { int nbytes=strlen(buf); /*, totalBytesWritten;*/ int bytesWritten = 0; /*int retry=10;*/ bytesWritten = write(fd, buf, nbytes); bytesWritten += write(fd, "\r\n", 2); /*fprintf(stderr,"portwrite :%d octets %s\n", bytesWritten, buf);*/ if (bytesWritten!=nbytes+2){ perror("write error: "); IDLog("Error writing to port"); return EWRITE; } return (bytesWritten); } int portRead(char *buf, int nbytes, int timeout) { /* A very basic finite state machine monitors the bytes read ; state 0 : read regular bytes state 1 : just read a \n, waiting for a \r state 2 : read a \n and a \r, command is over. Not sure it is useful here but I use a more sophisticated version of this with a GPS receiver and it is robust and reliable We return a null terminated string. */ int bytesRead = 0, state=0, /*i=0,*/ current=0; /*int totalBytesRead = 0;*/ int err; if ( (err = TemmareadOut(timeout)) ){ switch (err){ case ETIMEOUT: IDLog("Error: timeout while reading"); return err; } } while (read(fd,buf+bytesRead,1)==1){ /* fprintf(stderr,"%c",buf[bytesRead]); */ fflush(NULL); switch (state) { case 0: if(buf[bytesRead]==13) state=1; break; case 1: if(buf[bytesRead]==10) state=2; else if(buf[bytesRead]==13) state=1; else state=0; break; } ++current; if (state==2){ /*process(buf);*/ buf[bytesRead+1]=0; state=current=0; return SUCCESS; } bytesRead=current; } return state; } int TemmareadOut(int timeout) { struct timeval tv; fd_set readout; int retval; FD_ZERO(&readout); FD_SET(fd, &readout); /* wait for 'timeout' seconds */ tv.tv_sec = timeout; tv.tv_usec = 0; /* Wait till we have a change in the fd status */ retval = select (fd+1, &readout, NULL, NULL, &tv); /* Return 0 on successful fd change */ if (retval > 0) return 0; /* Return -1 due to an error */ else if (retval == EREAD) return retval; /* Return -2 if time expires before anything interesting happens */ else { return ETIMEOUT; } }