/***************************************************************************\ * MoonTracker: A Moon AutoTracking Utility * * Created by John A. Magliacane, KD2BD, on 29-Nov-2002 * * Last update: 06-Apr-2006 * ***************************************************************************** * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License or any later * * version. * * * * This program 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 * * General Public License for more details. * * * \***************************************************************************/ #include #include #include #include #include #include #include #include #include #include char callsign[20], output[20]; int qthalt, pid; double qthlat, qthlong, moon_az, moon_el, moonrise, PI=3.141592653589793, TWOPI=6.28318530717958623, deg2rad=1.745329251994330e-2; double FixAngle(x) double x; { /* This function reduces angles greater than two pi by subtracting two pi from the angle */ while (x>TWOPI) x-=TWOPI; return x; } double PrimeAngle(x) double x; { x=x-360.0*floor(x/360.0); return x; } double CurrentDaynum() { /* Read the system clock and return the number of days since 31Dec79 00:00:00 UTC (daynum 0) */ struct timeval tptr; int x; x=gettimeofday(&tptr,NULL); return ((((double)tptr.tv_sec+0.000001*(double)tptr.tv_usec)/86400.0)-3651.0); } char *Daynum2String(daynum) double daynum; { /* This function takes the given epoch as a fractional number of days since 31Dec79 00:00:00 UTC and returns the corresponding date as a string of the form "Tue 12Oct99 17:22:37". */ char timestr[25]; time_t t; int x; /* Convert daynum to Unix time (seconds since 01-Jan-70) */ t=(time_t)rint(86400.0*(daynum+3651.0)); sprintf(timestr,"%s",asctime(gmtime(&t))); if (timestr[8]==' ') timestr[8]='0'; for (x=0; x<=3; output[x]=timestr[x], x++); output[4]=timestr[8]; output[5]=timestr[9]; output[6]=timestr[4]; output[7]=timestr[5]; output[8]=timestr[6]; output[9]=timestr[22]; output[10]=timestr[23]; output[11]=' '; for (x=12; x<=19; output[x]=timestr[x-1], x++); output[20]=0; return output; } char ReadQTH(qthfile) char *qthfile; { /* This function reads the tracking station's latitude and longitude information from PREDICT's predict.qth file. */ FILE *fd; char success=0; fd=fopen(qthfile,"r"); if (fd!=NULL) { fgets(callsign,16,fd); callsign[strlen(callsign)-1]=0; fscanf(fd,"%lf", &qthlat); fscanf(fd,"%lf", &qthlong); fscanf(fd,"%d", &qthalt); fclose(fd); success=1; } return success; } void TrackDataOut(antfd, elevation, azimuth) int antfd; double elevation, azimuth; { /* This function sends Azimuth and Elevation data to an antenna tracker connected to the serial port */ int n, port; char message[30]="\n"; port=antfd; sprintf(message, "AZ%3.1f EL%3.1f \x0D\x0A", azimuth,elevation); n=write(port,message,30); if (n<0) { fprintf(stderr,"%c*** Error: Problem Writing To Antenna Port!\n",7); kill(pid,1); exit(-1); } } void FindMoon(daynum) double daynum; { /* This function determines the azimuth and elevation headings for the moon, relative to the latitude and longitude of the tracking station. This code was derived from a Javascript implementation of the Meeus method for determining the exact position of the Moon found at: http://www.geocities.com/s_perona/ingles/poslun.htm. */ double jd, s, t, t2, t3, d, f, l1, m, m1, ex, om, l, b, w1, w2, bt, p, lm, h, ra, dec, z, ob, n, e, el, az, teg, th; jd=daynum+2444238.5; t=(jd-2415020.0)/36525.0; t2=t*t; t3=t2*t; l1=270.434164+481267.8831*t-0.001133*t2+0.0000019*t3; m=358.475833+35999.0498*t-0.00015*t2-0.0000033*t3; m1=296.104608+477198.8491*t+0.009192*t2+0.0000144*t3; d=350.737486+445267.1142*t-0.001436*t2+0.0000019*t3; f=11.250889+483202.0251*t-0.003211*t2-0.0000003*t3; om=259.183275-1934.142*t+0.002078*t2+0.0000022*t3; om=om*deg2rad; /* Additive terms */ l1=l1+0.000233*sin((51.2+20.2*t)*deg2rad); s=0.003964*sin((346.56+132.87*t-0.0091731*t2)*deg2rad); l1=l1+s+0.001964*sin(om); m=m-0.001778*sin((51.2+20.2*t)*deg2rad); m1=m1+0.000817*sin((51.2+20.2*t)*deg2rad); m1=m1+s+0.002541*sin(om); d=d+0.002011*sin((51.2+20.2*t)*deg2rad); d=d+s+0.001964*sin(om); f=f+s-0.024691*sin(om); f=f-0.004328*sin(om+(275.05-2.3*t)*deg2rad); ex=1.0-0.002495*t-0.00000752*t2; om=om*deg2rad; l1=PrimeAngle(l1); m=PrimeAngle(m); m1=PrimeAngle(m1); d=PrimeAngle(d); f=PrimeAngle(f); om=PrimeAngle(om); m=m*deg2rad; m1=m1*deg2rad; d=d*deg2rad; f=f*deg2rad; /* Ecliptic Longitude */ l=l1+6.28875*sin(m1)+1.274018*sin(2.0*d-m1)+0.658309*sin(2.0*d); l=l+0.213616*sin(2.0*m1)-ex*0.185596*sin(m)-0.114336*sin(2.0*f); l=l+0.058793*sin(2.0*d-2.0*m1)+ex*0.057212*sin(2.0*d-m-m1)+0.05332*sin(2.0*d+m1); l=l+ex*0.045874*sin(2.0*d-m)+ex*0.041024*sin(m1-m)-0.034718*sin(d); l=l-ex*0.030465*sin(m+m1)+0.015326*sin(2.0*d-2.0*f)-0.012528*sin(2.0*f+m1); l=l-0.01098*sin(2.0*f-m1)+0.010674*sin(4.0*d-m1)+0.010034*sin(3.0*m1); l=l+0.008548*sin(4.0*d-2.0*m1)-ex*0.00791*sin(m-m1+2.0*d)-ex*0.006783*sin(2.0*d+m); l=l+0.005162*sin(m1-d)+ex*0.005*sin(m+d)+ex*0.004049*sin(m1-m+2.0*d); l=l+0.003996*sin(2.0*m1+2.0*d)+0.003862*sin(4.0*d)+0.003665*sin(2.0*d-3.0*m1); l=l+ex*0.002695*sin(2.0*m1-m)+0.002602*sin(m1-2.0*f-2.0*d)+ex*0.002396*sin(2.0*d-m-2.0*m1); l=l-0.002349*sin(m1+d)+ex*ex*0.002249*sin(2.0*d-2.0*m)-ex*0.002125*sin(2.0*m1+m); l=l-ex*ex*0.002079*sin(2.0*m)+ex*ex*0.002059*sin(2.0*d-m1-2.0*m)-0.001773*sin(m1+2.0*d-2.0*f); l=l+ex*0.00122*sin(4.0*d-m-m1)-0.00111*sin(2.0*m1+2.0*f)+0.000892*sin(m1-3.0*d); l=l-ex*0.000811*sin(m+m1+2.0*d)+ex*0.000761*sin(4.0*d-m-2.0*m1)+ex*ex*.000717*sin(m1-2.0*m); l=l+ex*ex*0.000704*sin(m1-2.0*m-2.0*d)+ex*0.000693*sin(m-2.0*m1+2.0*d)+ex*0.000598*sin(2.0*d-m-2.0*f)+0.00055*sin(m1+4.0*d); l=l+0.000538*sin(4.0*m1)+ex*0.000521*sin(4.0*d-m)+0.000486*sin(2.0*m1-d); l=l-0.001595*sin(2.0*f+2.0*d); /* Ecliptic latitude */ b=5.128189*sin(f)+0.280606*sin(m1+f)+0.277693*sin(m1-f)+0.173238*sin(2.0*d-f); b=b+0.055413*sin(2.0*d+f-m1)+0.046272*sin(2.0*d-f-m1)+0.032573*sin(2.0*d+f); b=b+0.017198*sin(2.0*m1+f)+9.266999e-03*sin(2.0*d+m1-f)+0.008823*sin(2.0*m1-f); b=b+ex*0.008247*sin(2.0*d-m-f)+0.004323*sin(2.0*d-f-2.0*m1)+0.0042*sin(2.0*d+f+m1); b=b+ex*0.003372*sin(f-m-2.0*d)+ex*0.002472*sin(2.0*d+f-m-m1)+ex*0.002222*sin(2.0*d+f-m); b=b+0.002072*sin(2.0*d-f-m-m1)+ex*0.001877*sin(f-m+m1)+0.001828*sin(4.0*d-f-m1); b=b-ex*0.001803*sin(f+m)-0.00175*sin(3.0*f)+ex*0.00157*sin(m1-m-f)-0.001487*sin(f+d)-ex*0.001481*sin(f+m+m1)+ex*0.001417*sin(f-m-m1)+ex*0.00135*sin(f-m)+0.00133*sin(f-d); b=b+0.001106*sin(f+3.0*m1)+0.00102*sin(4.0*d-f)+0.000833*sin(f+4.0*d-m1); b=b+0.000781*sin(m1-3.0*f)+0.00067*sin(f+4.0*d-2.0*m1)+0.000606*sin(2.0*d-3.0*f); b=b+0.000597*sin(2.0*d+2.0*m1-f)+ex*0.000492*sin(2.0*d+m1-m-f)+0.00045*sin(2.0*m1-f-2.0*d); b=b+0.000439*sin(3.0*m1-f)+0.000423*sin(f+2.0*d+2.0*m1)+0.000422*sin(2.0*d-f-3.0*m1); b=b-ex*0.000367*sin(m+f+2.0*d-m1)-ex*0.000353*sin(m+f+2.0*d)+0.000331*sin(f+4.0*d); b=b+ex*0.000317*sin(2.0*d+f-m+m1)+ex*ex*0.000306*sin(2.0*d-2.0*m-f)-0.000283*sin(m1+3.0*f); w1=0.0004664*cos(om*deg2rad); w2=0.0000754*cos((om+275.05-2.3*t)*deg2rad); bt=b*(1.0-w1-w2); /* Parallax calculations */ p=0.950724+0.051818*cos(m1)+0.009531*cos(2.0*d-m1)+0.007843*cos(2.0*d)+0.002824*cos(2.0*m1)+0.000857*cos(2.0*d+m1)+ex*0.000533*cos(2.0*d-m)+ex*0.000401*cos(2.0*d-m-m1); p=p+0.000173*cos(3.0*m1)+0.000167*cos(4.0*d-m1)-ex*0.000111*cos(m)+0.000103*cos(4.0*d-2.0*m1)-0.000084*cos(2.0*m1-2.0*d)-ex*0.000083*cos(2.0*d+m)+0.000079*cos(2.0*d+2.0*m1); p=p+0.000072*cos(4.0*d)+ex*0.000064*cos(2.0*d-m+m1)-ex*0.000063*cos(2.0*d+m-m1); p=p+ex*0.000041*cos(m+d)+ex*0.000035*cos(2.0*m1-m)-0.000033*cos(3.0*m1-2.0*d); p=p-0.00003*cos(m1+d)-0.000029*cos(2.0*f-2.0*d)-ex*0.000029*cos(2.0*m1+m); p=p+ex*ex*0.000026*cos(2.0*d-2.0*m)-0.000023*cos(2.0*f-2.0*d+m1)+ex*0.000019*cos(4.0*d-m-m1); b=bt*deg2rad; lm=l*deg2rad; /* Convert ecliptic coordinates to equatorial coordinates */ z=(jd-2415020.5)/365.2422; ob=23.452294-(0.46845*z+5.9e-07*z*z)/3600.0; ob=ob*deg2rad; dec=asin(sin(b)*cos(ob)+cos(b)*sin(ob)*sin(lm)); ra=acos(cos(b)*cos(lm)/cos(dec)); if (lm>PI) ra=TWOPI-ra; /* ra = right ascension */ /* dec = declination */ n=qthlat*deg2rad; /* North latitude of tracking station */ e=-qthlong*deg2rad; /* East longitude of tracking station */ /* Find siderial time in radians */ t=(jd-2451545.0)/36525.0; teg=280.46061837+360.98564736629*(jd-2451545.0)+(0.000387933*t-t*t/38710000.0)*t; while (teg>360.0) teg-=360.0; th=FixAngle((teg-qthlong)*deg2rad); h=th-ra; /* Determine local coordinates */ az=atan2(sin(h),cos(h)*sin(n)-tan(dec)*cos(n))+PI; el=asin(sin(n)*sin(dec)+cos(n)*cos(dec)*cos(h)); moon_az=az/deg2rad; moon_el=el/deg2rad; } double FindMoonRise() { /* This function finds the daynum of moonrise. */ double daynum2; daynum2=CurrentDaynum(); FindMoon(daynum2); moonrise=0.0; while (moonrise==0.0) { if (fabs(moon_el)<0.03) moonrise=daynum2; else { daynum2-=(0.003*moon_el); FindMoon(daynum2); } } return moonrise; } int main(argc,argv) char argc, *argv[]; { char qthfile[80], serial_port[15], once_per_second=0, *env=NULL; int x, y, z, antfd=-1, iel, iaz, oldaz=0, oldel=0; unsigned sleeptime=0; double daynum; struct termios oldtty, newtty; if (argc==1) { fprintf(stderr,"\n\t\t --==[ MoonTracker v1.1 by KD2BD ]==--\n"); fprintf(stderr,"\n\t\t\t ...Available options...\n\n"); fprintf(stderr,"\t-a serial port (/dev/ttyS0) -- sends data as it changes\n"); fprintf(stderr,"\t-a1 serial port (/dev/ttyS0) -- sends data at one-second intervals\n"); fprintf(stderr,"\t-q alternate qth file (site.qth)\n"); fprintf(stderr,"\nOnce started, MoonTracker spawns itself as a background process.\n\n"); exit(0); } y=argc-1; serial_port[0]=0; env=getenv("HOME"); sprintf(qthfile,"%s/.predict/predict.qth",env); /* Interpret command-line options */ for (x=1; x<=y; x++) { if (strcmp(argv[x],"-q")==0) { z=x+1; if (z<=y && argv[z][0] && argv[z][0]!='-') strncpy(qthfile,argv[z],78); } if (strcmp(argv[x],"-a")==0) { z=x+1; if (z<=y && argv[z][0] && argv[z][0]!='-') strncpy(serial_port,argv[z],13); } if (strcmp(argv[x],"-a1")==0) { z=x+1; if (z<=y && argv[z][0] && argv[z][0]!='-') { strncpy(serial_port,argv[z],13); once_per_second=1; } } } if (serial_port[0]==0) { fprintf(stderr,"%c*** Error: Serial port not specified!\n",7); exit(-1); } antfd=open(serial_port, O_RDWR|O_NOCTTY); if (antfd!=-1) { /* Set up serial port */ tcgetattr(antfd, &oldtty); bzero(&newtty, sizeof(newtty)); /* 9600 baud, 8-bits, no parity, 1-stop bit, no handshaking */ newtty.c_cflag=B9600|CS8|CLOCAL; newtty.c_oflag=0; newtty.c_lflag=0; tcflush(antfd, TCIFLUSH); tcsetattr(antfd, TCSANOW, &newtty); } else { fprintf(stderr, "%c*** Error: Unable To Open Antenna Port\n",7); exit(-1); } if (ReadQTH(qthfile)==0) { fprintf(stderr,"%c*** Error: QTH file \"%s\" could not be loaded!\n",7,qthfile); exit(-1); } moonrise=FindMoonRise(); daynum=CurrentDaynum(); if (!(pid=fork())) { while (1) { daynum=CurrentDaynum(); FindMoon(daynum); if (moon_el<0.0) { /* Go to sleep until moon rise. */ moonrise=FindMoonRise(); if (daynum=0.0 && antfd!=-1) { iaz=(int)rint(moon_az); iel=(int)rint(moon_el); if ((oldel!=iel || oldaz!=iaz) || (once_per_second)) { TrackDataOut(antfd,(float)iel,(float)iaz); oldel=iel; oldaz=iaz; } } if (once_per_second) sleep (1); else sleep (60); } } else { fprintf(stderr,"MoonTracker (pid %d) running on %s!\n",pid,serial_port); if (daynum