XEphem/GUI/xephem/tools/lx200xed/lx200xed.c

666 lines
17 KiB
C

/*
* liblx200 v0.8
* A library for controlling the Meade LX200 series scopes
*
* Copyright (C) 2000, Mike Stute
*/
#include <fcntl.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
/*Stuff needed by libastro*/
#include "astro.h"
#include "liblx200.h"
#define _POSIX_SOURCE 1
#define GOTOFIFO_NAME "/usr/local/xephem/fifos/xephem_loc_fifo"
#define MARKFIFO_NAME "/usr/local/xephem/fifos/xephem_in_fifo"
#define TEMPERATURE 10. /* air temp, C */
#define PRESSURE 1010. /* air pressure, mB */
#define SUNDIP degrad(18.) /* dusk, rads below horizon */
#define ELEVATION 0. /* elevation, m */
#define MINMOV 60 /* secs between moving objs updates */
#define UPDDEF 500 /* default graphical update period, ms*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define tracing config.iTrace
typedef int BOOLEAN;
/***** Predefines ****/
void usage(void);
void parse_opts(int, char **);
void die(char *, char *);
void initNow (int fd);
void replace(char, char, char *);
void telescope_goto(int, int, char);
int send_goto(int fd);
int open_fifo(char *, int, BOOLEAN);
void time_fromsys(Now *);
int tz_fromsys(Now *);
void trace(int, char *, char *);
/******* Globals *******/
static Obj op; /* Object for libastro, here due to laziness*/
static Now now; /* Now for libastro, here due to same laziness */
static BOOLEAN moving; /* TRUE if current object is not fixed*/
static char szMsg[101]; /* For trace*/
static int nohw; /* set by -e */
static int updper = UPDDEF; /* update period, ms */
static struct {
BOOLEAN fGoto; /*If TRUE then gotos okay, else don't do them*/
BOOLEAN fMarker; /*If TRUE then marker enabled*/
BOOLEAN fMoving; /*If TRUE then following moving objects*/
double dTemp; /*Temperature to use in C*/
double dDip; /*Sun dip (degress above horizon) in radians */
double dPressure; /*Pressure in mBars*/
double dElev; /*Elevation in meters*/
int iTrace; /*Trace level*/
char szFifo_goto_name[256];/*FIFO for telescope control (from xephem) */
char szFifo_mark_name[256];/*FIFO fir telescope marker (to xephem) */
char szTelDev[256]; /*Telescope device name*/
int minimum_move_time; /*How often goto is resent for moving object, sec*/
} config;
/*
* minimum_move_time determines how often to recenter the scope
* for a moving object. I have picked once a minute, but
* once every 3 or 5 is probably good too. The program pulses 3 times
* a second roughly and there's a little overhead due to scope
* communications for the marker, so I picked 57 seconds
* for the default
*/
int
main(int argc, char **argv)
{
int fd,fd_marker_fifo,fd_goto_fifo; /*File descriptors*/
unsigned char buf[101];
double ra,tmp;
int ra_hour, ra_min, ra_sec;
double dec;
int dec_deg, dec_min, dec_sec;
char szIn[5];
int iBytes;
long last_send_time = 0;
double ep;
/*Setup default config*/
strncpy(config.szFifo_goto_name, GOTOFIFO_NAME,255);
strncpy(config.szFifo_mark_name, MARKFIFO_NAME,255);
strncpy(config.szTelDev,TELESCOPE,255);
config.dTemp=TEMPERATURE;
config.dDip=SUNDIP;
config.dPressure=PRESSURE;
config.dElev=ELEVATION;
config.fMarker=TRUE;
config.fGoto=TRUE;
config.fMoving=TRUE;
config.minimum_move_time = MINMOV;
parse_opts(argc, argv);
/**************** Open out fifo *******************/
/* This is used for the telescope marker */
fd_marker_fifo = open_fifo(config.szFifo_mark_name,O_RDWR,TRUE);
/**************** Open in fifo ********************/
/* This is used to read for telescope gotos */
fd_goto_fifo = open_fifo(config.szFifo_goto_name,O_RDWR,FALSE);
/*************** Open the scope *******************/
/* This is, well, the telescope */
/**************************************************/
fd = lx200_open_scope(config.szTelDev);
if(fd==-1)
die(config.szTelDev, strerror(errno));
/*Set scope for long format*/
if(lx200_set_format(fd,LX200_OPT_LONG_FORMAT)==LX200_FALSE)
die("The scope is not responding", NULL);
/* set up global "now" for EOD */
initNow(fd);
while(1) {
/* Check for a goto */
if(config.fGoto) {
iBytes=0;
iBytes = read(fd_goto_fifo,szIn,1);
if(iBytes==1) {
if(tracing) {
szMsg[0]=szIn[0];
szMsg[1]='\0';
}
trace(1,"Reading GOTO %s\n",szMsg);
telescope_goto(fd_goto_fifo,fd,szIn[0]);
last_send_time = time(NULL);
}
if(moving && time(NULL) > last_send_time + config.minimum_move_time) {
send_goto(fd); /*Recenter a moving object*/
last_send_time = time(NULL);
} else {
usleep(updper*1000);
}
}
else
usleep(updper*1000);
if(config.fMarker) {
trace(7,"Sending get ra\n",NULL);
lx200_get_ra(fd,buf);
lx200_convert_RA(buf,&ra_hour,&ra_min,&ra_sec); /*Parse it*/
if(tracing) {
snprintf(szMsg,100,"%s RH: %d RM: %d RS: %d\n",buf,ra_hour,ra_min,ra_sec);
trace(8,szMsg,NULL);
}
/*Scan and convert it to radians*/
f_scansexa(buf,&tmp);
ra = nohw ? op.s_ra : hrrad(tmp);
lx200_get_dec(fd,buf);
lx200_convert_Dec(buf,&dec_deg,&dec_min,&dec_sec); /*Parse it*/
buf[3]=':';
if(tracing) {
snprintf(szMsg,100,"%s %d %d %d\n",buf,dec_deg,dec_min,dec_sec);
trace(8," %s\n",szMsg);
}
/*Scan it and convert to radians*/
f_scansexa(buf,&tmp);
dec = nohw ? op.s_dec : degrad(tmp);
time_fromsys( &now);
mjd_year( now.n_mjd, &ep);
snprintf(buf,sizeof(buf),"RA:%9.6f Dec:%9.6f Epoch:%13.8f\n",ra,dec,ep);
trace(7,"To XEphem: %s\n", buf);
write(fd_marker_fifo, buf, strlen(buf));
}
}
/*A clean exit*/
die(NULL,NULL);
}
/*
* Standard usage
*/
void
usage(void)
{
printf("Usage: lx200xed [options]\n");
printf("Purpose: allow XEphem to control a Meade LX200 telescope\n");
printf("Options:\n");
printf(" -e Turn on hardware emulation\n");
printf(" -m path alternate FIFO to XEphem for telescope marker\n");
printf(" default %s\n", MARKFIFO_NAME);
printf(" -g path alternate FIFO from XEphem for telescope GOTOs\n");
printf(" default %s\n", GOTOFIFO_NAME);
printf(" -t path device for telescope; default %s\n", TELESCOPE);
printf(" -p number air pressure, mbars; default %g\n", PRESSURE);
printf(" -c temp air temperature, Celius; default %g\n", TEMPERATURE);
printf(" -d dip sun angle down at end of twilight; default %g\n",
raddeg(SUNDIP));
printf(" -h elev elevation in meters; default %g\n", ELEVATION);
printf(" -l lat use this latitude, not scope's, D:M:S, +N\n");
printf(" -L lng use this longitude, not scope's, D:M:S, +E\n");
printf(" -N turn off GOTO control\n");
printf(" -M turn off telescope marker\n");
printf(" -A turn off automatic follow of moving objects\n");
printf(" -i secs interval betweens moves for automatic follow, secs; default %d\n", MINMOV);
printf(" -u ms graphical update period, ms; default %d\n", UPDDEF);
printf(" -x level set debug level to level\n");
printf(" -v print version and exit\n");
printf(" -<other> this schpeel\n");
die(NULL,NULL);
}
static void
prVersion(void)
{
char buf[128];
lx200_get_lib_version(buf);
printf("lx200xed $Revision: 1.18 $\n");
printf("liblx200 %s\n", buf);
}
/*
* Handle command line options
*/
void
parse_opts(int argc, char **argv)
{
char cOption;
double tmp;
trace(6,"In parse_opts...\n",NULL);
while (1) {
cOption=getopt(argc,argv,"l:L:ep:d:m:g:t:NMAx:u:i:h:vc:");
if(cOption==EOF)
break;
switch(cOption) {
case 'v':
prVersion();
die(NULL,NULL);
break;
case 'e': /*set liblx200 to emulate hardware*/
lx200_set_lib_emulate(LX200_TRUE,LX200_EMULATE_TRUE);
nohw = 1;
break;
case 'm': /*Override the default marker FIFO*/
strncpy(config.szFifo_mark_name,optarg,255);
break;
case 'l': /* new latitude D:M:S +N */
f_scansexa(optarg,&tmp);
now.n_lat = degrad(tmp);
break;
case 'L': /* new longitude D:M:S +W */
f_scansexa(optarg,&tmp);
now.n_lng = degrad(-tmp);
break;
case 'g': /*Override the default goto FIFO*/
strncpy(config.szFifo_goto_name,optarg,255);
break;
case 't': /*Override the default telescope device*/
strncpy(config.szTelDev,optarg,255);
break;
case 'p': /*Set the pressure*/
config.dPressure=strtod(optarg,NULL);
break;
case 'd': /*Set the dip*/
config.dDip=strtod(optarg,NULL);
if(!errno)
config.dDip=degrad(config.dDip);
break;
case 'c': /*Set the temperature*/
config.dTemp=strtod(optarg,NULL);
break;
case 'h': /*Set the elevation*/
config.dElev=strtod(optarg,NULL);
break;
case 'N': /*No goto control*/
config.fGoto=FALSE;
break;
case 'M': /*No marker control*/
config.fMarker=FALSE;
break;
case 'A': /*No automatic following of moving objects*/
config.fMoving=FALSE;
break;
case 'i':
config.minimum_move_time=atoi(optarg);
if(errno) {
printf("Invalid number of seconds for interval, setting to %d\n",
MINMOV);
config.minimum_move_time= MINMOV;
}
break;
case 'x': /*Debug level*/
config.iTrace=(int)strtol(optarg,NULL,10);
if(errno) {
printf("Invalid trace level, setting to no trace\n");
config.iTrace=0;
}
break;
case 'u':
updper = strtol (optarg, NULL, 10);
break;
default:
usage();
break;
}
}
}
/*
* die cleanly, possibly with a message and argument
*/
void
die(char *szMsg, char *szArg)
{
trace(6,"In die...\n",NULL);
/*
if(fd!=-1) {
lx200_close_scope(fd);
close(fd_goto_fifo);
close(fd_marker_fifo);
}*/
if(szMsg!=NULL && szArg!=NULL)
printf("%s: %s\n",szMsg, szArg);
else
if(szMsg!=NULL)
printf("%s\n",szMsg);
trace(1,"lx200xed exiting\n",NULL);
exit(0);
}
/* init now -- all but mjd, that gets set as needed.
* Get the latitude and longitude from scope if not already known.
*/
void
initNow (int fd)
{
if (!now.n_lng && !now.n_lat) {
char szLat[21],szLong[21];
double tmp;
lx200_get_latitude(fd,szLat);
if(tracing) trace(4,"Lat from scope: '%s' ",szLat);
szLat[3] = ':';
strcat(szLat,":00");
if(tracing) trace(4,"becomes '%s'\n",szLat);
lx200_get_longitude(fd,szLong);
if(tracing) trace(4,"Lng from scope: '%s' ",szLong);
szLong[3] = ':';
strcat(szLong,":00");
if(tracing) trace(4,"becomes '%s'\n",szLong);
f_scansexa(szLong,&tmp);
now.n_lng = degrad(-tmp);
f_scansexa(szLat,&tmp);
now.n_lat = degrad(tmp);
}
now.n_elev=config.dElev/ERAD;
now.n_dip=config.dDip;
now.n_epoch=J2000;
now.n_temp=config.dTemp;
now.n_pressure=config.dPressure;
}
/*
* Replace one character in a string with another
*/
void
replace(char cFrom, char cTo, char *szp)
{
char *c=szp;
trace(6,"In replace...",NULL);
while(*c++!='\0')
if(*c==cFrom)
*c=cTo;
}
/*
* Telescope GOTO
* Reads the in fifo to get the object information
* Calls db_crack_line from libastro
* sets the moving flag and then calls send_goto
* to calculate the position and move the scope
*/
void
telescope_goto(int fd_in, int fd_out, char c)
{
int iBytes,iRead=1;
char sz[121],szIn[2],szMsg[100];
trace(6,"In telescope_goto...",NULL);
sz[0]=c;
trace(1,"Telescope goto\n",NULL);
while(1) {
trace(5,"Reading a byte\n",NULL);
iBytes = read(fd_in,szIn,1);
if(tracing) {
szMsg[0]=c;
szMsg[1]='\0';
trace(6,"Read c=%.1s\n",szIn);
}
if(iBytes==-1) {
sz[iRead]='\0';
trace(5,"Received: %s\n",sz);
break;
}
if(iRead<120)
sz[iRead++]=szIn[0];
}
trace(4,"Cracking line:%s\n", sz);
if(db_crack_line(sz,&op,NULL,0,szMsg)<0) {
trace(4,"Couldn't crack edb info:%s\n", szMsg);
return;
}
if(op.o_type ==FIXED)
moving=FALSE;
else
moving=config.fMoving;
if(tracing) {
trace(4,"Cracked edb line for %s",op.o_name);
trace(4," %s\n",moving ? "Moving" : "Fixed" );
}
send_goto(fd_out);
}
/*
* send_goto
* This sends a goto by calling obj_cir
* from libastro to get current information,
* and then sending the goto to the scope.
*
* op should contain a valid object before this call.
*/
int
send_goto(int fd)
{
char szRa[20], szDec[20];
trace(6,"In send_goto...",NULL);
if(tracing) {
char szEp[10];
double tmp;
mjd_year (op.f_epoch, &tmp);
sprintf (szEp, "%8.3f", tmp);
fs_sexa(szRa,radhr(op.f_RA),2,3600);
fs_sexa(szDec,raddeg(op.f_dec),3,3600);
trace(5,"From XEphem RA=%s",szRa);
trace(5," Dec=%s",szDec);
trace(5," Ep=%s\n", szEp);
}
/*And finally calculate the emphererid for the object*/
trace(3,"Calling obj_cir\n",NULL);
time_fromsys(&now);
if(obj_cir(&now,&op)<0) {
trace(4,"obj_cir failed\n",NULL);
return LX200_FALSE;
}
trace(3,"Formatting scope goto values\n",NULL);
fs_sexa(szRa,radhr(op.s_ra),2,3600);
/* this gives always create 4 chars left of first :
* no zero pad, no explicit +
*/
fs_sexa(szDec,raddeg(op.s_dec),4,3600);
if(tracing) {
trace(5,"EOD RA=%s",szRa);
trace(5," Dec=%s\n",szDec);
}
/*Leading zeros are required by the LX200*/
/*RA is easy*/
if(szRa[0]==' ')
szRa[0]='0';
/*Deal with sign, but liblx200 accepts : or 223 for degree sign*/
/*However, this is a little harder.
* FS requires exactly "Sd SDD:MM:SS", we build " SDD:MM:SS" portion here.
*/
/* always leave szDec[0] a blank */
if (szDec[2] == ' ' || szDec[2] == '-') {
/* zero-pad degrees to 2 places */
szDec[1] = szDec[2];
szDec[2] = '0';
}
if (szDec[1] != '-') {
/* explicit + before positive deg */
szDec[1] = '+';
}
if(tracing) {
trace(5,"Real: RA %s ,",szRa);
trace(5," Dec: %s\n",szDec);
}
return(lx200_goto_RADec(fd,szRa,szDec));
}
/*
* Open a FIFO using mode "mode"
* If blocking is false, set the stream
* to block
*/
int
open_fifo(char *szName, int mode, BOOLEAN blocking)
{
struct stat fbuf;
int fd;
trace(6,"In send_goto...",NULL);
if (stat(szName, &fbuf) == -1) {
die(szName,strerror(errno));
}
else if (!S_ISFIFO(fbuf.st_mode))
{
die(szName, "not a fifo");
}
trace(4,"Open the FIFO\n",NULL);
if (!blocking)
mode |= O_NONBLOCK;
fd = open(szName,mode);
if(fd<0) {
die(szName, strerror(errno));
}
return(fd);
}
/*
* Shamelessly ripped from E.C. Downey's XEphem
* Gets time from system
*/
void
time_fromsys (Now *np)
{
#if defined(__STDC__)
time_t t;
#else
long t;
#endif
trace(6,"In time_fromsys...\n",NULL);
t = time(NULL);
/* t is seconds since 00:00:00 1/1/1970 UTC on UNIX systems;
* mjd was 25567.5 then.
*/
#if defined(VMS) && (__VMS_VER < 70000000)
/* VMS returns t in seconds since 00:00:00 1/1/1970 Local Time
* so we need to add the timezone offset to get UTC.
* Don't need to worry about 'set_t0' and 'inc_mjd' because
* they only deal in relative times.
* this change courtesy Stephen Hirsch <oahirsch@southpower.co.nz>
* - OpenVMS V7.0 finally has gmt support! so use standard time
* - algorithm Vance Haemmerle <vance@toyvax.Tucson.AZ.US>
*/
mjd = (25567.5 + t/3600.0/24.0) + (tz/24.0);
#else
mjd = 25567.5 + t/3600.0/24.0;
#endif
snprintf(szMsg,100,"TFS: %f\n",mjd);
trace(5,szMsg,NULL);
(void) tz_fromsys(np);
if(tracing) {
snprintf(szMsg,100,"TFS2 MJD: %f TZ=%f TZN=%s\n",mjd,tz,tznm);
trace(5,szMsg,NULL);
}
}
/* given the mjd within np, try to figure the timezone from the os.
* return 0 if it looks like it worked, else -1.
*
* Shamelessly ripped from E.C. Downey's XEphem
*/
int
tz_fromsys (Now *np)
{
struct tm *gtmp;
time_t t;
trace(6,"In tz_fromsys...",NULL);
t = (time_t)((mjd - 25567.5) * (3600.0*24.0) + 0.5);
/* try to find out timezone by comparing local with UTC time.
* GNU doesn't have difftime() so we do time math with doubles.
*/
gtmp = gmtime (&t);
if (gtmp) {
double gmkt, lmkt;
struct tm *ltmp;
gtmp->tm_isdst = 0; /* _should_ always be 0 already */
gmkt = (double) mktime (gtmp);
ltmp = localtime (&t);
ltmp->tm_isdst = 0; /* let mktime() figure out zone */
lmkt = (double) mktime (ltmp);
tz = (gmkt - lmkt) / 3600.0;
(void) strftime (tznm, sizeof(tznm)-1, "%Z", ltmp);
return (0);
} else
return (-1);
}
/*
* General debug printer
* The higher iLevel the more information
* you get
*/
void
trace(int iLevel, char *szpFormat, char *szpArg)
{
if(iLevel<=config.iTrace) {
if(szpArg==NULL)
printf(szpFormat);
else
printf(szpFormat,szpArg);
}
}