/* ---------------------------------------------------------------------------*/
/* -                Protocol for TheSkyX telescope control                   -*/
/* ---------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright 2010-2012 John Kielkopf                                          */
/*                                                                            */
/* Distributed under the terms of the General Public License (see LICENSE)    */
/*                                                                            */
/* John Kielkopf (kielkopf@louisville.edu)                                    */
/*                                                                            */
/* Date: July 22, 2012                                                        */
/* Version: 2.4                                                               */
/*                                                                            */
/* History:                                                                   */
/*                                                                            */
/* July 21, 2010                                                              */
/*   Version 1.0                                                              */
/*   Compatible with XmTel 5.2.0                                              */
/*                                                                            */
/* October 10, 2011                                                           */
/*   Version 2.3                                                              */
/*   Changed version number to match package                                  */
/*   Added CenterGuide                                                        */
/*                                                                            */
/* July 22, 2012                                                              */
/*   Version 6.0                                                              */
/*   Instrument control routines now use external functions                   */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <math.h>
#include "protocol.h"

#define NULL_PTR(x) (x *)0

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

/* System variables and prototypes */

/* Telescope and mounting commands that may be called externally */

/* Interface control */

void ConnectTel(void);
void DisconnectTel(void);
int  CheckConnectTel(void);

/* Slew and track control */

void SetRate(int newrate);
void StartSlew(int direction);
void StopSlew(int direction);
void StartTrack(void);
void CenterGuide(double centerra, double centerdec, 
  int raflag, int decflag, int pmodel);
void StopTrack(void);
void FullStop(void);

/* Coordinates and time */

void GetTel(double *telra, double *teldec, int pmodel);
int  GoToCoords(double newra, double newdec, int pmodel);
int  CheckGoTo(double desra, double desdec, int pmodel);

/* Slew limits */

int  GetSlewStatus(void);
int  SetLimits(int limits);
int  GetLimits(int *limits);

/* Synchronizing */

void SyncTelOffsets(double newoffsetra, double newoffsetdec);

/* Instrumentation */

void Heater(int heatercmd);
void Fan(int fancmd);
void Focus(int focuscmd, int focusspd);
void Rotate(int rotatecmd, int rotatespd);
void GetFocus(double *telfocus);
void GetRotate(double *telrotate);
void GetTemperature(double *teltemperature);

/* External variables and shared code */

extern double LSTNow(void);
extern double UTNow(void);
extern double offsetra, offsetdec;
extern double SiteLatitude;
extern double homeha;            /* Home ha                 */
extern double homera;            /* Home ra derived from ha */
extern double homedec;           /* Home dec                */ 

extern void PointingFromTel (double *telra1, double *teldec1, 
  double telra0, double teldec0, int pmodel);

extern void PointingToTel (double *telra0, double *teldec0, 
  double telra1, double teldec1, int pmodel);
  
/* Local data */

static int slewrate;        /* Rate for slew request in StartSlew */

/* Buffers */

char buf[256];              /* General purpose character buffer */

/* Communications variables for internal use */

static int telconnectflag = FALSE;

/* Scripting */

void write_tsx (char *script_message);
void read_tsx_status(int *tsx_status, double *tsx_ra, double *tsx_dec);
static int  script_number = 0;
static char script_filename[64];
static char script_dir[64];
FILE* fp_script;
static char status_filename[64];
static char status_dir[64];
FILE* fp_status;
static char handshake_filename[64];
FILE* fp_handshake;
double tsx_ddec, tsx_dra;


/* End of prototype and variable definitions */
 

/* Report on telescope connection status */

int CheckConnectTel(void)
{
  if (telconnectflag == TRUE)
  {
    return TRUE;
  }
  else
  {
    return FALSE;
  }
}


/* Connect to the telescope through the shared file interface */
/* Return without action if telconnectflag is TRUE */
/* Set telconnectflag TRUE on success and FALSE otherwise */

void ConnectTel(void)
{
  int limits, flag; 
  int tsx_status;
  double tsx_dec, tsx_ra;
  
  if (telconnectflag != FALSE)
  {
    return;
  }

  /* Test connection using TSX */
  
  strcpy(buf,"RunVBScript,telstatus.vbs,");
  write_tsx(buf);

  usleep(1000000);
  
  /* Read the status file from TSX */
  
  read_tsx_status(&tsx_status, &tsx_ra, &tsx_dec);

  if (tsx_status == 0)
  {
    telconnectflag = FALSE;
    return;
  }
  else
  {
    telconnectflag = TRUE;
  }  
  

  /* Perform startup tests */

  flag = GetLimits(&limits);
  printf("Reading old limits flag %d and return value  %d\n",limits, flag);

  usleep(1000000);

  limits = FALSE;
  flag = SetLimits(limits);
  printf("Setting new limits flag %d and return value  %d\n",limits, flag);
  
  usleep(1000000);
  
  flag = GetLimits(&limits);
  printf("Reading new limits flag %d and return value  %d\n",limits, flag);
     
}

/* Assign and save slewrate for use in StartSlew */
/* TheSkyX requires arcminute jog increments */ 
/* Small jogs have a RA error probably due to switching latency */

void SetRate(int newrate)
{
  if(newrate == SLEW) 
  {
    /* 2 degrees */
    slewrate = 4; 
    tsx_ddec =  120.;
    tsx_dra =  1200.;
  }
  else if(newrate == FIND) 
  {
    /* 30 arcminutes */
    slewrate = 3;
    tsx_ddec =  30.;
    tsx_dra =  30.;    
  }
  else if(newrate == CENTER) 
  {
    /* 10 arcminutes */
    slewrate = 2;
    tsx_ddec =  10.;
    tsx_dra =  10.;            
  }
  else if(newrate == GUIDE) 
  {
    /* 2 arcminutes */    
    slewrate = 1;
    tsx_ddec =  2.;
    tsx_dra =  2.;     
  }
}
 

/* Start a slew in chosen direction based on slewrate */
/* The TSX interface does not provide for changing slew rate */
/* The slews here only jog amounts to sensibly mimic optional slew rate */

void StartSlew(int direction)
{
  if(direction == NORTH)
  {
    sprintf(buf,"Jog,North %lf,", tsx_ddec);
  }
  else if(direction == EAST)
  {
    sprintf(buf,"Jog,East %lf,", tsx_dra);
  }
  else if(direction == SOUTH)
  {
    sprintf(buf,"Jog,South %lf,",tsx_ddec);
  }
  else if(direction == WEST)
  {
    sprintf(buf,"Jog,West %lf,", tsx_dra);  
  }
  write_tsx(buf);
  usleep(1000000);
  strcpy(buf,"RunVBScript,telstatus.vbs,");
  write_tsx(buf);
  usleep(1000000);
}


/* Stop the slew in chosen direction */
/* The TSX interface uses jogs rather than open ended runs */

void StopSlew(int direction)
{
  return;
}


void DisconnectTel(void)
{
  if (telconnectflag == TRUE)
  {
    telconnectflag = FALSE;
  }
}


/* Find the coordinates at which the telescope is pointing */
/* Correct for the pointing model */

void GetTel(double *telra, double *teldec, int pmodel)
{

  int tsx_status;
  double telra0, teldec0, telra1, teldec1;

  /* Prompt TSX for most recent data */
  
  strcpy(buf,"RunVBScript,telstatus.vbs,");
  write_tsx(buf);

  usleep(1000000);
  
  /* Read the telescope coordinates from TSX */
  
  read_tsx_status(&tsx_status, &telra0, &teldec0); 

  /* Correct the coordinates that are reported by the telescope */
  
  PointingFromTel(&telra1, &teldec1, telra0, teldec0, pmodel);
 
  /* Return corrected values */

  *telra=telra1;
  *teldec=teldec1;

  return;

}


/* Slew to new coordinates */
/* Returns 1 if goto command is sent successfully and 0 otherwise*/

int GoToCoords(double newra, double newdec, int pmodel)
{
  double telra0, telra1, teldec0, teldec1;
  int rahr, ramin, rasec;
  int decdeg, decmin, decsec;
  double dms, hms, ms;
  char rastr[32], decstr[32];
  
  /* Where do we really want to go to? */
  
  telra1 = newra;
  teldec1 = newdec;
  
  /* Where will the telescope point to make this happen? */
  
  PointingToTel(&telra0,&teldec0,telra1,teldec1,pmodel);
  
  /* Work with the telescope coordinates */
  /* We must send hh:mm:ss and dd:mm:ss */
  /* Allow for truncation if input is in seconds of arc */

  hms = telra0;
  hms = hms + 0.5/3600.;
  rahr = (int) hms;
  ms = hms - (double) rahr;
  ms = ms*60.;
  ramin = (int) ms;
  rasec = (int) 60.*(ms - (double) ramin);

   sprintf(rastr," %02dh%02dm%02ds",rahr,ramin,rasec);
 
  if(teldec0 >= 0.0)
  {
    dms = teldec0;
    dms = dms + 0.5/3600.;
    decdeg = (int) dms;
    ms = dms - (double) decdeg;
    ms = ms*60.;
    decmin = (int) ms;
    decsec = (int) 60.*(ms - (double) decmin);
    sprintf(decstr," +%02dd%02dm%02ds",decdeg,decmin,decsec);
  }
  else
  {
    dms = -teldec0;
    dms = dms + 0.5/3600.;
    decdeg = (int) dms;
    ms = dms - (double) decdeg;
    ms = ms*60.;
    decmin = (int) ms;
    decsec = (int) 60.*(ms - (double) decmin);
    sprintf(decstr," -%02dd%02dm%02ds",decdeg,decmin,decsec);
  }


  sprintf(buf,"SlewToRaDec,%s %s,", rastr, decstr);
  write_tsx(buf);

  /* Add a wait state to allow short slews to complete */
  
  usleep(4000000);

  return 1;
}


/* Test whether the destination was reached */
/* Return value is  */
/*   0 -- goto in progress */
/*   1 -- goto complete within tolerance */
/*   2 -- goto complete but outside tolerance */

int CheckGoTo(double desra, double desdec, int pmodel)
{
  double telra0, telra1, teldec0, teldec1;
  double errorra, errordec, nowra, nowdec;

  /* Where do we really want to go to? */
  
  telra1 = desra;
  teldec1 = desdec;
  
  /* Where will the telescope point to make this happen? */
  
  PointingToTel(&telra0,&teldec0,telra1,teldec1,pmodel);
  
  /* Work with the telescope coordinates */
  
  desra = telra0;
  desdec = teldec0;
  
  if ( GetSlewStatus() == 1 )
  {
    return (0);
  }
  
  GetTel(&nowra, &nowdec, pmodel);
  errorra = nowra - desra;
  errordec = nowdec - desdec;

  /* For 6 minute of arc precision; change as needed.  */

  if( fabs(errorra) > (0.1/15.) || fabs(errordec) > 0.1)
  {
    return (2);
  }
  else
  {
    return (1);
  }
}

/* Synchronize remote telescope to this ra dec pair */

void SyncTelOffsets(double newoffsetra, double newoffsetdec)
{
  offsetra = newoffsetra;
  offsetdec = newoffsetdec;
  return;
}

/* Functions not supported by TheSkyX scripting interface */

/* Return a flag indicating whether a slew is now in progress */

int GetSlewStatus(void)
{
  /* Always return a no-slew state */
  
  return (0);
}


/* Start sidereal tracking                                                    */

void StartTrack(void)
{
  return;
} 

/* Use high resolution encoder to improve tracking */

void CenterGuide(double centerra, double centerdec, 
  int raflag, int decflag, int pmodel)
{
}


/* Stop tracking if it is running */

void StopTrack(void)
{
  return;
}


/* Full stop */

void FullStop(void)

{  
  StopTrack();
  return;  
}

/* Set slew limits control off or on */

int SetLimits(int limits)
{
    
  return (0);
}


/* Get status of slew limits control */

int GetLimits(int *limits)
{

  *limits = 0; 

  return (0);
}
  

/* Set slew speed limit */

int SetSlewRate(int slewrate)
{
  return (0);
}


/* Control the dew and drive heaters */

void Heater(int heatercmd)
{
  char cmdstr[256];
  sprintf(cmdstr,"setheater %d 1>/dev/null 2>/dev/null", heatercmd);
  system(cmdstr);   
}

/* Control the telescope fans */

void Fan(int fancmd)
{
  char cmdstr[256];
  sprintf(cmdstr,"setfan %d 1>/dev/null 2>/dev/null", fancmd);
  system(cmdstr);    
}


/* Adjust the focus using an external routine */
/* The routine will time out on its own and report through the status file */

void Focus(int focuscmd, int focusspd)
{
  char cmdstr[256];
  sprintf(cmdstr,"setfocus %d %d  1>/dev/null 2>/dev/null", focuscmd, focusspd);
  system(cmdstr);  
}


/* Report the current focus reading from the status file */

void GetFocus(double *telfocus)
{
  int nread;
  double current_focus;
  char cmdstr[256];
  sprintf(cmdstr,"getfocus 1>/dev/null 2>/dev/null");
  system(cmdstr);  
  
  focusfile = (char*) malloc (MAXPATHLEN);
  strcpy(focusfile,FOCUSFILE);
  fp_focus = fopen(focusfile, "r");
  if (fp_focus == NULL)
  {
    return;
  }
  
  nread = fscanf(fp_focus, "%lg", &current_focus);
  fclose(fp_focus);
  
  if (nread !=0)
  {
    *telfocus = current_focus;
  }
  
  return;  
}


/* Adjust the rotation */

void Rotate(int rotatecmd, int rotatespd)
{
  char cmdstr[256];
  sprintf(cmdstr,"setrotate %d %d  1>/dev/null 2>/dev/null", rotatecmd, rotatespd);
  system(cmdstr);
}

/* Report the rotation setting */

void GetRotate(double *telrotate)
{
  int nread;
  double current_rotate;
  char cmdstr[256];
  sprintf(cmdstr,"getrotate 1>/dev/null 2>/dev/null");
  system(cmdstr);  
  
  rotatefile = (char*) malloc (MAXPATHLEN);
  strcpy(rotatefile,ROTATEFILE);
  fp_rotate = fopen(rotatefile, "r");
  if (fp_rotate == NULL)
  {
    *telrotate = 0.;
    return;
  }
  
  nread = fscanf(fp_rotate, "%lg", &current_rotate);
  fclose(fp_rotate);
  
  if (nread !=0)
  {
    *telrotate = current_rotate;
  }
  
  return;  
}

/* Read and report the temperature */

void GetTemperature(double *teltemperature)
{
  int nread;
  double current_temperature;
  char cmdstr[256];
  
  sprintf(cmdstr,"get_temperature  1>/dev/null 2>/dev/null");
  system(cmdstr);  
  
  temperaturefile = (char*) malloc (MAXPATHLEN);
  strcpy(temperaturefile,TEMPERATUREFILE);
  fp_temperature = fopen(temperaturefile, "r");
  if (fp_temperature == NULL)
  {
    return;
  }
  
  nread = fscanf(fp_temperature, "%lf", &current_temperature);
  fclose(fp_temperature);
  
  if (nread !=0)
  {
    *teltemperature = current_temperature;
  }
  
  return;   
}



/* Scripting */

/* Generic script writer */
/* Load the message with a line for a script */
/* If Orchestrate is running and connected this routine will communicate with TSX */

void write_tsx(char *script_message)
{
  
  /* TSX scripts must have 8 digits with leading zeros  */
  /* This simple implementation cylces through 9 unique script filenames */
  
  script_number++;
  if ( script_number > 9 )
  {
    script_number = 1;
  }  
  
  strcpy(script_dir,SCRIPTDIR);
  sprintf(script_filename, "%s/0000000%d.txt", script_dir, script_number);
  
  fp_script = fopen(script_filename,"w");
  fprintf(fp_script, "%s\n", script_message);      
  fclose(fp_script);
  
  sprintf(handshake_filename, "%s/HANDSHAKE", script_dir);
  fp_handshake = fopen(handshake_filename,"w");
  fprintf(fp_handshake, " ");
  fclose(fp_handshake);  
} 


/* Read the TSX status file */

void read_tsx_status(int *tsx_status, double *tsx_ra, double *tsx_dec)
{  
  double  current_tsx_ra, current_tsx_dec;
  int current_tsx_status;
  int nread;
  
  strcpy(status_dir,STATUSDIR);  
  sprintf(status_filename, "%s/telstatus", status_dir);
  fp_status = fopen(status_filename,"r");

  /* If the file does not exist that assume that TSX is not available */
  
  if (fp_status == NULL)
  {
    current_tsx_status = 0;
    current_tsx_ra = 0.0;
    current_tsx_dec = 0.0;
    *tsx_status = current_tsx_status;
    *tsx_ra = current_tsx_ra;
    *tsx_dec = current_tsx_dec;
    return;
  }

  nread = fscanf(fp_status, "%d %lf %lf", 
    &current_tsx_status, &current_tsx_ra, &current_tsx_dec);  
  fclose(fp_status);
  
  /* If the file is empty then assume that the telescope is not connected */
  
  if (nread == 0)
  {
    current_tsx_status = 0;
    current_tsx_ra = 0.0;
    current_tsx_dec = 0.0;
  }
  
  *tsx_status = current_tsx_status;
  *tsx_ra = current_tsx_ra;
  *tsx_dec = current_tsx_dec;

  return;

} 
