#if 0
    LX200 Driver
    Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>

#include "indicom.h"
#include "indidevapi.h"
#include "lx200driver.h"

#define LX200_TIMEOUT	5		/* FD timeout in seconds */

int fd;
int read_ret, write_ret;

/**************************************************************************
 Basic I/O
**************************************************************************/
int openPort(const char *portID);
int portRead(char *buf, int nbytes, int timeout);
int portWrite(const char * buf);
int LX200readOut(int timeout);

int Connect(const char* device);
void Disconnect(void);

/**************************************************************************
 Diagnostics
 **************************************************************************/
char ACK(void);
int testTelescope(void);

/**************************************************************************
 Get Commands: store data in the supplied buffer. Return 0 on success or -1 on failure 
 **************************************************************************/
 
/* Get Double from Sexagisemal */
int getCommandSexa(double *value, const char *cmd);
/* Get String */
int getCommandString(char *data, const char* cmd);
/* Get Int */
int getCommandInt(int *value, const char* cmd);
/* Get tracking frequency */
int getTrackFreq(double * value);
/* Get site Latitude */
int getSiteLatitude(int *dd, int *mm);
/* Get site Longitude */
int getSiteLongitude(int *ddd, int *mm);
/* Get Calender data */
int getCalenderDate(char *date);
/* Get site Name */
int getSiteName(char *siteName, int siteNum);
/* Get Number of Bars */
int getNumberOfBars(int *value);
/* Get Home Search Status */
int getHomeSearchStatus(int *status);
/* Get OTA Temperature */
int getOTATemp(double * value);
/* Get time format: 12 or 24 */
int getTimeFormat(int *format);


/**************************************************************************
 Set Commands
 **************************************************************************/

/* Set Int */
int setCommandInt(int data, const char *cmd);
/* Set Sexigesimal */
int setCommandXYZ( int x, int y, int z, const char *cmd);
/* Common routine for Set commands */
int setStandardProcedure(char * writeData);
/* Set Slew Mode */
int setSlewMode(int slewMode);
/* Set Alignment mode */
int setAlignmentMode(unsigned int alignMode);
/* Set Object RA */
int setObjectRA(double ra);
/* set Object DEC */
int setObjectDEC(double dec);
/* Set Calender date */
int setCalenderDate(int dd, int mm, int yy);
/* Set UTC offset */
int setUTCOffset(double hours);
/* Set Track Freq */
int setTrackFreq(double trackF);
/* Set current site longitude */
int setSiteLongitude(double Long);
/* Set current site latitude */
int setSiteLatitude(double Lat);
/* Set Object Azimuth */
int setObjAz(double az);
/* Set Object Altitude */
int setObjAlt(double alt);
/* Set site name */
int setSiteName(char * siteName, int siteNum);
/* Set maximum slew rate */
int setMaxSlewRate(int slewRate);
/* Set focuser motion */
int setFocuserMotion(int motionType);
/* Set focuser speed mode */
int setFocuserSpeedMode (int speedMode);
/* Set minimum elevation limit */
int setMinElevationLimit(int min);
/* Set maximum elevation limit */
int setMaxElevationLimit(int max);

/**************************************************************************
 Motion Commands
 **************************************************************************/
/* Slew to the selected coordinates */
int Slew(void);
/* Synchronize to the selected coordinates and return the matching object if any */
int Sync(char *matchedObject);
/* Abort slew in all axes */
int abortSlew(void);
/* Move into one direction, two valid directions can be stacked */
int MoveTo(int direction);
/* Half movement in a particular direction */
int HaltMovement(int direction);
/* Select the tracking mode */
int selectTrackingMode(int trackMode);

/**************************************************************************
 Other Commands
 **************************************************************************/
 /* Ensures LX200 RA/DEC format is long */
int checkLX200Format(void);
/* Select a site from the LX200 controller */
int selectSite(int siteNum);
/* Select a catalog object */
int selectCatalogObject(int catalog, int NNNN);
/* Select a sub catalog */
int selectSubCatalog(int catalog, int subCatalog);

/**********************************************************************
* BASIC
**********************************************************************/

int Connect(const char *device)
{
 fprintf(stderr, "Connecting to device %s\n", device);
 
 if (openPort(device) < 0)
  return -1;
 else
  return 0;
}

void Disconnect()
{
fprintf(stderr, "Disconnected.\n");
close(fd);
}

int testTelescope()
{  
  int i=0;
  char ack[1] = { (char) 0x06 };
  char MountAlign[64];
  fprintf(stderr, "Testing telescope's connection...\n");

  for (i=0; i < 2; i++)
  {
    write(fd, ack, 1);
    read_ret = portRead(MountAlign, 1, LX200_TIMEOUT);
    if (read_ret == 1)
     return 0;
    usleep(50000);
  }
  
  return -1;
}

/**********************************************************************
* GET
**********************************************************************/

char ACK()
{
  char ack[1] = { (char) 0x06 };
  char MountAlign[2];

  write_ret = write(fd, ack, 1);

  if (write_ret < 0)
    return -1;
 
  read_ret = portRead(MountAlign, 1, LX200_TIMEOUT);
  
  if (read_ret == 1)
    return MountAlign[0];
  else
    return read_ret;

}

int getCommandSexa(double *value, const char * cmd)
{
  char tempString[16];
  
  tcflush(fd, TCIFLUSH);

  if (portWrite(cmd) < 0)
   return -1;
  
  if ( (read_ret = portRead(tempString, -1, LX200_TIMEOUT)) < 1)
     return read_ret;
 
  tempString[read_ret - 1] = '\0';
  
  if (f_scansexa(tempString, value))
   return -1;
 
   return 0;
}

int getCommandString(char *data, const char* cmd)
{
    char * term;
    
    if (portWrite(cmd) < 0)
      return -1;

    read_ret = portRead(data, -1, LX200_TIMEOUT);
    
    if (read_ret < 1)
     return read_ret;

    term = strchr (data, '#');
    if (term)
      *term = '\0';

    fprintf(stderr, "Request data: %s\n", data);

    return 0;
}

int getCalenderDate(char *date)
{

 int dd, mm, yy;
 int err;

 if ( (err = getCommandString(date, "#:GC#")) )
   return err;

 /* Meade format is MM/DD/YY */

  read_ret = sscanf(date, "%d%*c%d%*c%d", &mm, &dd, &yy);
  if (read_ret < 3)
   return -1;

 /* We need to have in in YYYY/MM/DD format */
 sprintf(date, "20%02d/%02d/%02d", yy, mm, dd);

 return (0);

}

int getTimeFormat(int *format)
{
  char tempString[16];
  int tMode;

  if (portWrite("#:Gc#") < 0)
    return -1;

  read_ret = portRead(tempString, -1, LX200_TIMEOUT);
  
  if (read_ret < 1)
   return read_ret;
   
  tempString[read_ret-1] = '\0';

  read_ret = sscanf(tempString, "(%d)", &tMode);

  if (read_ret < 1)
   return -1;
  else
   *format = tMode;
   
  return 0;

}

/*int getUTCOffset()
{
    char tempString[4];
    int offSet;

    portWrite("#:GG#");

    read_ret = portRead(tempString, 4);
    if (read_ret)
     return -1;

    tempString[3] = '\0';

    sscanf(tempString, "%d", &offSet);

    fprintf(stderr, "UTC Offset: %d\n", offSet);

    return offSet;
}

int getMaxElevationLimit()
{
    char tempString[16];
    int limit;

    portWrite("#:Go#");

    read_ret = portRead(tempString, -1, LX200_TIMEOUT);
    if (read_ret < 1)
     return -1;

    tempString[read_ret-1] = '\0';

    sscanf(tempString, "%d", &limit);

    fprintf(stderr, "Max elevation limit string is %s\n", tempString);

    return limit;
}

int getMinElevationLimit()
{
    char tempString[16];
    int limit;

    portWrite("#:Gh#");

    read_ret = portRead(tempString, -1, LX200_TIMEOUT);
    if (read_ret < 1)
     return -1;

    tempString[read_ret-1] = '\0';

    sscanf(tempString, "%d", &limit);

    fprintf(stderr, "Min elevation limit string is %s\n", tempString);

    return limit;

}
*/

int getSiteName(char *siteName, int siteNum)
{
  char * term;

  switch (siteNum)
  {
    case 1:
     if (portWrite("#:GM#") < 0)
      return -1;
     break;
    case 2:
     if (portWrite("#:GN#") < 0)
      return -1;
     break;
    case 3:
     if (portWrite("#:GO#") < 0)
       return -1;
     break;
    case 4:
     if (portWrite("#:GP#") < 0)
      return -1;
     break;
    default:
     return -1;
   }

   read_ret = portRead(siteName, -1, LX200_TIMEOUT);
   if (read_ret < 1)
     return read_ret;

   siteName[read_ret - 1] = '\0';

   term = strchr (siteName, ' ');
    if (term)
      *term = '\0';

    term = strchr (siteName, '<');
    if (term)
      strcpy(siteName, "unused site");

   fprintf(stderr, "Requested site name: %s\n", siteName);

    return 0;
}

int getSiteLatitude(int *dd, int *mm)
{
  char tempString[16];
  
  if (portWrite("#:Gt#") < 0)
    return -1;

  read_ret = portRead(tempString, -1, LX200_TIMEOUT);
  
   if (read_ret < 1) 
   return read_ret;
   
  tempString[read_ret -1] = '\0';

  if (sscanf (tempString, "%d%*c%d", dd, mm) < 2)
   return -1;

  fprintf(stderr, "Requested site latitude in String %s\n", tempString);
  fprintf(stderr, "Requested site latitude %d:%d\n", *dd, *mm);

  return 0;
}

int getSiteLongitude(int *ddd, int *mm)
{
  char tempString[16];

  if (portWrite("#:Gg#") < 0)
   return -1;

  read_ret = portRead(tempString, -1, LX200_TIMEOUT);
  
  if (read_ret < 1)
    return read_ret;
    
  tempString[read_ret -1] = '\0';

  if (sscanf (tempString, "%d%*c%d", ddd, mm) < 2)
   return -1;

  fprintf(stderr, "Requested site longitude in String %s\n", tempString);
  fprintf(stderr, "Requested site longitude %d:%d\n", *ddd, *mm);

  return 0;
}

int getTrackFreq(double *value)
{
    float Freq;
    char tempString[16];
    
    if (portWrite("#:GT#") < 0)
      return -1;

    read_ret = portRead(tempString, -1, LX200_TIMEOUT);
    
    if (read_ret < 1)
     return read_ret;

    tempString[read_ret] = '\0';
    
    /*fprintf(stderr, "Telescope tracking freq str: %s\n", tempString);*/
    
    if (sscanf(tempString, "%f#", &Freq) < 1)
     return -1;
   
    *value = (double) Freq;
    
    /*fprintf(stderr, "Tracking frequency value is %f\n", Freq);*/
    
    return 0;
}

int getNumberOfBars(int *value)
{
   char tempString[128];

   if (portWrite("#:D#") < 0)
     return -1;

   read_ret = portRead(tempString, -1, LX200_TIMEOUT);
   
   if (read_ret < 0)
    return read_ret;

   *value = read_ret -1;
   
   return 0;
}

int getHomeSearchStatus(int *status)
{
  char tempString[16];

  if (portWrite("#:h?#") < 0)
   return -1;

  read_ret = portRead(tempString, 1, LX200_TIMEOUT);
  
  if (read_ret < 1)
   return read_ret;
   
  tempString[1] = '\0';

  if (tempString[0] == '0')
    *status = 0;
  else if (tempString[0] == '1')
    *status = 1;
  else if (tempString[0] == '2')
    *status = 1;
  
  return 0;
}

int getOTATemp(double *value)
{

  char tempString[16];
  float temp;
  
  if (portWrite("#:fT#") < 0)
   return -1;

  read_ret = portRead(tempString, -1, LX200_TIMEOUT);
  
  if (read_ret < 1)
   return read_ret;
   
  tempString[read_ret - 1] = '\0';

  if (sscanf(tempString, "%f", &temp) < 1)
   return -1;
   
   *value = (double) temp;

  return 0;

}

/**********************************************************************
* SET
**********************************************************************/

int setStandardProcedure(char * data)
{
 char boolRet[2];
 
 if (portWrite(data) < 0)
  return -1;
 
 read_ret = portRead(boolRet, 1, LX200_TIMEOUT);
 
 if (read_ret < 1)
   return read_ret;

 if (boolRet[0] == '0')
 {
     fprintf(stderr, "%s Failed.\n", data);
     return -1;
 }

 fprintf(stderr, "%s Successful\n", data);
 return 0;


}

int setCommandInt(int data, const char *cmd)
{

  char tempString[16];

  snprintf(tempString, sizeof( tempString ), "%s%d#", cmd, data);

  if (portWrite(tempString) < 0)
    return -1;

  return 0;
}

int setMinElevationLimit(int min)
{
 char tempString[16];

 snprintf(tempString, sizeof( tempString ), "#:Sh%02d#", min);

 return (setStandardProcedure(tempString));
}

int setMaxElevationLimit(int max)
{
 char tempString[16];

 snprintf(tempString, sizeof( tempString ), "#:So%02d*#", max);

 return (setStandardProcedure(tempString));

}

int setMaxSlewRate(int slewRate)
{

   char tempString[16];

   if (slewRate < 2 || slewRate > 8)
    return -1;

   snprintf(tempString, sizeof( tempString ), "#:Sw%d#", slewRate);

   return (setStandardProcedure(tempString));

}


int setObjectRA(double ra)
{

 int h, m, s;
 char tempString[16];

 getSexComponents(ra, &h, &m, &s);

 snprintf(tempString, sizeof( tempString ), "#:Sr %02d:%02d:%02d#", h, m, s);
 IDLog("Set Object RA String %s\n", tempString);
  return (setStandardProcedure(tempString));
}


int setObjectDEC(double dec)
{
  int d, m, s;
  char tempString[16];

  getSexComponents(dec, &d, &m, &s);

  /* case with negative zero */
  if (!d && dec < 0)
    snprintf(tempString, sizeof( tempString ), "#:Sd -%02d:%02d:%02d#", d, m, s);
  else
    snprintf(tempString, sizeof( tempString ), "#:Sd %+03d:%02d:%02d#", d, m, s);

  IDLog("Set Object DEC String %s\n", tempString);
  
   return (setStandardProcedure(tempString));

}

int setCommandXYZ(int x, int y, int z, const char *cmd)
{
  char tempString[16];

  snprintf(tempString, sizeof( tempString ), "%s %02d:%02d:%02d#", cmd, x, y, z);

  return (setStandardProcedure(tempString));
}

int setAlignmentMode(unsigned int alignMode)
{
  fprintf(stderr , "Set alignment mode %d\n", alignMode);

  switch (alignMode)
   {
     case LX200_ALIGN_POLAR:
      if (portWrite("#:AP#") < 0)
        return -1;
      break;
     case LX200_ALIGN_ALTAZ:
      if (portWrite("#:AA#") < 0)
        return -1;
      break;
     case LX200_ALIGN_LAND:
       if (portWrite("#:AL#") < 0)
        return -1;
       break;
   }
   
   return 0;
}

int setCalenderDate(int dd, int mm, int yy)
{
   char tempString[32];
   char dumpPlanetaryUpdateString[64];
   char boolRet[2];
   yy = yy % 100;

   snprintf(tempString, sizeof( tempString ), "#:SC %02d/%02d/%02d#", mm, dd, yy);

   if (portWrite(tempString) < 0)
    return -1;

   read_ret = portRead(boolRet, 1, LX200_TIMEOUT);
   
   if (read_ret < 1)
    return read_ret;
    
   boolRet[1] = '\0';

   if (boolRet[0] == '0')
     return -1;
     
    /* Read dumped data */
    portRead(dumpPlanetaryUpdateString, -1, LX200_TIMEOUT);
    portRead(dumpPlanetaryUpdateString, -1, 5);

   return 0;
}

int setUTCOffset(double hours)
{
   char tempString[16];

   /*TODO add fractions*/
   snprintf(tempString, sizeof( tempString ), "#:SG %+03d#", (int) hours);

   fprintf(stderr, "UTC string is %s\n", tempString);

   return (setStandardProcedure(tempString));

}

int setSiteLongitude(double Long)
{
   int d, m, s;
   char tempString[32];

   getSexComponents(Long, &d, &m, &s);

   snprintf(tempString, sizeof( tempString ), "#:Sg%03d:%02d#", d, m);

   return (setStandardProcedure(tempString));
}

int setSiteLatitude(double Lat)
{
   int d, m, s;
   char tempString[32];

   getSexComponents(Lat, &d, &m, &s);

   snprintf(tempString, sizeof( tempString ), "#:St%+03d:%02d:%02d#", d, m, s);

   return (setStandardProcedure(tempString));
}

int setObjAz(double az)
{
   int d,m,s;
   char tempString[16];

   getSexComponents(az, &d, &m, &s);

   snprintf(tempString, sizeof( tempString ), "#:Sz%03d:%02d#", d, m);

   return (setStandardProcedure(tempString));

}

int setObjAlt(double alt)
{
    int d, m, s;
    char tempString[16];

   getSexComponents(alt, &d, &m, &s);

   snprintf(tempString, sizeof( tempString ), "#:Sa%+02d*%02d#", d, m);

   return (setStandardProcedure(tempString));
}


int setSiteName(char * siteName, int siteNum)
{

   char tempString[16];

   switch (siteNum)
   {
     case 1:
      snprintf(tempString, sizeof( tempString ), "#:SM %s#", siteName);
      break;
     case 2:
      snprintf(tempString, sizeof( tempString ), "#:SN %s#", siteName);
      break;
     case 3:
      snprintf(tempString, sizeof( tempString ), "#:SO %s#", siteName);
      break;
    case 4:
      snprintf(tempString, sizeof( tempString ), "#:SP %s#", siteName);
      break;
    default:
      return -1;
    }

   return (setStandardProcedure(tempString));
}

int setSlewMode(int slewMode)
{

  switch (slewMode)
  {
    case LX200_SLEW_MAX:
     if (portWrite("#:RS#") < 0)
      return -1;
     break;
    case LX200_SLEW_FIND:
     if (portWrite("#:RM#") < 0)
      return -1;
     break;
    case LX200_SLEW_CENTER:
     if (portWrite("#:RC#") < 0)
      return -1;
     break;
    case LX200_SLEW_GUIDE:
     if (portWrite("#:RG#") < 0)
      return -1;
     break;
    default:
     break;
   }
   
   return 0;

}

int setFocuserMotion(int motionType)
{

  switch (motionType)
  {
    case LX200_FOCUSIN:
    if (portWrite("#:F+#") < 0)
      return -1;
      break;
    case LX200_FOCUSOUT:
     if (portWrite("#:F-#") < 0)
       return -1;
     break;
  }

  return 0;
}

int setFocuserSpeedMode (int speedMode)
{

 switch (speedMode)
 {
    case LX200_HALTFOCUS:
     if (portWrite("#:FQ#") < 0)
      return -1;
     break;
    case LX200_FOCUSFAST:
     if (portWrite("#:FF#") < 0)
      return -1;
     break;
    case LX200_FOCUSMEDIUM:
     if (portWrite("#:F3#") < 0)
      return -1;
      break;
    case LX200_FOCUSSLOW:
      if (portWrite("#:FS#") < 0)
       return -1;
      break;
 }
 
 return 0;

}

int setTrackFreq(double trackF)
{
  char tempString[16];

  snprintf(tempString, sizeof( tempString ), "#:ST %04.1f#", trackF);

  return (setStandardProcedure(tempString));

}

/**********************************************************************
* Misc
*********************************************************************/

int Slew()
{
    char slewNum[2];
    char errorMsg[128];

    if (portWrite("#:MS#") < 0)
      return -1;

    read_ret = portRead(slewNum, 1, LX200_TIMEOUT);
    
    if (read_ret < 1)
      return read_ret;
      
    slewNum[1] = '\0';

    if (slewNum[0] == '0')
     return 0;
   
   read_ret = portRead(errorMsg, -1, LX200_TIMEOUT);
   
   if (read_ret < 1)
    return read_ret;
    
    if  (slewNum[0] == '1')
      return 1;
    else return 2;

}

int MoveTo(int direction)
{

  switch (direction)
  {
    case LX200_NORTH:
    portWrite("#:Mn#");
    break;
    case LX200_WEST:
    portWrite("#:Mw#");
    break;
    case LX200_EAST:
    portWrite("#:Me#");
    break;
    case LX200_SOUTH:
    portWrite("#:Ms#");
    break;
    default:
    break;
  }
  
  return 0;
}

int HaltMovement(int direction)
{

switch (direction)
  {
    case LX200_NORTH:
     if (portWrite("#:Qn#") < 0)
      return -1;
     break;
    case LX200_WEST:
     if (portWrite("#:Qw#") < 0)
      return -1;
     break;
    case LX200_EAST:
     if (portWrite("#:Qe#") < 0)
      return -1;
     break;
    case LX200_SOUTH:
     if (portWrite("#:Qs#") < 0)
       return -1;
    break;
    case LX200_ALL:
     if (portWrite("#:Q#") < 0)
       return -1;
     break;
    default:
     return -1;
    break;
  }
  
  return 0;

}

int abortSlew()
{
 if (portWrite("#:Q#") < 0)
  return -1;

 return 0;
}

int Sync(char *matchedObject)
{
  portWrite("#:CM#");

  read_ret = portRead(matchedObject, -1, LX200_TIMEOUT);
  
  if (read_ret < 1)
   return read_ret;
   
  matchedObject[read_ret-1] = '\0';
  
  /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
  usleep(10000);
  
  tcflush(fd, TCIFLUSH);

  return 0;
}

int selectSite(int siteNum)
{

  switch (siteNum)
  {
    case 1:
      if (portWrite("#:W1#") < 0)
       return -1;
      break;
    case 2:
      if (portWrite("#:W2#") < 0)
       return -1;
      break;
    case 3:
      if (portWrite("#:W3#") < 0)
       return -1;
      break;
    case 4:
      if (portWrite("#:W4#") < 0)
       return -1;
      break;
    default:
    return -1;
    break;
  }
  
  return 0;

}

int selectCatalogObject(int catalog, int NNNN)
{
 char tempString[16];
 
 switch (catalog)
 {
   case LX200_STAR_C:
    snprintf(tempString, sizeof( tempString ), "#:LS%d#", NNNN);
    break;
   case LX200_DEEPSKY_C:
    snprintf(tempString, sizeof( tempString ), "#:LC%d#", NNNN);
    break;
   case LX200_MESSIER_C:
    snprintf(tempString, sizeof( tempString ), "#:LM%d#", NNNN);
    break;
   default:
    return -1;
  }

  if (portWrite(tempString) < 0)
   return -1;

  return 0;
}

int selectSubCatalog(int catalog, int subCatalog)
{
  char tempString[16];
  switch (catalog)
  {
    case LX200_STAR_C:
    snprintf(tempString, sizeof( tempString ), "#:LsD%d#", subCatalog);
    break;
    case LX200_DEEPSKY_C:
    snprintf(tempString, sizeof( tempString ), "#:LoD%d#", subCatalog);
    break;
    case LX200_MESSIER_C:
     return 1;
    default:
     return 0;
  }

  return (setStandardProcedure(tempString));
}

int checkLX200Format()
{

  char tempString[16];

  if (portWrite("#:GR#") < 0)
   return -1;

  read_ret = portRead(tempString, -1, LX200_TIMEOUT);
  
  if (read_ret < 1)
   return read_ret;
   
  tempString[read_ret - 1] = '\0';

  /* Short */
  if (tempString[5] == '.')
     if (portWrite("#:U#") < 0)
      return -1;
  
  return 0;
}

int  selectTrackingMode(int trackMode)
{

   switch (trackMode)
   {
    case LX200_TRACK_DEFAULT:
      fprintf(stderr, "Setting tracking mode to sidereal.\n");
     if (portWrite("#:TQ#") < 0)
       return -1;
     break;
    case LX200_TRACK_LUNAR:
      fprintf(stderr, "Setting tracking mode to LUNAR.\n");
     if (portWrite("#:TL#") < 0)
       return -1;
     break;
   case LX200_TRACK_MANUAL:
     fprintf(stderr, "Setting tracking mode to CUSTOM.\n");
     if (portWrite("#:TM#") < 0)
      return -1;
     break;
   default:
    return -1;
    break;
   }
   
   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);

   /* Control options
    charecter size */
   ttyOptions.c_cflag &= ~CSIZE;
   /* 8 bit, enable read */
   ttyOptions.c_cflag |= CREAD | CLOCAL | CS8;
   /* no parity */
   ttyOptions.c_cflag &= ~PARENB;

   /* set baud rate */
   cfsetispeed(&ttyOptions, B9600);
   cfsetospeed(&ttyOptions, B9600);

  /* set input/output flags */
  ttyOptions.c_iflag = IGNBRK;
  /* no software flow control */
  ttyOptions.c_iflag &= ~(IXON|IXOFF|IXANY);

  /* 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(const char * buf)
{
  int nbytes, totalBytesWritten;
  int bytesWritten = 0;   
   
  nbytes = totalBytesWritten = strlen(buf);

  while (nbytes > 0)
  {
    
    bytesWritten = write(fd, buf, nbytes);

    if (bytesWritten < 0)
     return -1;

    buf += bytesWritten;
    nbytes -= bytesWritten;
  }

  /* Returns the # of bytes written */
  return (totalBytesWritten);
}

int portRead(char *buf, int nbytes, int timeout)
{

int bytesRead = 0;
int totalBytesRead = 0;
int err;

  /* Loop until encountring the '#' char */
  if (nbytes == -1)
  {
     for (;;)
     {
         if ( (err = LX200readOut(timeout)) )
	   return err;

         bytesRead = read(fd, buf, 1);

         if (bytesRead < 0 )
            return -1;

        if (bytesRead)
          totalBytesRead++;

        if (*buf == '#')
	   return totalBytesRead;

        buf += bytesRead;
     }
  }
  
  while (nbytes > 0)
  {
     if ( (err = LX200readOut(timeout)) )
      return err;

     bytesRead = read(fd, buf, nbytes);

     if (bytesRead < 0 )
      return -1;

     buf += bytesRead;
     totalBytesRead++;
     nbytes -= bytesRead;
  }

  return totalBytesRead;
}

int LX200readOut(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 == -1)
   return retval;
  /* Return -2 if time expires before anything interesting happens */
  else 
    return -2;
  
}

