From f16f85fd92e1a092a42f6f250894fe39b9551740 Mon Sep 17 00:00:00 2001 From: Cees Bassa Date: Sat, 18 May 2013 18:54:11 +0100 Subject: [PATCH] first commit of code --- addwcs.c | 826 +++++++++++++++++ deep.c | 924 +++++++++++++++++++ dsmin.c | 124 +++ ferror.c | 29 + fitsheader.c | 63 ++ fitskey.c | 37 + makefile | 72 ++ pgm2fits.c | 594 +++++++++++++ plotfits.c | 726 +++++++++++++++ rde2iod.c | 151 ++++ reduce.c | 1243 ++++++++++++++++++++++++++ residuals.c | 526 +++++++++++ runsched.c | 155 ++++ satfit.c | 1202 +++++++++++++++++++++++++ satid.c | 666 ++++++++++++++ satmap.c | 869 ++++++++++++++++++ satorbit.c | 1066 ++++++++++++++++++++++ satutl.c | 221 +++++ satutl.h | 30 + sgdp4.c | 828 +++++++++++++++++ sgdp4h.h | 360 ++++++++ simplex.c | 28 + skymap.c | 2423 ++++++++++++++++++++++++++++++++++++++++++++++++++ tleinfo.c | 156 ++++ uk2iod.c | 163 ++++ versafit.c | 168 ++++ viewer.c | 244 +++++ wcsfit.c | 431 +++++++++ 28 files changed, 14325 insertions(+) create mode 100644 addwcs.c create mode 100644 deep.c create mode 100644 dsmin.c create mode 100644 ferror.c create mode 100644 fitsheader.c create mode 100644 fitskey.c create mode 100644 makefile create mode 100644 pgm2fits.c create mode 100644 plotfits.c create mode 100644 rde2iod.c create mode 100644 reduce.c create mode 100644 residuals.c create mode 100644 runsched.c create mode 100644 satfit.c create mode 100644 satid.c create mode 100644 satmap.c create mode 100644 satorbit.c create mode 100644 satutl.c create mode 100644 satutl.h create mode 100644 sgdp4.c create mode 100644 sgdp4h.h create mode 100644 simplex.c create mode 100644 skymap.c create mode 100644 tleinfo.c create mode 100644 uk2iod.c create mode 100644 versafit.c create mode 100644 viewer.c create mode 100644 wcsfit.c diff --git a/addwcs.c b/addwcs.c new file mode 100644 index 0000000..ceb234a --- /dev/null +++ b/addwcs.c @@ -0,0 +1,826 @@ +#include +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" +#include +#include + +#define NMAX 2048 +#define LIM 128 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI + +struct image { + int naxis1,naxis2,nframes; + float *zavg,*zstd,*zmax,*znum; + double ra0,de0; + float x0,y0; + float a[2],b[2]; + double mjd; + float *dt; +}; +struct transformation { + double mjd; + double ra0,de0; + float a[3],b[3]; + float x0,y0; + float xrms,yrms,rms; +}; +struct star { + double ra,de; + float pmra,pmde; + float mag; +}; +struct catalog { + int n; + float x[NMAX],y[NMAX],imag[NMAX],fm[NMAX],fb[NMAX],bg[NMAX]; + double ra[NMAX],de[NMAX],vmag[NMAX]; + double rx[NMAX],ry[NMAX]; + float xres[NMAX],yres[NMAX],res[NMAX]; + int usage[NMAX]; +}; +struct image read_fits(char *filename); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); +void reverse(double ra0,double de0,double x,double y,double *ra,double *de); +double gmst(double mjd); +double modulo(double x,double y); +int fgetline(FILE *file,char *s,int lim); +struct catalog match_catalogs(char *pixcat,char *astcat,struct transformation t,struct image img,float rmax,float mmin); +void plot_astrometric_catalog(struct transformation t,struct image img,float mmin); +void plot_pixel_catalog(char *filename); +void lfit2d(float *x,float *y,float *z,int n,float *a); +void add_fits_keywords(struct transformation t,char *filename); +void modify_fits_keywords(struct transformation t,char *filename); + +void plot_image(struct image img,struct transformation t,struct catalog c,char *filename,float mmin) +{ + int i; + float tr[]={-0.5,1.0,0.0,-0.5,0.0,1.0}; + float heat_l[]={0.0,0.2,0.4,0.6,1.0}; + float heat_r[]={0.0,0.5,1.0,1.0,1.0}; + float heat_g[]={0.0,0.0,0.5,1.0,1.0}; + float heat_b[]={0.0,0.0,0.0,0.3,1.0}; + float zmin,zmax,zavg,zstd; + + for (i=0,zavg=0.0;i: FITS file to add/fit WCS to [required]\n"); + printf("-r : FITS file with reference WCS [required]\n"); + printf("-m : Magnitude cut-off in Tycho-2 catalog [optional; default %.1f]\n",mmin); + printf("-R : Radius cut-off for matching [optional; default %.1f pix]\n",rmin); + printf("-p Plot image and selected stars [optional]\n"); + printf("-a Add WCS keywords to input file (instead of modify) [optional]\n"); + printf("-t Track on a fixed RA/Dec (correct for field rotation)\n"); + printf("-h Print this help\n"); + + return; +} + +// Get reference transformation +struct transformation reference(char *filename) +{ + struct transformation t; + + t.mjd=atof(qfits_query_hdr(filename,"MJD-OBS")); + t.ra0=atof(qfits_query_hdr(filename,"CRVAL1")); + t.de0=atof(qfits_query_hdr(filename,"CRVAL2")); + t.x0=atof(qfits_query_hdr(filename,"CRPIX1")); + t.y0=atof(qfits_query_hdr(filename,"CRPIX2")); + t.a[0]=0.0; + t.a[1]=3600.0*atof(qfits_query_hdr(filename,"CD1_1")); + t.a[2]=3600.0*atof(qfits_query_hdr(filename,"CD1_2")); + t.b[0]=0.0; + t.b[1]=3600.0*atof(qfits_query_hdr(filename,"CD2_1")); + t.b[2]=3600.0*atof(qfits_query_hdr(filename,"CD2_2")); + + return t; +} + +void rotate(float theta,float *x,float *y) +{ + float ct,st; + float x0,y0; + + ct=cos(theta*D2R); + st=sin(theta*D2R); + x0= *x; + y0= *y; + + *x=ct*x0-st*y0; + *y=st*x0+ct*y0; + + return; +} + +int main(int argc,char *argv[]) +{ + int i,j,k,l,m; + struct transformation t; + struct image img; + char *fitsfile=NULL,*reffile=NULL,catfile[128],calfile[128]; + FILE *outfile; + struct catalog c; + float mmin=10.0,rmin=10.0; + double ra0,de0; + float q0,q1; + float rmsmin; + float x[NMAX],y[NMAX],rx[NMAX],ry[NMAX]; + int arg=0,plot=0,add=0,track=0; + char *env,starfile[128]; + + // Environment variables + env=getenv("ST_DATADIR"); + sprintf(starfile,"%s/data/tycho2.dat",env); + + // Decode options + if (argc>1) { + while ((arg=getopt(argc,argv,"f:r:m:R:hpnta"))!=-1) { + switch (arg) { + + case 'f': + fitsfile=optarg; + break; + + case 'r': + reffile=optarg; + break; + + case 'm': + mmin=atof(optarg); + break; + + case 't': + track=1; + break; + + case 'R': + rmin=atof(optarg); + break; + + case 'p': + plot=1; + break; + + case 'a': + add=1; + break; + + case 'h': + usage(mmin,rmin); + return 0; + + default: + usage(mmin,rmin); + return 0; + } + } + } else { + usage(mmin,rmin); + return 0; + } + + // Check if minimum input is provided + if (fitsfile==NULL || reffile==NULL) { + usage(mmin,rmin); + return 0; + } + + // Check this is indeed a FITS file + if (is_fits_file(fitsfile)!=1) { + printf("%s is not a FITS file\n",fitsfile); + return -1 ; + } + + // Check this is indeed a FITS file + if (is_fits_file(reffile)!=1) { + printf("%s is not a FITS file\n",reffile); + return -1 ; + } + + // Read fits file + img=read_fits(fitsfile); + sprintf(catfile,"%s.cat",fitsfile); + sprintf(calfile,"%s.cal",fitsfile); + + // Read reference transformation + t=reference(reffile); + + // Correct astrometry for fixed or tracked setup + if (track==0) + t.ra0=modulo(t.ra0+gmst(img.mjd)-gmst(t.mjd),360.0); + + // Match catalog + c=match_catalogs(catfile,starfile,t,img,rmin,mmin); + + + // Plot + if (plot==1) + plot_image(img,t,c,catfile,mmin); + + // Do fit + for (l=0;l<10;l++) { + for (j=0;j<5;j++) { + // Transform + for (i=0;i2*t.rms) + c.usage[i]=0; + } + } + + // Print results + outfile=fopen(calfile,"w"); + for (i=0;i 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Match catalogs +struct catalog match_catalogs(char *pixcat,char *astcat,struct transformation t,struct image img,float rmax,float mmin) +{ + int i=0,imin,j,k,np; + FILE *file; + char line[LIM]; + struct star s; + double rx,ry,d,dx,dy; + int usage[NMAX]; + float xp[NMAX],yp[NMAX],mp[NMAX],x,y,fb[NMAX],fm[NMAX],bg[NMAX]; + struct catalog c; + float r,rmin; + + // Read pixel catalog + file=fopen(pixcat,"r"); + if (file==NULL) { + printf("pixel catalog not found\n"); + exit(-1); + } + while (fgetline(file,line,LIM)>0) { + if (strstr(line,"#")!=NULL) + continue; + sscanf(line,"%f %f %f %f %f %f",&xp[i],&yp[i],&mp[i],&fb[i],&fm[i],&bg[i]); + usage[i]=1; + i++; + } + fclose(file); + np=i; + + // Denominator + d=t.a[1]*t.b[2]-t.a[2]*t.b[1]; + + // Read astrometric catalog + file=fopen(astcat,"rb"); + if (file==NULL) { + printf("astrometric catalog not found\n"); + exit(-1); + } + j=0; + while (!feof(file)) { + fread(&s,sizeof(struct star),1,file); + if (s.mag>mmin) + continue; + r=acos(sin(t.de0*D2R)*sin(s.de*D2R)+cos(t.de0*D2R)*cos(s.de*D2R)*cos((t.ra0-s.ra)*D2R))*R2D; + if (r>90.0) + continue; + forward(t.ra0,t.de0,s.ra,s.de,&rx,&ry); + + dx=rx-t.a[0]; + dy=ry-t.b[0]; + x=(t.b[2]*dx-t.a[2]*dy)/d+t.x0; + y=(t.a[1]*dy-t.b[1]*dx)/d+t.y0; + + // On image + if (x>0.0 && x0.0 && ymmin) + continue; + r=acos(sin(t.de0*D2R)*sin(s.de*D2R)+cos(t.de0*D2R)*cos(s.de*D2R)*cos((t.ra0-s.ra)*D2R))*R2D; + if (r>90.0) + continue; + forward(t.ra0,t.de0,s.ra,s.de,&rx,&ry); + x=(t.b[2]*rx-t.a[2]*ry)/d+t.x0; + y=(t.a[1]*ry-t.b[1]*rx)/d+t.y0; + if (x>0.0 && x0.0 && y0) { + if (strstr(line,"#")!=NULL) + continue; + sscanf(line,"%f %f %f",&x,&y,&mag); + + cpgpt1(x,y,4); + + i++; + } + fclose(file); + + return; +} + +// Linear 2D fit +void lfit2d(float *x,float *y,float *z,int n,float *a) +{ + int i,j,m; + double chisq; + gsl_matrix *X,*cov; + gsl_vector *yy,*w,*c; + + X=gsl_matrix_alloc(n,3); + yy=gsl_vector_alloc(n); + w=gsl_vector_alloc(n); + + c=gsl_vector_alloc(3); + cov=gsl_matrix_alloc(3,3); + + // Fill matrices + for(i=0;i deep.c + * + * 1.00 around 1980 - Felix R. Hoots & Ronald L. Roehrich, from original + * DEEP.FOR used in the SGP deep-space models SDP4 + * and SDP8. + * + ************************************************************************ + * + * Made famous by the spacetrack report No.3: + * "Models for Propogation of NORAD Element Sets" + * Edited and subsequently distributed by Dr. T. S. Kelso. + * + ************************************************************************ + * + * This conversion by: + * Paul S. Crawford and Andrew R. Brooks + * Dundee University + * + * NOTE ! + * This code is supplied "as is" and without warranty of any sort. + * + * (c) 1994-2004, Paul Crawford, Andrew Brooks + * + ************************************************************************ + * + * 2.00 psc Mon Dec 19 1994 - Translated from FORTRAN into 'C' (of sorts). + * + * 2.01 psc Wed Dec 21 1994 - Re-write of the secular integrator from a + * messy FORTRAN block in to something which + * (hopefully!) is understandable. + * + * 2.02 psc Thu Dec 22 1994 - Final mods and tested against the FORTRAN + * version (using ~12 hour resonant and + * geostationary (~24 hour) elements). + * + * 2.03 psc Mon Jan 02 1995 - Some additional refinements and error-traps. + * + * 3.00 psc Mon May 29 1995 - Cleaned up for general use & distrabution (to + * remove Dundee specific features). + * + * 3.01 psc Mon Jan 12 2004 - Final fix agreed for "Lyddane bug". + * 3.02 psc Mon Jul 03 2006 - Extended range "Lyddane bug" fix. + * 3.03 psc Tue Jul 04 2006 - Bug fix for extended range "Lyddane bug" fix. + */ + +#include +#include +#include + +static const char SCCSid[] = "@(#)deep.c 3.03 (C) 1995 psc SatLib: Deep Space effects"; + +#ifndef NO_DEEP_SPACE + +#include "sgdp4h.h" + +extern long Isat; +int Set_LS_zero = 0; /* Set to 1 to zero Lunar-Solar terms at epoch. */ + +/* ======================= Function prototypes ====================== */ + +static void dot_terms_calculated(void); +static void compute_LunarSolar(double tsince); +static void thetag(double ep, real *thegr, double *days50); + +/* ===================== Strange constants, etc ===================== */ + +#define ZNS ((real)1.19459e-5) +#define C1SS ((real)2.9864797e-6) +#define ZES ((real)0.01675) + +#define ZNL ((real)1.5835218e-4) +#define C1L ((real)4.7968065e-7) +#define ZEL ((real)0.0549) + +#define ZCOSIS ((real)0.91744867) +#define ZSINIS ((real)0.39785416) +#define ZCOSGS ((real)0.1945905) +#define ZSINGS ((real)-0.98088458) + +#define Q22 ((real)1.7891679e-6) +#define Q31 ((real)2.1460748e-6) +#define Q33 ((real)2.2123015e-7) + +#define G22 ((real)5.7686396) +#define G32 ((real)0.95240898) +#define G44 ((real)1.8014998) +#define G52 ((real)1.0508330) +#define G54 ((real)4.4108898) + +#define ROOT22 ((real)1.7891679e-6) +#define ROOT32 ((real)3.7393792e-7) +#define ROOT44 ((real)7.3636953e-9) +#define ROOT52 ((real)1.1428639e-7) +#define ROOT54 ((real)2.1765803e-9) + +#define THDT ((real)4.37526908801129966e-3) +//#define THDT ((real)0.0043752691) + +#define STEP 720.0 +#define MAX_INTEGRATE (STEP * 10000) +#define SIN_EPS (real)(1.0e-12) + +/* ======= Global variables used by dpsec(), from dpinit(). ======== */ + +static real eo; /* copy of original eccentricity. */ +static real xincl; /* copy of original equatorial inclination. */ + +static int isynfl=0, iresfl=0; + +static double atime, xli, xni, xnq, xfact; + +static real ssl, ssg, ssh, sse, ssi; +static real xlamo, omegaq, omgdt, thgr; +static real del1, del2, del3, fasx2, fasx4, fasx6; +static real d2201, d2211, d3210, d3222, d4410, d4422; +static real d5220, d5232, d5421, d5433; + +static real xnddt, xndot, xldot; /* Integrator terms. */ +static real xnddt0, xndot0, xldot0; /* Integrator at epoch. */ + +/* ======== Global Variables used by dpper(), from dpinit(). ======= */ + +static int ilsd=0, ilsz=0; + +static real zmos, se2, se3, si2, si3, sl2, sl3, sl4; +static real sgh2, sgh3, sgh4, sh2, sh3; +static real zmol, ee2, e3 ,xi2, xi3, xl2, xl3, xl4; +static real xgh2, xgh3, xgh4, xh2, xh3; + +static real pe, pinc, pgh, ph, pl; +static real pgh0, ph0, pe0, pinc0, pl0; /* Added terms to save the epoch values of perturbations. */ + + +/* ================================================================== + + ----------------- DEEP SPACE INITIALIZATION ---------------------- + + epoch : Input, epoch time as YYDDD.DDDD as read from 2-line elements. + omegao : Input, argument of perigee from elements, radian. + xnodeo : Input, right asc. for ascn node from elements, radian. + xmo : Input, mean anomaly from elements, radian. + orb_eo : Input, eccentricity from elements, dimentionless. + orb_xincl : Input, equatorial inclination from elements, radian. + aodp : Input, original semi-major axis, earth radii. + xlldot : Input, 1st derivative of "mean anomaly" (xmdot), radian/min. + omgdot : Input, 1st derivative of arg. per., radian/min. + xnodot : Input, 1st derivative of right asc., radian/min. + xnodp : Input, original mean motion, radian/min. + + ================================================================== */ + +int SGDP4_dpinit(double epoch, real omegao, real xnodeo, real xmo, + real orb_eo, real orb_xincl, real aodp, double xlldot, + real omgdot, real xnodot, double xnodp) +{ +LOCAL_DOUBLE ds50, day, xnodce, bfact=0, gam, c; +LOCAL_REAL ctem, sinq, cosq, aqnv, xmao, stem, eqsq, xnoi, ainv2; +LOCAL_REAL zcosg, zsing, zcosi, zsini, zcosh, zsinh; +LOCAL_REAL cosomo, zcosgl, zcoshl, zcosil, sinomo; +LOCAL_REAL xpidot, zsinil, siniq2, cosiq2; +LOCAL_REAL rteqsq, zsinhl, zsingl; +LOCAL_REAL eoc, sgh, g200, bsq, zmo, xno2; +LOCAL_REAL a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; +LOCAL_REAL x1, x2, x3, x4, x5, x6, x7, x8; +LOCAL_REAL z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33; +LOCAL_REAL s1, s2, s3, s4, s5, s6, s7, cc, ao, eq, se, shdq, si, sl; +LOCAL_REAL zx, zy, ze, zn; +LOCAL_REAL g201, g211, g310, g300, g322, g410, g422, g520, g533, g521, g532; +LOCAL_REAL f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543; +real siniq, cosiq; +real temp0, temp1; +int ls, imode=0; +int ishq; + + /* + Copy the supplied orbital elements to "local" (static to this file) + variables and compute common trig values. + */ + eq = eo = orb_eo; + xincl = orb_xincl; + + /* Decide on direct or Lyddane Lunar-Solar perturbations. */ + ilsd = 0; + if(xincl >= (real)0.2) ilsd = 1; + + /* Drop some terms below 3 deg inclination. */ + ishq = 0; +#define SHQT 0.052359877 + if (xincl >= (real)SHQT) ishq = 1; /* As per reoprt #3. */ + + SINCOS(omegao, &sinomo, &cosomo); + SINCOS(xnodeo, &sinq, &cosq); + SINCOS(xincl, &siniq, &cosiq); + + if (fabs(siniq) <= SIN_EPS) + { + siniq = SIGN(SIN_EPS, siniq); + } + + cosiq2 = cosiq * cosiq; + siniq2 = siniq * siniq; + + ao = aodp; + omgdt = omgdot; + eqsq = eo * eo; + bsq = (real)1.0 - eqsq; + rteqsq = SQRT(bsq); + thetag(epoch, &thgr, &ds50); + + /*printf("# epoch = %.8f ds50 = %.8f thgr = %f\n", epoch, ds50, DEG(thgr));*/ + + xnq = xnodp; + aqnv = (real)1.0 / ao; + xmao = xmo; + xpidot = omgdt + xnodot; + omegaq = omegao; + + /* INITIALIZE LUNAR SOLAR TERMS */ + + day = ds50 + 18261.5; + + xnodce = 4.523602 - day * 9.2422029e-4; + temp0 = (real)fmod(xnodce, TWOPI); + SINCOS(temp0, &stem, &ctem); + + zcosil = (real)0.91375164 - ctem * (real)0.03568096; + zsinil = SQRT((real)1.0 - zcosil * zcosil); + zsinhl = stem * (real)0.089683511 / zsinil; + zcoshl = SQRT((real)1.0 - zsinhl * zsinhl); + c = day * 0.2299715 + 4.7199672; + gam = day * 0.001944368 + 5.8351514; + zmol = (real)MOD2PI(c - gam); + zx = stem * (real)0.39785416 / zsinil; + zy = zcoshl * ctem + zsinhl * (real)0.91744867 * stem; + zx = ATAN2(zx, zy); + zx = (real)fmod(gam + zx - xnodce, TWOPI); + SINCOS(zx, &zsingl, &zcosgl); + zmos = (real)MOD2PI(day * 0.017201977 + 6.2565837); + + /* DO SOLAR TERMS */ + + zcosg = ZCOSGS; + zsing = ZSINGS; + zcosi = ZCOSIS; + zsini = ZSINIS; + zcosh = cosq; + zsinh = sinq; + cc = C1SS; + zn = ZNS; + ze = ZES; + zmo = zmos; + xnoi = (real)(1.0 / xnq); + + for(ls = 0; ls < 2; ls++) + { + a1 = zcosg * zcosh + zsing * zcosi * zsinh; + a3 = -zsing * zcosh + zcosg * zcosi * zsinh; + a7 = -zcosg * zsinh + zsing * zcosi * zcosh; + a8 = zsing * zsini; + a9 = zsing * zsinh + zcosg * zcosi * zcosh; + a10 = zcosg * zsini; + a2 = cosiq * a7 + siniq * a8; + a4 = cosiq * a9 + siniq * a10; + a5 = -siniq * a7 + cosiq * a8; + a6 = -siniq * a9 + cosiq * a10; + + x1 = a1 * cosomo + a2 * sinomo; + x2 = a3 * cosomo + a4 * sinomo; + x3 = -a1 * sinomo + a2 * cosomo; + x4 = -a3 * sinomo + a4 * cosomo; + x5 = a5 * sinomo; + x6 = a6 * sinomo; + x7 = a5 * cosomo; + x8 = a6 * cosomo; + + z31 = x1 * (real)12.0 * x1 - x3 * (real)3.0 * x3; + z32 = x1 * (real)24.0 * x2 - x3 * (real)6.0 * x4; + z33 = x2 * (real)12.0 * x2 - x4 * (real)3.0 * x4; + z1 = (a1 * a1 + a2 * a2) * (real)3.0 + z31 * eqsq; + z2 = (a1 * a3 + a2 * a4) * (real)6.0 + z32 * eqsq; + z3 = (a3 * a3 + a4 * a4) * (real)3.0 + z33 * eqsq; + z11 = a1 * (real)-6.0 * a5 + eqsq * (x1 * (real)-24.0 * x7 - x3 * + (real)6.0 * x5); + z12 = (a1 * a6 + a3 * a5) * (real)-6.0 + eqsq * ((x2 * x7 + + x1 * x8) * (real)-24.0 - (x3 * x6 + x4 * x5) * (real)6.0); + z13 = a3 * (real)-6.0 * a6 + eqsq * (x2 * (real)-24.0 * x8 - x4 * + (real)6.0 * x6); + z21 = a2 * (real)6.0 * a5 + eqsq * (x1 * (real)24.0 * x5 - + x3 * (real)6.0 * x7); + z22 = (a4 * a5 + a2 * a6) * (real)6.0 + eqsq * ((x2 * x5 + x1 * x6) * + (real)24.0 - (x4 * x7 + x3 * x8) * (real)6.0); + z23 = a4 * (real)6.0 * a6 + eqsq * (x2 * (real)24.0 * x6 - x4 * + (real)6.0 * x8); + z1 = z1 + z1 + bsq * z31; + z2 = z2 + z2 + bsq * z32; + z3 = z3 + z3 + bsq * z33; + s3 = cc * xnoi; + s2 = s3 * (real)-0.5 / rteqsq; + s4 = s3 * rteqsq; + s1 = eq * (real)-15.0 * s4; + s5 = x1 * x3 + x2 * x4; + s6 = x2 * x3 + x1 * x4; + s7 = x2 * x4 - x1 * x3; + se = s1 * zn * s5; + si = s2 * zn * (z11 + z13); + sl = -zn * s3 * (z1 + z3 - (real)14.0 - eqsq * (real)6.0); + sgh = s4 * zn * (z31 + z33 - (real)6.0); + + shdq = 0; + if(ishq) + { + real sh = -zn * s2 * (z21 + z23); + shdq = sh / siniq; + } + + ee2 = s1 * (real)2.0 * s6; + e3 = s1 * (real)2.0 * s7; + xi2 = s2 * (real)2.0 * z12; + xi3 = s2 * (real)2.0 * (z13 - z11); + xl2 = s3 * (real)-2.0 * z2; + xl3 = s3 * (real)-2.0 * (z3 - z1); + xl4 = s3 * (real)-2.0 * ((real)-21.0 - eqsq * (real)9.0) * ze; + xgh2 = s4 * (real)2.0 * z32; + xgh3 = s4 * (real)2.0 * (z33 - z31); + xgh4 = s4 * (real)-18.0 * ze; + xh2 = s2 * (real)-2.0 * z22; + xh3 = s2 * (real)-2.0 * (z23 - z21); + + if (ls == 1) break; + + /* DO LUNAR TERMS */ + + sse = se; + ssi = si; + ssl = sl; + ssh = shdq; + ssg = sgh - cosiq * ssh; + se2 = ee2; + si2 = xi2; + sl2 = xl2; + sgh2 = xgh2; + sh2 = xh2; + se3 = e3; + si3 = xi3; + sl3 = xl3; + sgh3 = xgh3; + sh3 = xh3; + sl4 = xl4; + sgh4 = xgh4; + zcosg = zcosgl; + zsing = zsingl; + zcosi = zcosil; + zsini = zsinil; + zcosh = zcoshl * cosq + zsinhl * sinq; + zsinh = sinq * zcoshl - cosq * zsinhl; + zn = ZNL; + cc = C1L; + ze = ZEL; + zmo = zmol; + } + + sse += se; + ssi += si; + ssl += sl; + ssg += sgh - cosiq * shdq; + ssh += shdq; + + if (xnq < 0.0052359877 && xnq > 0.0034906585) + { + /* 24h SYNCHRONOUS RESONANCE TERMS INITIALIZATION */ + iresfl = 1; + isynfl = 1; + g200 = eqsq * (eqsq * (real)0.8125 - (real)2.5) + (real)1.0; + g310 = eqsq * (real)2.0 + (real)1.0; + g300 = eqsq * (eqsq * (real)6.60937 - (real)6.0) + (real)1.0; + f220 = (cosiq + (real)1.0) * (real)0.75 * (cosiq + (real)1.0); + f311 = siniq * (real)0.9375 * siniq * (cosiq * (real)3.0 + + (real)1.0) - (cosiq + (real)1.0) * (real)0.75; + f330 = cosiq + (real)1.0; + f330 = f330 * (real)1.875 * f330 * f330; + del1 = (real)3.0 * (real)(xnq * xnq * aqnv * aqnv); + del2 = del1 * (real)2.0 * f220 * g200 * Q22; + del3 = del1 * (real)3.0 * f330 * g300 * Q33 * aqnv; + del1 = del1 * f311 * g310 * Q31 * aqnv; + fasx2 = (real)0.13130908; + fasx4 = (real)2.8843198; + fasx6 = (real)0.37448087; + xlamo = xmao + xnodeo + omegao - thgr; + bfact = xlldot + xpidot - THDT; + bfact += (double)(ssl + ssg + ssh); + } + else if (xnq >= 0.00826 && xnq <= 0.00924 && eq >= (real)0.5) + { + /* GEOPOTENTIAL RESONANCE INITIALIZATION FOR 12 HOUR ORBITS */ + iresfl = 1; + isynfl = 0; + eoc = eq * eqsq; + g201 = (real)-0.306 - (eq - (real)0.64) * (real)0.44; + + if (eq <= (real)0.65) + { + g211 = (real)3.616 - eq * (real)13.247 + eqsq * (real)16.29; + g310 = eq * (real)117.39 - (real)19.302 - eqsq * (real)228.419 + eoc * (real)156.591; + g322 = eq * (real)109.7927 - (real)18.9068 - eqsq * (real)214.6334 + eoc * (real)146.5816; + g410 = eq * (real)242.694 - (real)41.122 - eqsq * (real)471.094 + eoc * (real)313.953; + g422 = eq * (real)841.88 - (real)146.407 - eqsq * (real)1629.014 + eoc * (real)1083.435; + g520 = eq * (real)3017.977 - (real)532.114 - eqsq * 5740.032 + eoc * (real)3708.276; + } + else + { + g211 = eq * (real)331.819 - (real)72.099 - eqsq * (real)508.738 + eoc * (real)266.724; + g310 = eq * (real)1582.851 - (real)346.844 - eqsq * (real)2415.925 + eoc * (real)1246.113; + g322 = eq * (real)1554.908 - (real)342.585 - eqsq * (real)2366.899 + eoc * (real)1215.972; + g410 = eq * (real)4758.686 - (real)1052.797 - eqsq * (real)7193.992 + eoc * (real)3651.957; + g422 = eq * (real)16178.11 - (real)3581.69 - eqsq * (real)24462.77 + eoc * (real)12422.52; + + if (eq <= (real)0.715) + { + g520 = (real)1464.74 - eq * (real)4664.75 + eqsq * (real)3763.64; + } + else + { + g520 = eq * (real)29936.92 - (real)5149.66 - eqsq * (real)54087.36 + eoc * (real)31324.56; + } + } + + if (eq < (real)0.7) + { + g533 = eq * (real)4988.61 - (real)919.2277 - eqsq * (real)9064.77 + eoc * (real)5542.21; + g521 = eq * (real)4568.6173 - (real)822.71072 - eqsq * (real)8491.4146 + eoc * (real)5337.524; + g532 = eq * (real)4690.25 - (real)853.666 - eqsq * (real)8624.77 + eoc * (real)5341.4; + } + else + { + g533 = eq * (real)161616.52 - (real)37995.78 - eqsq * (real)229838.2 + eoc * (real)109377.94; + g521 = eq * (real)218913.95 - (real)51752.104 - eqsq * (real)309468.16 + eoc * (real)146349.42; + g532 = eq * (real)170470.89 - (real)40023.88 - eqsq * (real)242699.48 + eoc * (real)115605.82; + } + + f220 = (cosiq * (real)2.0 + (real)1.0 + cosiq2) * (real)0.75; + f221 = siniq2 * (real)1.5; + f321 = siniq * (real)1.875 * ((real)1.0 - cosiq * (real)2.0 - cosiq2 * (real)3.0); + f322 = siniq * (real)-1.875 * (cosiq * (real)2.0 + (real)1.0 - cosiq2 * (real)3.0); + f441 = siniq2 * (real)35.0 * f220; + f442 = siniq2 * (real)39.375 * siniq2; + f522 = siniq * (real)9.84375 * (siniq2 * ((real)1.0 - cosiq * + (real)2.0 - cosiq2 * (real)5.0) + (cosiq * (real)4.0 - + (real)2.0 + cosiq2 * (real)6.0) * (real)0.33333333); + f523 = siniq * (siniq2 * (real)4.92187512 * ((real)-2.0 - cosiq * + (real)4.0 + cosiq2 * (real)10.0) + (cosiq * (real)2.0 + + (real)1.0 - cosiq2 * (real)3.0) * (real)6.56250012); + f542 = siniq * (real)29.53125 * ((real)2.0 - cosiq * (real)8.0 + + cosiq2 * (cosiq * (real)8.0 - (real)12.0 + cosiq2 * + (real)10.0)); + f543 = siniq * (real)29.53125 * ((real)-2.0 - cosiq * (real)8.0 + + cosiq2 * (cosiq * (real)8.0 + (real)12.0 - cosiq2 * + (real)10.0)); + xno2 = (real)(xnq * xnq); + ainv2 = aqnv * aqnv; + temp1 = xno2 * (real)3.0 * ainv2; + temp0 = temp1 * ROOT22; + d2201 = temp0 * f220 * g201; + d2211 = temp0 * f221 * g211; + temp1 *= aqnv; + temp0 = temp1 * ROOT32; + d3210 = temp0 * f321 * g310; + d3222 = temp0 * f322 * g322; + temp1 *= aqnv; + temp0 = temp1 * (real)2.0 * ROOT44; + d4410 = temp0 * f441 * g410; + d4422 = temp0 * f442 * g422; + temp1 *= aqnv; + temp0 = temp1 * ROOT52; + d5220 = temp0 * f522 * g520; + d5232 = temp0 * f523 * g532; + temp0 = temp1 * (real)2.0 * ROOT54; + d5421 = temp0 * f542 * g521; + d5433 = temp0 * f543 * g533; + xlamo = xmao + xnodeo + xnodeo - thgr - thgr; + bfact = xlldot + xnodot + xnodot - THDT - THDT; + bfact += (double)(ssl + ssh + ssh); + } + else + { + /* NON RESONANT ORBITS */ + iresfl = 0; + isynfl = 0; + } + + if(iresfl == 0) + { + /* Non-resonant orbits. */ + imode = SGDP4_DEEP_NORM; + } + else + { + /* INITIALIZE INTEGRATOR */ + xfact = bfact - xnq; + xli = (double)xlamo; + xni = xnq; + atime = 0.0; + + dot_terms_calculated(); + + /* Save the "dot" terms for integrator re-start. */ + xnddt0 = xnddt; + xndot0 = xndot; + xldot0 = xldot; + + if (isynfl) + imode = SGDP4_DEEP_SYNC; + else + imode = SGDP4_DEEP_RESN; + } + + /* Set up for original mode (LS terms at epoch non-zero). */ + ilsz = 0; + pgh0 = ph0 = pe0 = pinc0 = pl0 = (real)0.0; + + if(Set_LS_zero) + { + /* Save the epoch case Lunar-Solar terms to remove this bias for + * actual computations later on. + * Not sure if this is a good idea. + */ + compute_LunarSolar(0.0); + + pgh0 = pgh; + ph0 = ph; + pe0 = pe; + pinc0 = pinc; + pl0 = pl; + ilsz = 1; + } + + +return imode; +} /* SGDP4_dpinit */ + +/* ===================================================================== + + ------------- ENTRANCE FOR DEEP SPACE SECULAR EFFECTS --------------- + + xll : Input/Output, modified "mean anomaly" or "mean longitude". + omgasm : Input/Output, modified argument of perigee. + xnodes : Input/Output, modified right asc of ascn node. + em : Input/Output, modified eccentricity. + xinc : Input/Output, modified inclination. + + xn : Output, modified period from 'xnodp'. + + tsince : Input, time from epoch (minutes). + + ===================================================================== */ + +int SGDP4_dpsec(double *xll, real *omgasm, real *xnodes, real *em, + real *xinc, double *xn, double tsince) +{ +LOCAL_DOUBLE delt, ft, xl; +real temp0; + + *xll += ssl * tsince; + *omgasm += ssg * tsince; + *xnodes += ssh * tsince; + *em += sse * tsince; + *xinc += ssi * tsince; + + if (iresfl == 0) return 0; + + /* + * A minor increase in some efficiency can be had by restarting if + * the new time is closer to epoch than to the old integrated + * time. This also forces a re-start on a change in sign (i.e. going + * through zero time) as then we have |tsince - atime| > |tsince| + * as well. Second test is for stepping back towards zero, forcing a restart + * if close enough rather than integrating to zero. + */ +#define AHYST 1.0 + /* Most accurate (OK, most _consistant_) method. Restart if need to + * integrate 'backwards' significantly from current point. + */ + if(fabs(tsince) < STEP || + (atime > 0.0 && tsince < atime - AHYST) || + (atime < 0.0 && tsince > atime + AHYST)) + { + /* Epoch restart if we are at, or have crossed, tsince==0 */ + atime = 0.0; + xni = xnq; + xli = (double)xlamo; + /* Restore the old "dot" terms. */ + xnddt = xnddt0; + xndot = xndot0; + xldot = xldot0; + } + + ft = tsince - atime; + + if (fabs(ft) > MAX_INTEGRATE) + { + fatal_error("SGDP4_dpsec: Integration limit reached"); + return -1; + } + + if (fabs(ft) >= STEP) + { + /* + Do integration if required. Find the step direction to + make 'atime' catch up with 'tsince'. + */ + delt = (tsince >= atime ? STEP : -STEP); + + do { + /* INTEGRATOR (using the last "dot" terms). */ + xli += delt * (xldot + delt * (real)0.5 * xndot); + xni += delt * (xndot + delt * (real)0.5 * xnddt); + atime += delt; + + dot_terms_calculated(); + + /* Are we close enough now ? */ + ft = tsince - atime; + } while (fabs(ft) >= STEP); + } + + xl = xli + ft * (xldot + ft * (real)0.5 * xndot); + *xn = xni + ft * (xndot + ft * (real)0.5 * xnddt); + + temp0 = -(*xnodes) + thgr + tsince * THDT; + + if (isynfl == 0) + *xll = xl + temp0 + temp0; + else + *xll = xl - *omgasm + temp0; + +return 0; +} /* SGDP4_dpsec */ + +/* ===================================================================== + + Here we do the "dot" terms for the integrator. Separate function so we + can call when initialising and save the atime==0.0 values for later + epoch re-start of the integrator. + + ===================================================================== */ + +static void dot_terms_calculated(void) +{ +LOCAL_DOUBLE x2li, x2omi, xomi; + + /* DOT TERMS CALCULATED */ + if (isynfl) + { + xndot = del1 * SIN(xli - fasx2) + + del2 * SIN((xli - fasx4) * (real)2.0) + + del3 * SIN((xli - fasx6) * (real)3.0); + + xnddt = del1 * COS(xli - fasx2) + + del2 * COS((xli - fasx4) * (real)2.0) * (real)2.0 + + del3 * COS((xli - fasx6) * (real)3.0) * (real)3.0; + } + else + { + xomi = omegaq + omgdt * atime; + x2omi = xomi + xomi; + x2li = xli + xli; + + xndot = d2201 * SIN(x2omi + xli - G22) + + d2211 * SIN(xli - G22) + + d3210 * SIN(xomi + xli - G32) + + d3222 * SIN(-xomi + xli - G32) + + d5220 * SIN(xomi + xli - G52) + + d5232 * SIN(-xomi + xli - G52) + + d4410 * SIN(x2omi + x2li - G44) + + d4422 * SIN(x2li - G44) + + d5421 * SIN(xomi + x2li - G54) + + d5433 * SIN(-xomi + x2li - G54); + + xnddt = d2201 * COS(x2omi + xli - G22) + + d2211 * COS(xli - G22) + + d3210 * COS(xomi + xli - G32) + + d3222 * COS(-xomi + xli - G32) + + d5220 * COS(xomi + xli - G52) + + d5232 * COS(-xomi + xli - G52) + + (d4410 * COS(x2omi + x2li - G44) + + d4422 * COS(x2li - G44) + + d5421 * COS(xomi + x2li - G54) + + d5433 * COS(-xomi + x2li - G54)) * (real)2.0; + } + + xldot = (real)(xni + xfact); + xnddt *= xldot; + +} /* dot_terms_calculated */ + +/* ===================================================================== + + ---------------- ENTRANCES FOR LUNAR-SOLAR PERIODICS ---------------- + + em : Input/Output, modified eccentricity. + xinc : Input/Output, modified inclination. + omgasm : Input/Output, modified argument of perigee. + xnodes : Input/Output, modified right asc of ascn node. + xll : Input/Output, modified "mean anomaly" or "mean longitude". + tsince : Input, time from epoch (minutes). + + ===================================================================== */ + +int SGDP4_dpper(real *em, real *xinc, real *omgasm, real *xnodes, + double *xll, double tsince) +{ +real sinis, cosis; + + compute_LunarSolar(tsince); + + *xinc += pinc; + *em += pe; + + /* Spacetrack report #3 has sin/cos from before perturbations + * added to xinc (oldxinc), but apparently report # 6 has then + * from after they are added. + */ + SINCOS(*xinc, &sinis, &cosis); + + if (ilsd) + { + /* APPLY PERIODICS DIRECTLY */ + real tmp_ph; + tmp_ph = ph / sinis; + + *omgasm += pgh - cosis * tmp_ph; + *xnodes += tmp_ph; + *xll += pl; + } + else + { + /* APPLY PERIODICS WITH LYDDANE MODIFICATION */ + LOCAL_REAL alfdp, betdp, dalf, dbet, xls, dls; + LOCAL_REAL sinok, cosok; + int ishift; + real tmp, oldxnode = (*xnodes); + + SINCOS(*xnodes, &sinok, &cosok); + alfdp = sinis * sinok; + betdp = sinis * cosok; + dalf = ph * cosok + pinc * cosis * sinok; + dbet = -ph * sinok + pinc * cosis * cosok; + alfdp += dalf; + betdp += dbet; + xls = (real)*xll + *omgasm + cosis * *xnodes; + dls = pl + pgh - pinc * *xnodes * sinis; + xls += dls; + *xnodes = ATAN2(alfdp, betdp); + + /* Get perturbed xnodes in to same quadrant as original. */ + ishift = NINT((oldxnode - (*xnodes))/TWOPI); + *xnodes += (real)(TWOPI * ishift); + + *xll += (double)pl; + *omgasm = xls - (real)*xll - cosis * (*xnodes); + } + +return 0; +} /* SGDP4_dpper */ + +/* ===================================================================== + Do the Lunar-Solar terms for the SGDP4_dpper() function (normally only + every 1/2 hour needed. Seperate function so initialisng could save the + epoch terms to zero them. Not sure if this is a good thing (some believe + it the way the equations were intended) as the two-line elements may + be computed to give the right answer with out this (which I would hope + as it would make predictions consistant with the 'official' model + code). + ===================================================================== */ + +static void compute_LunarSolar(double tsince) +{ +LOCAL_REAL sinzf, coszf; +LOCAL_REAL f2, f3, zf, zm; +LOCAL_REAL sel, sil, ses, sll, sis, sls; +LOCAL_REAL sghs, shs, sghl, shl; + + /* Update Solar terms. */ + zm = zmos + ZNS * tsince; + zf = zm + ZES * (real)2.0 * SIN(zm); + SINCOS(zf, &sinzf, &coszf); + f2 = sinzf * (real)0.5 * sinzf - (real)0.25; + f3 = sinzf * (real)-0.5 * coszf; + ses = se2 * f2 + se3 * f3; + sis = si2 * f2 + si3 * f3; + sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; + + sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; + shs = sh2 * f2 + sh3 * f3; + + /* Update Lunar terms. */ + zm = zmol + ZNL * tsince; + zf = zm + ZEL * (real)2.0 * SIN(zm); + SINCOS(zf, &sinzf, &coszf); + f2 = sinzf * (real)0.5 * sinzf - (real)0.25; + f3 = sinzf * (real)-0.5 * coszf; + sel = ee2 * f2 + e3 * f3; + sil = xi2 * f2 + xi3 * f3; + sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf; + + sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; + shl = xh2 * f2 + xh3 * f3; + + /* Save computed values to calling structure. */ + pgh = sghs + sghl; + ph = shs + shl; + pe = ses + sel; + pinc = sis + sil; + pl = sls + sll; + + if (ilsz) + { + /* Correct for previously saved epoch terms. */ + pgh -= pgh0; + ph -= ph0; + pe -= pe0; + pinc -= pinc0; + pl -= pl0; + } + +} + +/* ===================================================================== + This function converts the epoch time (in the form of YYDDD.DDDDDDDD, + exactly as it appears in the two-line elements) into days from 00:00:00 + hours Jan 1st 1950 UTC. Also it computes the right ascencion of Greenwich + at the epoch time, but not in a very accurate manner. However, the same + method is used here to allow exact comparason with the original FORTRAN + versions of the programs. The calling arguments are: + + ep : Input, epoch time of elements (as read from 2-line data). + thegr : Output, right ascensionm of Greenwich at epoch, radian. + days50 : Output, days from Jan 1st 1950 00:00:00 UTC. + + ===================================================================== */ + +#define THETAG 2 + +/* Version like sat_code. */ +#define J1900 (2451545.5 - 36525. - 1.) +#define SECDAY (86400.0) + +#define C1 (1.72027916940703639E-2) +#define C1P2P (C1 + TWOPI) +#define THGR70 (1.7321343856509374) +#define FK5R (5.07551419432269442E-15) + + +static void thetag(double ep, real *thegr, double *days50) +{ +double d; +long n, jy; +double jd, theta; + + jy = (long)((ep + 2.0e-7) * 0.001); /* Extract the year. */ + d = ep - jy * 1.0e3; /* And then the day of year. */ + + /* Assume " 8" is 1980, or more sensibly 2008 ? */ + /* + if (jy < 10) jy += 80; + */ + if (jy < 50) jy += 100; + + if (jy < 70) /* Fix for leap years ? */ + n = (jy - 72) / 4; + else + n = (jy - 69) / 4; + + *days50 = (jy - 70) * 365.0 + 7305.0 + n + d; + + jd = d + J1900 + jy * 365. + ((jy - 1) / 4); + +#if THETAG == 0 + /* Original report #3 code. */ + theta = *days50 * 6.3003880987 + 1.72944494; +#elif THETAG == 1 + { + /* Method from project pluto code. */ + /* Reference: The 1992 Astronomical Almanac, page B6. */ + const double omega_E = 1.00273790934; /* Earth rotations per sidereal day (non-constant) */ + const double UT = fmod(jd + 0.5, 1.0); + double t_cen, GMST; + + t_cen = (jd - UT - 2451545.0) / 36525.0; + GMST = 24110.54841 + t_cen * (8640184.812866 + t_cen * (0.093104 - t_cen * 6.2E-6)); + + GMST = fmod( GMST + SECDAY * omega_E * UT, SECDAY); + + if(GMST < 0.0) GMST += SECDAY; + + theta = TWOPI * GMST / SECDAY; + } +#elif THETAG == 2 + { + /* Method from SGP4SUB.F code. */ + double ts70, ds70, trfac; + long ids70; + + ts70 = (*days50) - 7305.0; + ids70 = (long)(ts70 + 1.0e-8); + ds70 = ids70; + + trfac = ts70 - ds70; + + /* CALCULATE GREENWICH LOCATION AT EPOCH */ + theta = THGR70 + C1*ds70 + C1P2P*trfac + ts70*ts70*FK5R; + } +#else +#error 'Unknown method for theta-G calculation' +#endif + + theta = fmod(theta, TWOPI); + if (theta < 0.0) theta += TWOPI; + + *thegr = (real)theta; + +} /* thetag */ + + +#endif /* !NO_DEEP_SPACE */ diff --git a/dsmin.c b/dsmin.c new file mode 100644 index 0000000..7222508 --- /dev/null +++ b/dsmin.c @@ -0,0 +1,124 @@ +#include +#include +#include + +#define TINY 1.0e-10 +#define NMAX 100000 +#define SWAP(a,b) {(a)+=(b);(b)=(a)-(b);(a)-=(b);} + +// Downhill Simplex Minimization +int dsmin(double **p,double *y,int n,double ftol,double (*func)(double *)) +{ + int i,j,nfunk=0; + int ihi,ilo,ise; + double *ptry,*pmid,*psum; + double tol,ytry,rtol,ysave; + double *vector_sum(double **,int); + double dsmod(double **,double *,double *,int,double (*func)(double *),int,double); + + // Allocate memory + psum=(double *) malloc(sizeof(double) * n); + + // Get function values + for (i=0;i<=n;i++) + y[i]=func(p[i]); + + // Sum vectors + psum=vector_sum(p,n); + + // Start forever loop + for (;;) { + // Find high and low point + ilo=0; + ihi = (y[0]>y[1]) ? (ise=1,0) : (ise=0,1); + for (i=0;i<=n;i++) { + if (y[i]<=y[ilo]) ilo=i; + if (y[i]>y[ihi]) { + ise=ihi; + ihi=i; + } else if (y[i]>y[ise] && i!=ihi) ise=i; + } + + // Compute fractional range from highest to lowest point + rtol=2.0*fabs(y[ihi]-y[ilo])/(fabs(y[ihi])+fabs(y[ilo])+TINY); + + // Return if fractional tolerance is acceptable + if (rtol=NMAX) { + printf("dsmin: NMAX exceeded!\n"); + return -1; + } + nfunk+=2; + + // Reflect simplex + ytry=dsmod(p,y,psum,n,func,ihi,-1.0); + + if (ytry<=y[ilo]) // Goes right direction, extrapolate by factor 2 + ytry=dsmod(p,y,psum,n,func,ihi,2.0); + else if (ytry>=y[ise]) { // 1D contraction + ysave=y[ihi]; + ytry=dsmod(p,y,psum,n,func,ihi,0.5); + if (ytry>=ysave) { + for (i=0;i<=n;i++) { + if (i!=ilo) { + for (j=0;j satutl.c + * + */ + + +#include "sgdp4h.h" + +#include + +void fatal_error(const char *format, ...) +{ +va_list arg_ptr; + + fflush(stdout); + + fprintf(stderr, "\nDundee Satellite Lab fatal run-time error:\n"); + + va_start(arg_ptr, format); + vfprintf(stderr, format, arg_ptr); + va_end(arg_ptr); + + fprintf(stderr, "\nNow terminating the program...\n"); + fflush(stderr); + + exit(5); + +} + +/* ===================================================================== */ diff --git a/fitsheader.c b/fitsheader.c new file mode 100644 index 0000000..ffd09a0 --- /dev/null +++ b/fitsheader.c @@ -0,0 +1,63 @@ +#include +#include +#include + +#define LIM 81 + +int fgetline(FILE *,char *,int); +void rtrim(char *); + +int main(int argc,char *argv[]) +{ + char line[LIM]; + FILE *fitsfile; + + // Usage + if (argc<2) { + printf("Usage: %s \n",argv[0]); + printf("\n\nOutputs the header of \n"); + return 1; + } + + // Open file + fitsfile=fopen(argv[1],"r"); + + // Loop over file and output + while (fgetline(fitsfile,line,LIM)>0) { + rtrim(line); + printf("%s\n",line); + if (strcmp(line,"END")==0) break; + } + + // Close file + fclose(fitsfile); + + return 0; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Removes trailing blanks from string s +void rtrim(char *s) +{ + int i,j=0,n; + + n=strlen(s); + for (i=n;i>=0;i--) + if (s[i]!='\0' && s[i]!='\n') + if (!isspace(s[i]) && j==0) j=i; + s[++j]='\0'; + + return; +} diff --git a/fitskey.c b/fitskey.c new file mode 100644 index 0000000..0bdb2a9 --- /dev/null +++ b/fitskey.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include "qfits.h" + +int main(int argc, char * argv[]) +{ + int i; + char keyword[FITS_LINESZ+1]; + char *value; + + // Usage + if (argc<3) { + printf("Usage: %s [ext] etc.\n", argv[0]); + return 1 ; + } + + // Check this is indeed a FITS file + if (is_fits_file(argv[1])!=1) { + printf("%s is not a FITS file\n", argv[1]); + return -1 ; + } + + // Extension header? + if (atoi(argv[2])==0) { + for (i=2;i +#include +#include +#include +#include +#include + +struct image { + int nx,ny; + char timestamp[64]; + unsigned char *c; +}; +struct fourframe { + int nx,ny,nt,nlayer; + char timestamp[64],observer[64]; + double mjd; + float *z,*dt; + int cospar; +}; +int fgetline(FILE *file,char *s,int lim); +void write_fits(char *filename,struct fourframe ff); +struct image read_pgm(char *filename,int *status); +double nfd2mjd(char *date); +double date2mjd(int year,int month,double day); +void write_pgm(char *filename,struct fourframe ff); +void mjd2date(double mjd,char *date); + +void usage(void) +{ + printf("pgm2fits p:w:h:s:n:Dd:x:y:c:o:gm:t:r:\n\n"); + printf("-p image prefix\n"); + printf("-w image width in pixels\n"); + printf("-h image height in pixels\n"); + printf("-s number of first image to process\n"); + printf("-n number of images to process\n"); + printf("-D toggle for creating dark frame\n"); + printf("-d toggle for creating dark frame\n"); + printf("-d filename of dark frame to substract\n"); + printf("-m filename of mask frame to apply\n"); + printf("-x tracking rate in x (pix/s)\n"); + printf("-y tracking rate in y (pix/s)\n"); + printf("-c COSPAR [default 4553]\n"); + printf("-o observer [default \"Cees Bassa\"]\n"); + printf("-g toggle for guiding??\n"); + printf("-t time stamp of first image [YYYY-MM-DDTHH:MM:SS.SSS]\n"); + printf("-r frame rate (frames/s)\n"); + exit(0); +} + +int main(int argc,char *argv[]) +{ + int i,j,k,l,m,k0=1,status,nt,darkout=0,darkin=0,track=0,maskin=0; + int n,n0,di,dj,npix; + struct image *img,drk,msk; + struct fourframe ff; + char filename[128],nfd[64]; + float s1,s2,z; + float avg,std,max,cnt,*trk; + int *wt; + int arg=0; + char *path,*darkfile,*maskfile; + double mjd,mjd0=0.0; + float dxdn=0.0,dydn=0.0,dx,dy; + int guide=0,timereset=0; + float framerate=25.0; + char *env; + + // Set defaults + env=getenv("ST_COSPAR"); + ff.cospar=atoi(env); + strcpy(ff.observer,"Cees Bassa"); + + // Decode options + if (argc>1) { + while ((arg=getopt(argc,argv,"p:w:h:s:n:Dd:x:y:c:o:gm:t:r:"))!=-1) { + switch(arg) { + case 'p': + path=optarg; + break; + + case 'g': + guide=1; + break; + + case 'w': + ff.nx=atoi(optarg); + break; + + case 'h': + ff.ny=atoi(optarg); + break; + + case 'c': + ff.cospar=atoi(optarg); + break; + + case 'o': + strcpy(ff.observer,optarg); + break; + + case 's': + k0=atoi(optarg); + break; + + case 'n': + nt=atoi(optarg); + break; + + case 'D': + darkout=1; + break; + + case 'd': + darkin=1; + darkfile=optarg; + break; + + case 'm': + maskin=1; + maskfile=optarg; + break; + + case 'x': + dxdn=atof(optarg); + track=1; + break; + + case 'y': + dydn=atof(optarg); + track=1; + break; + + case 't': + strcpy(nfd,optarg); + mjd0=nfd2mjd(nfd); + timereset=1; + break; + + case 'r': + framerate=atof(optarg); + timereset=1; + break; + + default: + usage(); + } + } + } else { + usage(); + } + + // Add layer + if (track==1) + ff.nlayer=5; + else + ff.nlayer=4; + + // Allocate + ff.z=(float *) malloc(ff.nlayer*sizeof(float)*ff.nx*ff.ny); + ff.dt=(float *) malloc(sizeof(float)*nt); + trk=(float *) malloc(sizeof(float)*ff.nx*ff.ny); + wt=(int *) malloc(sizeof(float)*ff.nx*ff.ny); + img=(struct image *) malloc(sizeof(struct image)*nt); + + // Read dark file + if (darkin==1) + drk=read_pgm(darkfile,&status); + + // Read mask file + if (maskin==1) + msk=read_pgm(maskfile,&status); + + // Loop over files + for (k=0,l=0;kmax) { + max=z; + cnt=(float) k; + } + } + avg=s1/(float) ff.nt; + std=sqrt((s2-s1*avg)/(float) (ff.nt-1)); + + // Reset masked pixels + if (maskin==1 && msk.c[n]==0.0) { + avg=0.0; + std=0.0; + max=0.0; + cnt=128.0; + } + + for (m=0;m0 && i+di0 && j+dj0) + ff.z[l]=trk[k]/(float) wt[k]; + else + ff.z[l]=trk[k]; + } + } + } + + // Write fits + if (ff.timestamp!=NULL) + sprintf(filename,"%s.fits",ff.timestamp); + else + strcpy(filename,"test.fits"); + write_fits(filename,ff); + + // Write dark frame + if (darkout==1) + write_pgm("dark.pgm",ff); + + // Free + for (k=0;k0 && (c=fgetc(file))!=EOF && c!='\n') + s[i++]=c; + // if (c=='\n') + // s[i++]=c; + s[i]='\0'; + + return i; +} + +// Write fits file +void write_fits(char *filename,struct fourframe ff) +{ + int i,j,k,l; + float *fbuf; + int *ibuf; + qfitsdumper qd; + qfits_header *qh; + char key[FITS_LINESZ+1] ; + char val[FITS_LINESZ+1] ; + char com[FITS_LINESZ+1] ; + char lin[FITS_LINESZ+1] ; + FILE *file; + + // Create FITS header + qh=qfits_header_default(); + + // Add stuff + qfits_header_add(qh,"BITPIX","-32"," ",NULL); + // qfits_header_add(qh,"BITPIX","16"," ",NULL); + qfits_header_add(qh,"NAXIS","3"," ",NULL); + sprintf(val,"%i",ff.nx); + qfits_header_add(qh,"NAXIS1",val," ",NULL); + sprintf(val,"%i",ff.ny); + qfits_header_add(qh,"NAXIS2",val," ",NULL); + sprintf(val,"%i",ff.nlayer); + qfits_header_add(qh,"NAXIS3",val," ",NULL); + qfits_header_add(qh,"BSCALE","1.0"," ",NULL); + qfits_header_add(qh,"BZERO","0.0"," ",NULL); + qfits_header_add(qh,"DATAMAX","255.0"," ",NULL); + qfits_header_add(qh,"DATAMIN","0.0"," ",NULL); + sprintf(val,"%s",ff.timestamp); + qfits_header_add(qh,"DATE-OBS",val," ",NULL); + // MJD-OBS + sprintf(val,"%lf",ff.mjd); + qfits_header_add(qh,"MJD-OBS",val," ",NULL); + sprintf(val,"%f",ff.dt[ff.nt-1],ff.dt[0]); + qfits_header_add(qh,"EXPTIME",val," ",NULL); + sprintf(val,"%d",ff.nt); + qfits_header_add(qh,"NFRAMES",val," ",NULL); + + // Astrometry keywors + sprintf(val,"%f",ff.nx/2.0); + qfits_header_add(qh,"CRPIX1",val," ",NULL); + sprintf(val,"%f",ff.ny/2.0); + qfits_header_add(qh,"CRPIX2",val," ",NULL); + qfits_header_add(qh,"CRVAL1","0.0"," ",NULL); + qfits_header_add(qh,"CRVAL2","0.0"," ",NULL); + qfits_header_add(qh,"CD1_1","0.0"," ",NULL); + qfits_header_add(qh,"CD1_2","0.0"," ",NULL); + qfits_header_add(qh,"CD2_1","0.0"," ",NULL); + qfits_header_add(qh,"CD2_2","0.0"," ",NULL); + qfits_header_add(qh,"CTYPE1","'RA---TAN'"," ",NULL); + qfits_header_add(qh,"CTYPE2","'DEC--TAN'"," ",NULL); + qfits_header_add(qh,"CUNIT1","'deg'"," ",NULL); + qfits_header_add(qh,"CUNIT2","'deg'"," ",NULL); + qfits_header_add(qh,"CRRES1","0.0"," ",NULL); + qfits_header_add(qh,"CRRES2","0.0"," ",NULL); + qfits_header_add(qh,"EQUINOX","2000.0"," ",NULL); + qfits_header_add(qh,"RADECSYS","ICRS"," ",NULL); + sprintf(val,"%d",ff.cospar); + qfits_header_add(qh,"COSPAR",val," ",NULL); + sprintf(val,"'%s'",ff.observer); + qfits_header_add(qh,"OBSERVER",val," ",NULL); + + // Add timestamps + for (k=0;k=0;j--) { + for (k=0;k2) + year=c-4716; + else + year=c-4715; + + day=(int) floor(dday); + x=24.0*(dday-day); + x=3600.*fabs(x); + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + hour=x; + + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%06.3f",year,month,day,hour,min,sec); + + return; +} diff --git a/plotfits.c b/plotfits.c new file mode 100644 index 0000000..c739647 --- /dev/null +++ b/plotfits.c @@ -0,0 +1,726 @@ +#include +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" +#include + +#define LIM 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define NMAX 4096 + +struct star { + double ra,de; + float pmra,pmde; + float mag; +}; +struct image { + int naxis1,naxis2,naxis3; + float *z; + float zmin,zmax; + double ra0,de0; + float x0,y0; + float a[3],b[3]; + double mjd; +} img; +struct catalog { + int n; + float x[NMAX],y[NMAX],mag[NMAX]; + double ra[NMAX],de[NMAX],rx[NMAX],ry[NMAX]; + int select[NMAX]; +}; +struct map { + double lat,lng; + float alt; + int site_id; + char observer[32]; +} m; + +struct image read_fits(char *filename,int pnum); +int fgetline(FILE *,char *,int); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); +void reverse(double,double,double,double,double *,double *); +void lfit2d(float *x,float *y,float *z,int n,float *a); +struct catalog read_pixel_catalog(char *filename); +double gmst(double mjd); +double modulo(double x,double y); +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de); +double sex2dec(char *s); + +// Read astrometric catalog +struct catalog read_astrometric_catalog(char *filename,float mmin,float sx,float sy,float angle) +{ + int i=0; + FILE *file; + char line[LIM]; + struct catalog c; + double rx,ry,x,y,ra,de; + struct star s; + double d,dx,dy; + double mjd0=51544.5; + + file=fopen(filename,"rb"); + if (file==NULL) { + fprintf(stderr,"%s not found!\n",filename); + exit(0); + } + while (!feof(file)) { + fread(&s,sizeof(struct star),1,file); + if (s.mag>mmin) + continue; + precess(mjd0,s.ra,s.de,img.mjd,&ra,&de); + forward(img.ra0,img.de0,ra,de,&rx,&ry); + x=img.x0+1.0/sx*(cos(angle*D2R)*rx+sin(angle*D2R)*ry); + y=img.y0+1.0/sy*(-sin(angle*D2R)*rx+cos(angle*D2R)*ry); + /* + } else if (t.state==1) { + dx=rx-t.a[0]; + dy=ry-t.b[0]; + d=t.a[1]*t.b[2]-t.a[2]*t.b[1]; + x=(t.b[2]*dx-t.a[2]*dy)/d; + y=(t.a[1]*dy-t.b[1]*dx)/d; + } + */ + if (x>0.0 && x0.0 && ymmin) + continue; + precess(mjd0,s.ra,s.de,img.mjd,&ra,&de); + forward(img.ra0,img.de0,ra,de,&rx,&ry); + dx=rx-img.a[0]; + dy=ry-img.b[0]; + d=img.a[1]*img.b[2]-img.a[2]*img.b[1]; + x=(img.b[2]*dx-img.a[2]*dy)/d+img.x0; + y=(img.a[1]*dy-img.b[1]*dx)/d+img.y0; + if (x>0.0 && x0.0 && yn;i++) + cat->select[i]=0; + for (i=0;in;i++) + ast->select[i]=0; + + file=fopen("out.dat","w"); + for (i=0,n=0;in;i++) { + for (j=0,flag=0;jn;j++) { + if (ast->select[j]!=0) + continue; + r=sqrt(pow(cat->x[i]-ast->x[j],2)+pow(cat->y[i]-ast->y[j],2)); + if (flag==0 || rx[i]-img.x0,cat->y[i]-img.y0,ast->ra[jmin],ast->de[jmin]); + cat->select[i]=n+1; + ast->select[jmin]=n+1; + n++; + } + } + fclose(file); + + printf("%d stars matched\n",n); + return n; +} + +// Get observing site +void get_site(int site_id) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64],filename[LIM],*env; + + env=getenv("ST_DATADIR"); + sprintf(filename,"%s/data/sites.txt",env); + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + // Change to km + alt/=1000.0; + + if (id==site_id) { + m.lat=lat; + m.lng=lng; + m.alt=alt; + m.site_id=id; + strcpy(m.observer,observer); + } + + } + fclose(file); + + return; +} + +int main(int argc,char *argv[]) +{ + int i; + float tr[]={-0.5,1.0,0.0,-0.5,0.0,1.0}; + float heat_l[] = {0.0, 0.2, 0.4, 0.6, 1.0}; + float heat_r[] = {0.0, 0.5, 1.0, 1.0, 1.0}; + float heat_g[] = {0.0, 0.0, 0.5, 1.0, 1.0}; + float heat_b[] = {0.0, 0.0, 0.0, 0.3, 1.0}; + float x,y,r,rmin=1.0,rmax=10.0,mmin=5.0,mmax=10.0; + struct catalog cat,ast; + char c; + int redraw=1,click=0,nselect=0; + char filename[128],sra[20],sde[20]; + float h,q,s=0.0,mag=9; + FILE *file; + char *env,starfile[128]; + + // Environment variables + env=getenv("ST_DATADIR"); + sprintf(starfile,"%s/data/tycho2.dat",env); + + // Geographic position + env=getenv("ST_COSPAR"); + get_site(atoi(env)); + + + // Read image + img=read_fits(argv[1],0); + sprintf(filename,"%s.cat",argv[1]); + + printf("Image read\n"); + + // Initial transformation + if (argc==7) { + s=atof(argv[2]); + img.ra0=atof(argv[3]); + img.de0=atof(argv[4]); + q=atof(argv[5]); + mag=atof(argv[6]); + } else { + file=fopen("position.txt","r"); + if (file==NULL) { + fprintf(stderr,"No position file found\n"); + return 0; + } + fscanf(file,"%s %s",sra,sde); + fclose(file); + + // Get parameters + img.ra0=15.0*sex2dec(sra); + img.de0=sex2dec(sde); + + // Hour angle + h=gmst(img.mjd)+m.lng-img.ra0; + q=atan2(sin(h*D2R),(tan(m.lat*D2R)*cos(img.de0*D2R)-sin(img.de0*D2R)*cos(h*D2R)))*R2D; + printf("Hour angle: %.3f deg, parallactic angle: %.3f deg\n",h,q); + } + img.x0=0.5*(float) img.naxis1; + img.y0=0.5*(float) img.naxis2; + + // Read catalogs + cat=read_pixel_catalog(filename); + if (s==0.0) + ast=read_astrometric_catalog(starfile,mag,-36.15,33.22,-q); + else + ast=read_astrometric_catalog(starfile,mag,-s,s,-q); + + // Plot image + cpgopen("/xs"); + cpgwnad(0.0,img.naxis1,0.0,img.naxis2); + cpgsfs(2); + cpgctab (heat_l,heat_r,heat_g,heat_b,5,1.0,0.5); + + // For ever loop + for (;;) { + if (redraw==1) { + cpgimag(img.z,img.naxis1,img.naxis2,1,img.naxis1,1,img.naxis2,img.zmin,img.zmax,tr); + cpgbox("BCTSNI",0.,0,"BCTSNI",0.,0); + + // Plot catalogs + cpgsci(3); + for (i=0;i=3) { + fit_transformation(cat,ast,nselect); + ast=reread_astrometric_catalog(starfile,mag+1); + redraw=1; + } + + // Reread + if (c=='r') { + ast=reread_astrometric_catalog(starfile,mag+1); + redraw=1; + } + + // Select pixel catalog + if (c=='a' && click==0) { + i=select_nearest(cat,x,y); + cat.select[i]=nselect+1; + redraw=1; + click=1; + } + + // Select catalog + if (c=='b' && click==1) { + i=select_nearest(ast,x,y); + ast.select[i]=nselect+1; + redraw=1; + click=0; + nselect++; + } + + // Print + if (c=='p') { + + } + + // Match catalogs + if (c=='m') { + nselect=match_catalogs(&cat,&ast,10.0); + redraw=1; + } + } + cpgend(); + + return 0; +} + +// Read fits image +struct image read_fits(char *filename,int pnum) +{ + int i,j,k,l,m; + qfitsloader ql; + char key[FITS_LINESZ+1] ; + struct image img; + float s1,s2,avg,std; + + // Set plane + ql.xtnum = 0; + ql.pnum = pnum; + + // Set loadtype + ql.ptype = PTYPE_FLOAT; + + // Set filename + ql.filename=filename; + + // Image size + img.naxis1=atoi(qfits_query_hdr(filename,"NAXIS1")); + img.naxis2=atoi(qfits_query_hdr(filename,"NAXIS2")); + img.mjd=atof(qfits_query_hdr(filename,"MJD-OBS")); + + // Initialize load + if (qfitsloader_init(&ql) != 0) + printf("Error initializing data loading\n"); + + // Test load + if (qfits_loadpix(&ql) != 0) + printf("Error loading actual data\n"); + + // Allocate image memory + img.z=(float *) malloc(sizeof(float) * img.naxis1*img.naxis2); + + // Fill z array + for (i=0,l=0,m=0;i 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Get a x and y from a RA and Decl +void forward(double ra0,double de0,double ra,double de,double *x,double *y) +{ + int i; + char pcode[4]="STG"; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(pcode,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd(pcode,ra,de,&cel,&phi,&theta,&prj,x,y)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + *x *=3600.; + *y *=3600.; + + return; +} + +// Linear 2D fit +void lfit2d(float *x,float *y,float *z,int n,float *a) +{ + int i; + double chisq; + gsl_matrix *X,*cov; + gsl_vector *yy,*w,*c; + + X=gsl_matrix_alloc(n,3); + yy=gsl_vector_alloc(n); + w=gsl_vector_alloc(n); + + c=gsl_vector_alloc(3); + cov=gsl_matrix_alloc(3,3); + + // Fill matrices + for(i=0;i0) { + if (strstr(line,"#")!=NULL) + continue; + sscanf(line,"%f %f %f",&c.x[i],&c.y[i],&c.mag[i]); + c.select[i]=0; + i++; + } + fclose(file); + c.n=i; + + return c; +} + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// Precess a celestial position +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de) +{ + double t0,t; + double zeta,z,theta; + double a,b,c; + + // Angles in radians + ra0*=D2R; + de0*=D2R; + + // Time in centuries + t0=(mjd0-51544.5)/36525.0; + t=(mjd-mjd0)/36525.0; + + // Precession angles + zeta=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + zeta+=(0.30188-0.000344*t0)*t*t+0.017998*t*t*t; + zeta*=D2R/3600.0; + z=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + z+=(1.09468+0.000066*t0)*t*t+0.018203*t*t*t; + z*=D2R/3600.0; + theta=(2004.3109-0.85330*t0-0.000217*t0*t0)*t; + theta+=-(0.42665+0.000217*t0)*t*t-0.041833*t*t*t; + theta*=D2R/3600.0; + + a=cos(de0)*sin(ra0+zeta); + b=cos(theta)*cos(de0)*cos(ra0+zeta)-sin(theta)*sin(de0); + c=sin(theta)*cos(de0)*cos(ra0+zeta)+cos(theta)*sin(de0); + + *ra=(atan2(a,b)+z)*R2D; + *de=asin(c)*R2D; + + if (*ra<360.0) + *ra+=360.0; + if (*ra>360.0) + *ra-=360.0; + + return; +} + +// Convert Sexagesimal into Decimal +double sex2dec(char *s) +{ + double x; + float deg,min,sec; + char t[LIM]; + + strcpy(t,s); + + deg=fabs(atof(strtok(t," :"))); + min=fabs(atof(strtok(NULL," :"))); + sec=fabs(atof(strtok(NULL," :"))); + + x=(double) deg+(double) min/60.+(double) sec/3600.; + if (s[0]=='-') x= -x; + + return x; +} diff --git a/rde2iod.c b/rde2iod.c new file mode 100644 index 0000000..f87f5c2 --- /dev/null +++ b/rde2iod.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +#define LIM 128 + +int fgetline(FILE *file,char *s,int lim); +int find_satno(char *desig0) +{ + FILE *file; + int satno=99999,status; + char desig[16]; + char *env,filename[LIM]; + + env=getenv("ST_DATADIR"); + sprintf(filename,"%s/data/desig.txt",env); + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"Designation file not found!\n"); + exit(0); + } + while (!feof(file)) { + status=fscanf(file,"%d %s",&satno,desig); + if (strcmp(desig,desig0)==0) + break; + } + fclose(file); + + return satno; +} + + +int main(int argc,char *argv[]) +{ + FILE *file; + char line[LIM]; + int intidy,intido,piece,site,year,month,day,hour,min,sec,fsec,satno; + char desig[16],pdesig[16]; + int format,epoch,dummy,icsec; + float csec,cang; + int rah,ram,rafm,ded,dem,defm; + float tm,tx,am,ax; + char sign; + int lineno=0; + + file=fopen(argv[1],"r"); + while (fgetline(file,line,LIM)>0) { + if (strncmp(line,"2420",4)==0 && lineno==0) { + lineno++; + sscanf(line,"%04d %02d%02d",&site,&year,&month); + sscanf(line+12,"%1d%1d%1d %3d%1d",&icsec,&dummy,&format,&dummy,&epoch); + + csec=0.1*icsec; + cang=dummy; + + // Year switch + if (year>50) + year+=1900; + else + year+=2000; + + // Time accuracy + tx=floor(log10(csec))+8; + tm=floor(csec/pow(10.0,tx-8)); + + // angle accuracy + ax=floor(log10(cang))+8; + am=floor(cang/pow(10.0,ax-8)); + + if (ax>9.0) { + ax=9.0; + am=9.0; + } + + continue; + } + if (strlen(line)<5 && lineno==1) { + sscanf(line,"%d",&day); + if (day==999) + break; + continue; + } + + // Skip wrong lines + if (!isdigit(line[0])) + continue; + // Skip short lines + if (strlen(line)<31) + continue; + + // Scan line + sscanf(line,"%02d%03d%02d",&intidy,&intido,&piece); + sscanf(line+8,"%02d%02d%02d.%02d",&hour,&min,&sec,&fsec); + sscanf(line+18,"%02d%02d%d",&rah,&ram,&rafm); + sscanf(line+24,"%c%02d%02d%d",&sign,&ded,&dem,&defm); + fsec*=10.0; + // Format designation + if (piece<26) { + sprintf(desig,"%02d %03d%c",intidy,intido,piece+'A'-1); + sprintf(pdesig,"%02d%03d%c",intidy,intido,piece+'A'-1); + } else { + fprintf(stderr,"Failed to understand designation!\n"); + fprintf(stderr,"%s\n",line); + continue; + } + + // Test data format + if (format!=1) { + fprintf(stderr,"Angle format %d not implemented!\n",format); + fprintf(stderr,"%s\n",line); + continue; + } + + // Fractional RA + if (rafm<10) + rafm*=100; + else if (rafm<100) + rafm*=10; + + // Fractional DE + if (defm<10) + defm*=10; + else if (defm<100) + defm*=1; + + // Get satellite number + satno=find_satno(pdesig); + + // Format IOD line + printf("%05d %s %04d G %04d%02d%02d%02d%02d%02d%03d %1.0f%1.0f %d%d ",satno,desig,site,year,month,day,hour,min,sec,fsec,tm,tx,format,epoch); + printf("%02d%02d%03d%c%02d%02d%02d %1.0f%1.0f\n",rah,ram,rafm,sign,ded,dem,defm,am,ax); + } + fclose(file); + + return 0; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + diff --git a/reduce.c b/reduce.c new file mode 100644 index 0000000..a339a2f --- /dev/null +++ b/reduce.c @@ -0,0 +1,1243 @@ +#include +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" + +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define LIM 128 + +struct image { + char filename[64]; + int naxis1,naxis2,naxis3,nframes; + float *zavg,*zstd,*zmax,*znum,*zd; + int *mask; + char nfd[32]; + double ra0,de0; + float x0,y0; + float a[3],b[3],xrms,yrms; + double mjd; + float *dt,exptime; + int cospar; +}; +struct selection { + int state,fit; + float x0,y0,x1,y1; + float w,zmin; + float a,ca,sa,r; + float tmid,tmin,tmax,ax[2],sax[2],ay[2],say[2],chi2x,chi2y; +}; +struct observation { + int satno,cospar; + char desig[16],conditions,behavior; + double mjd,ra,de; + float terr,perr,tmid; + char nfd[32],pos[32]; + int epoch,type; + char iod_line[80]; + float x[3],y[3]; + int state; +}; +struct track { + float x0,y0,x1,y1,texp; + int satno; +} trk; +int iobject=0; + +struct image read_fits(char *filename); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); +void reverse(double ra0,double de0,double x,double y,double *ra,double *de); +double nfd2mjd(char *date); +double date2mjd(int year,int month,double day); +void mjd2date(double mjd,char *date); +void dec2sex(double x,char *s,int type); +float linear_fit(float x[],float y[],int n,float a[],float sa[]); +int fgetline(FILE *file,char *s,int lim); + +// MJD to DOY +double mjd2doy(double mjd,int *yr) +{ + int year,month,k=2; + int day; + double doy; + char nfd[32]; + + mjd2date(mjd,nfd); + + sscanf(nfd,"%04d",&year); + sscanf(nfd+4,"%02d",&month); + sscanf(nfd+6,"%02d",&day); + + if (year%4==0 && year%400!=0) + k=1; + + doy=floor(275.0*month/9.0)-k*floor((month+9.0)/12.0)+day-30; + + *yr=year; + + return doy; +} + +// Reduce point +void reduce_point(struct observation *obs,struct image img,float tmid,float x,float y) +{ + int iframe,k; + double ra,de,rx,ry; + float dx,dy,dt; + double mjd; + char nfd[32],sra[15],sde[15]; + + // Transform position + dx=x-img.x0; + dy=y-img.y0; + rx=img.a[0]+img.a[1]*dx+img.a[2]*dy; + ry=img.b[0]+img.b[1]*dx+img.b[2]*dy; + reverse(img.ra0,img.de0,rx,ry,&ra,&de); + + dec2sex(ra/15.0,sra,0); + dec2sex(de,sde,1); + + // Get time + k=(int) x + img.naxis1*(int) y; + iframe=(int) img.znum[k]; + if (tmid<0.0) + dt=img.dt[iframe]; + else + dt=tmid; + mjd=nfd2mjd(img.nfd)+(double) dt/86400.0; + mjd2date(mjd,nfd); + + // Copy + strcpy(obs->nfd,nfd); + sprintf(obs->pos,"%s%s",sra,sde); + + return; +} + +void compute_cuts(float *z,int *mask,int n,float *zmin,float *zmax,float lcut,float hcut) +{ + int i,m; + float s1,s2; + float avg,std; + + for (i=0,s1=0.0,s2=0.0,m=0;i1) { + // cpgmove(s.x0,s.y0); + // cpgdraw(s.x1,s.y1); + // } + + if (s.state==2) { + for (i=0;i<5;i++) { + if (i==0 || i==4) { + dx=-s.w; + dy=-s.w; + } else if (i==1) { + dx=-s.w; + dy=s.w; + } else if (i==2) { + dx=s.w; + dy=s.w; + } else if (i==3) { + dx=s.w; + dy=-s.w; + } + dx=0.0; + if (i<2 || i==4) { + x=s.ca*dx-s.sa*dy+s.x0; + y=s.sa*dx+s.ca*dy+s.y0; + } else { + x=s.ca*dx-s.sa*dy+s.x1; + y=s.sa*dx+s.ca*dy+s.y1; + } + + if (i==0) + cpgmove(x,y); + else + cpgdraw(x,y); + + } + } + cpgsci(1); + + + + return; +} + +void apply_mask(struct image *img,struct selection s) +{ + int i,j,k; + float x,y,dx,dy; + + for (i=0;inaxis1;i++) { + for (j=0;jnaxis2;j++) { + k=i+img->naxis1*j; + if (img->mask[k]==0) + continue; + dx=(float) i-s.x0; + dy=(float) j-s.y0; + x=s.ca*dx+s.sa*dy; + y=-s.sa*dx+s.ca*dy; + if (x>=0.0 && x<=s.r && y>-s.w && yzmax[k]>s.zmin) { + img->mask[k]=1; + } else { + img->mask[k]=0; + } + } + } + + return; +} + +void mask_pixel(struct image *img,float x,float y) +{ + int i,j,k,kmin,i0,j0,flag; + float r,rmin; + + i0=(int) x; + j0=(int) y; + + // Find nearest pixel + for (i=0,flag=0;inaxis1;i++) { + for (j=0;jnaxis2;j++) { + k=i+img->naxis1*j; + r=sqrt(pow(i-i0,2)+pow(j-j0,2)); + if (img->mask[k]==0) + continue; + if (flag==0 || rmask[kmin]=0; + + return; +} + + + +void fit(struct observation *obs,struct image img) +{ + int i,j,k,l,n; + float *t,*dt,*x,*y; + float tmin,tmax,tmid; + float chi2x,chi2y,ax[2],sax[2],ay[2],say[2]; + + // Count number of points + for (i=0,n=0;itmax) tmax=t[i]; + } + } + tmid=0.5*(tmin+tmax); + printf("Using points between %.3f and %.3f\n",tmin,tmax); + + // Shift in time + for (i=0;ix[0]=ax[0]; + obs->y[0]=ay[0]; + obs->x[1]=ax[0]+ax[1]*(tmin-tmid); + obs->y[1]=ay[0]+ay[1]*(tmin-tmid); + obs->x[2]=ax[0]+ax[1]*(tmax-tmid); + obs->y[2]=ay[0]+ay[1]*(tmax-tmid); + obs->state=1; + + // Reduce point + reduce_point(obs,img,tmid,ax[0],ay[0]); + + // Free + free(t); + free(dt); + free(x); + free(y); + + return; +} + +void format_iod_line(struct observation *obs) +{ + int mt,xt,mp,xp; + char string[10]; + + // Time format + sprintf(string,"%7.1e",obs->terr); + mt=string[0]-'0'; + xt=atoi(string+4)+8; + + // Position format + if (obs->type==2) { + sprintf(string,"%7.1e",obs->perr); + mp=string[0]-'0'; + xp=atoi(string+4)+8; + } else { + printf("Position format not implemented!\n"); + } + + sprintf(obs->iod_line,"%05d %c%c %-6s %04d %c %-17s %d%d %d%d %-14s %d%d %c", + obs->satno, + obs->desig[0],obs->desig[1], + obs->desig+2, + obs->cospar, + obs->conditions, + obs->nfd, + mt,xt, + obs->type,obs->epoch, + obs->pos, + mp,xp, + obs->behavior); + + return; +} + +void find_designation(int satno0,char *desig0) +{ + FILE *file; + int satno; + char desig[16]; + char *env,filename[128]; + + // Environment variables + env=getenv("ST_DATADIR"); + sprintf(filename,"%s/data/desig.txt",env); + + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"Designation file not found!\n"); + exit(0); + } + while (!feof(file)) { + fscanf(file,"%d %s",&satno,desig); + if (satno==satno0) { + strcpy(desig0,desig); + break; + } + } + fclose(file); + + return; +} + +void write_observation(struct observation obs) +{ + FILE *file; + + file=fopen("observations.txt","a"); + fprintf(file,"%s\n",obs.iod_line); + fclose(file); + + printf("Observation written\n"); + + return; +} + +void track(char *fileroot,struct observation obs,struct image *img,float frac) +{ + FILE *file; + char line[LIM],filename[LIM]; + int flag=0,satno; + float x0,y0,x1,y1,texp; + int i,j,k,l,k0; + int di,dj; + float *z; + int *wt; + float dxdn,dydn,dx,dy; + + sprintf(filename,"%s.id",fileroot); + + // Open ID file + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"ID file %s not found\n",filename); + return; + } + while (fgetline(file,line,LIM)>0) { + sscanf(line,"%s %f %f %f %f %f %d",filename,&x0,&y0,&x1,&y1,&texp,&satno); + if (satno==obs.satno) + break; + } + fclose(file); + + if (satno!=obs.satno) { + fprintf(stderr,"Object %d not found\n",obs.satno); + return; + } + dxdn=(x1-x0)/(float) img->nframes; + dydn=(y1-y0)/(float) img->nframes; + + // Allocate + z=(float *) malloc(sizeof(float)*img->naxis1*img->naxis2); + wt=(int *) malloc(sizeof(int)*img->naxis1*img->naxis2); + + // Set to zero + for (i=0;inaxis1*img->naxis2;i++) { + z[i]=0.0; + wt[i]=0; + } + + // Loop over frames + for (l=0;lnframes;l++) { + // Offset + dx=dxdn*(l-frac*img->nframes); + dy=dydn*(l-frac*img->nframes); + + // Integer offset + di=(int) floor(dx+0.5); + dj=(int) floor(dy+0.5); + + // Set + for (i=0;inaxis1;i++) { + for (j=0;jnaxis2;j++) { + k=i+img->naxis1*j; + k0=i+di+img->naxis1*(j+dj); + if (i+di>0 && i+dinaxis1 && j+dj>0 && j+djnaxis2) { + wt[k]+=1; + if (img->znum[k0]==l) + z[k]+=img->zmax[k0]; + // else + // z[k]+=img->zavg[k0]; + } + } + } + } + + // Scale + for (i=0;inaxis1*img->naxis2;i++) { + if (wt[i]>0) + img->zd[i]=z[i]/(float) wt[i]; + else + img->zd[i]=z[i]; + } + img->naxis3=5; + + free(z); + free(wt); + + return; +} + +int autotrack(char *fileroot,struct observation obs,struct image *img) +{ + FILE *file; + char line[LIM],filename[LIM]; + int flag=0,satno,satno0=0,i=0,n; + float x0,y0,x1,y1,texp; + int status=0; + + sprintf(filename,"%s.id",fileroot); + + // Open ID file + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"ID file %s not found\n",filename); + return; + } + while (fgetline(file,line,LIM)>0) { + if (strstr(line,"classfd")==NULL) + continue; + sscanf(line,"%s %f %f %f %f %f %d",filename,&trk.x0,&trk.y0,&trk.x1,&trk.y1,&trk.texp,&trk.satno); + if (i==iobject) { + status=1; + break; + } + + i++; + } + fclose(file); + + iobject++; + + return status; +} + +int main(int argc,char *argv[]) +{ + int i,j,k,l; + int iconditions=0,ibehavior=0; + struct image img; + float tr[]={-0.5,1.0,0.0,-0.5,0.0,1.0}; + float heat_l[] = {0.0, 0.2, 0.4, 0.6, 1.0}; + float heat_r[] = {0.0, 0.5, 1.0, 1.0, 1.0}; + float heat_g[] = {0.0, 0.0, 0.5, 1.0, 1.0}; + float heat_b[] = {0.0, 0.0, 0.0, 0.3, 1.0}; + float x,y,frac=0.5; + char c; + float xmin,xmax,ymin,ymax,zmin,zmax,*z; + float width; + int redraw=1,layer=2,status; + float lcut=4,hcut=6; + struct selection s; + struct observation obs; + char conditions[]="EGFPBT",behavior[]="EFIRSX"; + char text[128]; + double doy,mjd; + int year; + char *env; + + env=getenv("ST_COSPAR"); + + // Default observation + obs.satno=99999; + strcpy(obs.desig,"99999U"); + obs.cospar=atoi(env); + obs.conditions='G'; + strcpy(obs.nfd,"YYYYMMDDHHMMSSsss"); + obs.terr=0.1; + strcpy(obs.pos,"HHMMmmm+DDMMmm"); + strcpy(obs.iod_line,""); + obs.perr=0.3; + obs.epoch=5; + obs.type=2; + obs.behavior='S'; + obs.state=0; + + // Set track + trk.satno=0; + + // Read image + img=read_fits(argv[1]); + + // Allocate + z=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2); + + // Get fake designation + mjd=nfd2mjd(img.nfd); + doy=mjd2doy(mjd,&year); + sprintf(obs.desig,"%02d%03.0lfA",year-2000,doy+500); + + cpgopen("/xs"); + cpgpap(0.,1.0); + cpgask(0); + cpgsch(0.8); + + // Default limits + xmin=0.0; + xmax=(float) img.naxis1; + ymin=0.0; + ymax=(float) img.naxis2; + width=img.naxis1; + + // Default selection + s.state=0; + s.w=10; + s.zmin=0; + s.fit=0; + + // Set cospas + obs.cospar=img.cospar; + + for (;;) { + if (redraw==1) { + cpgeras(); + + cpgsvp(0.1,0.95,0.1,0.95); + cpgwnad(xmin,xmax,ymin,ymax); + cpglab("x (pix)","y (pix)"," "); + cpgsfs(2); + cpgctab (heat_l,heat_r,heat_g,heat_b,5,1.0,0.5); + + sprintf(text,"UT Date: %.23s COSPAR ID: %04d",img.nfd+1,img.cospar); + cpgmtxt("T",6.0,0.0,0.0,text); + sprintf(text,"R.A.: %10.5f (%4.1f'') Decl.: %10.5f (%4.1f'')",img.ra0,img.xrms,img.de0,img.yrms); + cpgmtxt("T",4.8,0.0,0.0,text); + sprintf(text,"FoV: %.2f\\(2218)x%.2f\\(2218) Scale: %.2f''x%.2f'' pix\\u-1\\d",img.naxis1*sqrt(img.a[1]*img.a[1]+img.b[1]*img.b[1])/3600.0,img.naxis2*sqrt(img.a[2]*img.a[2]+img.b[2]*img.b[2])/3600.0,sqrt(img.a[1]*img.a[1]+img.b[1]*img.b[1]),sqrt(img.a[2]*img.a[2]+img.b[2]*img.b[2])); + cpgmtxt("T",3.6,0.0,0.0,text); + + // Apply mask + for (i=0;i0.49 && frac<0.51) + frac=1.0; + else if (frac>0.51) + frac=0.0; + else if (frac<0.49) + frac=0.5; + printf("Fraction: %.1f\n",frac); + iobject=0; + } + + // Reduce + if (c=='M' || c=='D') { + reduce_point(&obs,img,frac*img.exptime,x,y); + obs.x[0]=x; + obs.y[0]=y; + obs.state=2; + redraw=1; + continue; + } + + // Get designation + if (c=='d') { + printf("Provide satellite number: "); + scanf("%d",&obs.satno); + find_designation(obs.satno,obs.desig); + redraw=1; + continue; + } + + // Toggle condition + if (c=='C') { + iconditions++; + if (iconditions>strlen(conditions)-1) + iconditions=0; + obs.conditions=conditions[iconditions]; + redraw=1; + continue; + } + // Toggle behavior + if (c=='B') { + ibehavior++; + if (ibehavior>strlen(behavior)-1) + ibehavior=0; + obs.behavior=behavior[ibehavior]; + redraw=1; + continue; + } + + // Reread + if (c=='R') { + img=read_fits(argv[1]); + redraw=1; + continue; + } + + // Start + if (c=='s' && s.state==0) { + s.x0=x; + s.y0=y; + s.state=1; + redraw=1; + continue; + } + + // Fit + if (c=='F') { + fit(&obs,img); + redraw=1; + continue; + } + + // End + if (c=='f' && s.state==1) { + s.x1=x; + s.y1=y; + s.a=atan2(s.y1-s.y0,s.x1-s.x0); + s.ca=cos(s.a); + s.sa=sin(s.a); + s.r=sqrt(pow(s.x0-s.x1,2)+pow(s.y0-s.y1,2)); + s.state=2; + apply_mask(&img,s); + s.zmin=zmin; + redraw=1; + continue; + } + + // Mask pixel + if (c=='X' && s.state!=0) { + mask_pixel(&img,x,y); + apply_mask(&img,s); + redraw=1; + continue; + } + + // Change level + if (c=='+' || c=='=') { + s.zmin+=1.0; + apply_mask(&img,s); + redraw=1; + continue; + } + if (c=='-') { + s.zmin-=1.0; + apply_mask(&img,s); + redraw=1; + continue; + } + + // Mean + if (isdigit(c)) { + layer=c-'0'-1; + redraw=1; + continue; + } + + // Adjust cuts + if (c=='v') { + lcut*=2; + hcut*=2; + redraw=1; + continue; + } + if (c=='b') { + lcut/=2; + hcut/=2; + if (lcut<0.5) lcut=0.5; + if (hcut<0.75) hcut=0.75; + redraw=1; + continue; + } + + // Center + if (c=='c') { + xmin=x-0.5*width; + xmax=x+0.5*width; + ymin=y-0.5*width*img.naxis2/img.naxis1; + ymax=y+0.5*width*img.naxis2/img.naxis1; + redraw=1; + continue; + } + + // Zoom + if (c=='z') { + width/=2; + xmin=x-0.5*width; + xmax=x+0.5*width; + ymin=y-0.5*width*img.naxis2/img.naxis1; + ymax=y+0.5*width*img.naxis2/img.naxis1; + redraw=1; + continue; + } + + // Unzoom + if (c=='x') { + width*=2; + xmin=x-0.5*width; + xmax=x+0.5*width; + ymin=y-0.5*width*img.naxis2/img.naxis1; + ymax=y+0.5*width*img.naxis2/img.naxis1; + redraw=1; + continue; + } + + // Reset + if (c=='r') { + xmin=0.0; + xmax=(float) img.naxis1; + ymin=0.0; + ymax=(float) img.naxis2; + width=img.naxis1; + lcut=4.0; + hcut=6.0; + s.state=0; + s.fit=0; + obs.state=0; + redraw=1; + continue; + } + } + + cpgend(); + + free(img.zavg); + free(img.zstd); + free(img.zmax); + free(img.znum); + free(img.zd); + + return 0; +} + +// Read fits image +struct image read_fits(char *filename) +{ + int i,j,k,l,m; + qfitsloader ql; + char key[FITS_LINESZ+1]; + char val[FITS_LINESZ+1]; + struct image img; + + // Copy filename + strcpy(img.filename,filename); + + // Image size + img.naxis1=atoi(qfits_query_hdr(filename,"NAXIS1")); + img.naxis2=atoi(qfits_query_hdr(filename,"NAXIS2")); + img.naxis3=atoi(qfits_query_hdr(filename,"NAXIS3")); + img.nframes=atoi(qfits_query_hdr(filename,"NFRAMES")); + + // MJD + img.mjd=(double) atof(qfits_query_hdr(filename,"MJD-OBS")); + strcpy(img.nfd,qfits_query_hdr(filename,"DATE-OBS")); + img.exptime=atof(qfits_query_hdr(filename,"EXPTIME")); + + // COSPAR ID + img.cospar=atoi(qfits_query_hdr(filename,"COSPAR")); + + // Transformation + img.mjd=atof(qfits_query_hdr(filename,"MJD-OBS")); + img.ra0=atof(qfits_query_hdr(filename,"CRVAL1")); + img.de0=atof(qfits_query_hdr(filename,"CRVAL2")); + img.x0=atof(qfits_query_hdr(filename,"CRPIX1")); + img.y0=atof(qfits_query_hdr(filename,"CRPIX2")); + img.a[0]=0.0; + img.a[1]=3600.0*atof(qfits_query_hdr(filename,"CD1_1")); + img.a[2]=3600.0*atof(qfits_query_hdr(filename,"CD1_2")); + img.b[0]=0.0; + img.b[1]=3600.0*atof(qfits_query_hdr(filename,"CD2_1")); + img.b[2]=3600.0*atof(qfits_query_hdr(filename,"CD2_2")); + img.xrms=3600.0*atof(qfits_query_hdr(filename,"CRRES1")); + img.yrms=3600.0*atof(qfits_query_hdr(filename,"CRRES2")); + + // Timestamps + img.dt=(float *) malloc(sizeof(float)*img.nframes); + for (i=0;i2) + year=c-4716; + else + year=c-4715; + + day=(int) floor(dday); + x=24.0*(dday-day); + x=3600.*fabs(x); + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + hour=x; + fsec=1000.0*(sec-floor(sec)); + sprintf(date,"%04d%02d%02d%02d%02d%02.0f%03.0f",(int) year,(int) month,(int) day,(int) hour,(int) min,floor(sec),fsec); + + return; +} + +// Convert Decimal into Sexagesimal +void dec2sex(double x,char *s,int type) +{ + int i; + double sec,deg,min,fmin; + char sign; + + sign=(x<0 ? '-' : '+'); + x=60.*fabs(x); + + min=fmod(x,60.); + x=(x-min)/60.; + // deg=fmod(x,60.); + deg=x; + if (type==0) + fmin=1000.0*(min-floor(min)); + else + fmin=100.0*(min-floor(min)); + + if (type==0) + sprintf(s,"%02.0f%02.0f%03.0f",deg,floor(min),fmin); + else + sprintf(s,"%c%02.0f%02.0f%02.0f",sign,deg,floor(min),fmin); + + return; +} + +// Linear least squares fit +float linear_fit(float x[],float y[],int n,float a[],float sa[]) +{ + int i; + float sum,sumx,sumy,sumxx,sumxy; + float w,d,chi2,covar,r; + + // Compute sums + sum=sumx=sumy=sumxx=sumxy=0.; + for (i=0;i 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\t') + c=' '; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} diff --git a/residuals.c b/residuals.c new file mode 100644 index 0000000..2ad717d --- /dev/null +++ b/residuals.c @@ -0,0 +1,526 @@ +#include +#include +#include +#include +#include "cel.h" +#include "sgdp4h.h" +#include + +#define LIM 80 +#define NMAX 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define XKMPAU 149597879.691 // AU in km +#define FLAT (1.0/298.257) + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct point { + int flag,satno; + double mjd,ra,de; + float st,sr; + char iod_line[LIM]; + xyz_t obspos; +}; +struct site { + int id; + double lng,lat; + float alt; + char observer[64]; +}; +struct data { + int n; + struct point *p; +} ; +struct point decode_iod_observation(char *iod_line); +struct site get_site(int site_id); +int fgetline(FILE *file,char *s,int lim); +double modulo(double x,double y); +double gmst(double mjd); +double dgmst(double mjd); +double date2mjd(int year,int month,double day); +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de); +void usage(); +void obspos_xyz(double mjd,double lng,double lat,float alt,xyz_t *pos,xyz_t *vel); +struct data read_data(char *filename); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); + +void compute_residual(char *filename,struct point p) +{ + int i,imode; + FILE *file; + orbit_t orb; + xyz_t satpos,satvel; + double dx,dy,dz; + double r[2],ra,de; + double rx[2],ry[2],dr,dt,drx,dry; + double jd; + double age; + + // Open catalog + file=fopen(filename,"r"); + if (file==NULL) + fatal_error("Failed to open %s\n",filename); + + // Read TLE + read_twoline(file,p.satno,&orb); + fclose(file); + + // Check for match + if (orb.satno!=p.satno) { + // fprintf(stderr,"object %d not found in %s\n",p.satno,filename); + return; + } + + // Initialize + imode=init_sgdp4(&orb); + if (imode==SGDP4_ERROR) { + fprintf(stderr,"Error initializing SGDP4\n"); + exit(0); + } + + for (i=0;i<2;i++) { + jd=p.mjd+2400000.5+(double) i/86400; + + // Compute position + satpos_xyz(jd,&satpos,&satvel); + age=jd-SGDP4_jd0; + + // compute difference vector + dx=satpos.x-p.obspos.x; + dy=satpos.y-p.obspos.y; + dz=satpos.z-p.obspos.z; + + // Celestial position + r[i]=sqrt(dx*dx+dy*dy+dz*dz); + ra=modulo(atan2(dy,dx)*R2D,360.0); + de=asin(dz/r[i])*R2D; + + // Compute offset + forward(p.ra,p.de,ra,de,&rx[i],&ry[i]); + } + drx=rx[1]-rx[0]; + dry=ry[1]-ry[0]; + dt=-(rx[0]*drx+ry[0]*dry)/(drx*drx+dry*dry); + dr=sqrt(pow(dry*rx[0]-drx*ry[0],2)/(drx*drx+dry*dry)); + if ((-rx[0]*drx-ry[0]*dry)<0.0) + dr*=-1; + printf("%s | %8.3f deg %8.3f sec %5.1f day, %.1f km\n",p.iod_line,dr,dt,age,r[0]); + + return; +} + +int main(int argc,char *argv[]) +{ + int i,arg=0; + struct data d; + char *datafile,catalog[LIM]; + char *env; + + env=getenv("ST_TLEDIR"); + sprintf(catalog,"%s/classfd.tle",env); + // Decode options + while ((arg=getopt(argc,argv,"d:c:h"))!=-1) { + switch(arg) { + case 'd': + datafile=optarg; + break; + + case 'c': + strcpy(catalog,optarg); + break; + + case 'h': + usage(); + return 0; + break; + + default: + usage(); + return 0; + } + } + + // Read data + d=read_data(datafile); + for (i=0;ix=gc*cos(lat*D2R)*cos(theta*D2R)*XKMPER; + pos->y=gc*cos(lat*D2R)*sin(theta*D2R)*XKMPER; + pos->z=gs*sin(lat*D2R)*XKMPER; + vel->x=-gc*cos(lat*D2R)*sin(theta*D2R)*XKMPER*dtheta; + vel->y=gc*cos(lat*D2R)*cos(theta*D2R)*XKMPER*dtheta; + vel->z=0.0; + + return; +} + +// Precess a celestial position +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de) +{ + double t0,t; + double zeta,z,theta; + double a,b,c; + + // Angles in radians + ra0*=D2R; + de0*=D2R; + + // Time in centuries + t0=(mjd0-51544.5)/36525.0; + t=(mjd-mjd0)/36525.0; + + // Precession angles + zeta=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + zeta+=(0.30188-0.000344*t0)*t*t+0.017998*t*t*t; + zeta*=D2R/3600.0; + z=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + z+=(1.09468+0.000066*t0)*t*t+0.018203*t*t*t; + z*=D2R/3600.0; + theta=(2004.3109-0.85330*t0-0.000217*t0*t0)*t; + theta+=-(0.42665+0.000217*t0)*t*t-0.041833*t*t*t; + theta*=D2R/3600.0; + + a=cos(de0)*sin(ra0+zeta); + b=cos(theta)*cos(de0)*cos(ra0+zeta)-sin(theta)*sin(de0); + c=sin(theta)*cos(de0)*cos(ra0+zeta)+cos(theta)*sin(de0); + + *ra=(atan2(a,b)+z)*R2D; + *de=asin(c)*R2D; + + if (*ra<360.0) + *ra+=360.0; + if (*ra>360.0) + *ra-=360.0; + + return; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\t') + c=' '; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +void usage() +{ + printf("bla\n"); + + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// Read data +struct data read_data(char *filename) +{ + int i=0; + char line[LIM]; + FILE *file; + struct data d; + + // Open file + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"Failed to open %s\n",filename); + exit(1); + } + + // Count lines + while (fgetline(file,line,LIM)>0) + i++; + d.n=i; + + // Allocate + d.p=(struct point *) malloc(sizeof(struct point)*d.n); + + // Rewind file + rewind(file); + + // Read data + i=0; + while (fgetline(file,line,LIM)>0) + d.p[i++]=decode_iod_observation(line); + + // Close file + fclose(file); + + return d; +} + +// Get a x and y from an AZI, ALT +void forward(double ra0,double de0,double ra,double de,double *x,double *y) +{ + int i; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset("STG",&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd("STG",ra,de,&cel,&phi,&theta,&prj,x,y)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + + return; +} diff --git a/runsched.c b/runsched.c new file mode 100644 index 0000000..6a5ce2f --- /dev/null +++ b/runsched.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PORT 7264 +#define IP "127.0.0.1" +#define LIM 2048 +#define NMAX 128 + +#define SCHEDULED 0 +#define STARTED 1 +#define FINISHED 2 + +struct observation { + char stime[20],sra[15],sde[15]; + time_t ptime; + float dt; +}; + +int fgetline(FILE *file,char *s,int lim); +void send_position(char *sra,char *sde); +time_t decode_time(char *stm); + + +int main(int argc, char *argv[]) +{ + int i=0,nobs,flag=0; + time_t rawtime,aimtime; + struct tm *ptm,*rtm; + char buf[20],line[LIM],stm[20],sra[15],sde[15],pra[15],pde[15]; + FILE *file; + struct observation obs[NMAX]; + + // For ever loop + for (;;) { + // Read file + i=0; + file=fopen("schedule.txt","r"); + while (fgetline(file,line,LIM)>0) { + sscanf(line,"%s %s %s",obs[i].stime,obs[i].sra,obs[i].sde); + obs[i].ptime=decode_time(obs[i].stime); + + i++; + } + fclose(file); + nobs=i; + + // Get local time + time(&rawtime); + + // Print UTC time + ptm=gmtime(&rawtime); + strftime(buf,20,"%Y-%m-%dT%H:%M:%S",ptm); + + // Compute time differences + for (i=0;i0.0) { + printf("%4.0f %s %s %s\n",obs[i].dt,obs[i].stime,obs[i].sra,obs[i].sde); + break; + } else if (obs[i].dt==0) { + printf("Slewing to %s %s\n",obs[i].sra,obs[i].sde); + send_position(obs[i].sra,obs[i].sde); + } + } + + // Sleep + sleep(1); + } + + return 0; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Send new position to telescope +void send_position(char *sra,char *sde) +{ + int skt; + struct hostent *he; + struct sockaddr_in addr; + char packet[LIM]; + FILE *file; + + sprintf(packet,"%s%s",sra,sde); + + // Send TCP packet + skt=socket(AF_INET,SOCK_STREAM,0); + addr.sin_family=AF_INET; + addr.sin_port=htons(PORT); + he=gethostbyname(IP); + bcopy(he->h_addr,(struct in_addr *) &addr.sin_addr,he->h_length); + if(connect(skt,(struct sockaddr *) &addr,sizeof(addr))<0) { + fprintf(stderr,"Connection refused by remote host.\n"); + return; + } + write(skt,packet,strlen(packet)); + close(skt); + + // Set restart + file=fopen("/media/video/satobs/control/state.txt","w"); + if (file!=NULL) { + fprintf(file,"restart"); + fclose(file); + } + + // Set position + file=fopen("/media/video/satobs/control/position.txt","w"); + if (file!=NULL) { + fprintf(file,"%s %s\n",sra,sde); + fclose(file); + } + + return; +} + +// Decode time +time_t decode_time(char *stm) +{ + time_t aimtime; + struct tm *rtm; + int d; + + rtm=gmtime(&aimtime); + sscanf(stm,"%04d-%02d-%02dT%02d:%02d:%02d",&rtm->tm_year,&rtm->tm_mon,&rtm->tm_mday,&rtm->tm_hour,&rtm->tm_min,&rtm->tm_sec); + rtm->tm_year-=1900; + rtm->tm_mon--; + aimtime=mktime(rtm); + + return aimtime; +} diff --git a/satfit.c b/satfit.c new file mode 100644 index 0000000..c3ef58b --- /dev/null +++ b/satfit.c @@ -0,0 +1,1202 @@ +#include +#include +#include +#include +#include "cpgplot.h" +#include "cel.h" +#include "sgdp4h.h" +#include + +#define LIM 80 +#define NMAX 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define XKMPAU 149597879.691 // AU in km +#define FLAT (1.0/298.257) + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct point { + int flag,satno; + double mjd,ra,de,rac,dec; + float st,sr; + char iod_line[LIM]; + double dx,dy,dr,dt; + xyz_t obspos; +}; +struct data { + int n,nsel; + struct point *p; + double chisq,rms; +} d; +struct site { + int id; + double lng,lat; + float alt; + char observer[64]; +}; +orbit_t orb; +struct site get_site(int site_id); +struct point decode_iod_observation(char *iod_line); +int fgetline(FILE *file,char *s,int lim); +double modulo(double x,double y); +double gmst(double mjd); +double dgmst(double mjd); +double date2mjd(int year,int month,double day); +double mjd2doy(double mjd,int *yr); +void mjd2date(double mjd,int *year,int *month,double *day); +void obspos_xyz(double mjd,double lng,double lat,float alt,xyz_t *pos,xyz_t *vel); +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); +struct data read_data(char *filename); +void versafit(int m,int n,double *a,double *da,double (*func)(double *),double dchisq,double tol,char *opt); +double chisq(double a[]); +orbit_t read_tle(char *filename,int satno); +void format_tle(orbit_t orb,char *line1,char *line2); +void highlight(float x0,float y0,float x,float y,int flag); +void time_range(double *mjdmin,double *mjdmax,int flag); +void print_tle(orbit_t orb,char *filename); +void fit(orbit_t orb,int *ia); +void usage(); + +xyz_t get_position(double r0,int i0) +{ + int i; + double rr,drr,r; + xyz_t pos; + double x,y,z; + + // Initial range + rr=100.0; + do { + x=d.p[i0].obspos.x+rr*cos(d.p[i0].ra*D2R)*cos(d.p[i0].de*D2R); + y=d.p[i0].obspos.y+rr*sin(d.p[i0].ra*D2R)*cos(d.p[i0].de*D2R); + z=d.p[i0].obspos.z+rr*sin(d.p[i0].de*D2R); + r=sqrt(x*x+y*y+z*z); + drr=r-r0; + rr-=drr; + } while (fabs(drr)>0.01); + + pos.x=x; + pos.y=y; + pos.z=z; + + return pos; +} + +int circular_fit(void) +{ + int i; + double mjdmin,mjdmax; + int ia[7]={0,0,0,0,0,0,0}; + + // Step 1: select all points + // for (i=0;i1e-5 && i<1000); + printf("%f\n",r0); + return; +} + +int main(int argc,char *argv[]) +{ + int i,j,nobs=0; + int redraw=1,plot_residuals=0,adjust=0,quit=0; + int ia[]={0,0,0,0,0,0,0}; + float dx[]={0.1,0.1,0.35,0.35,0.6,0.6,0.85},dy[]={0.0,-0.25,0.0,-0.25,0.0,-0.25,0.0}; + char c; + int mode=0,posn=0,click=0; + float x0,y0,x,y; + float xmin=0.0,xmax=360.0,ymin=-90.0,ymax=90.0; + char string[64],bstar[10]=" 50000-4",line0[72],line1[72],line2[72],text[10]; + char filename[64]; + int satno=-1; + double mjdmin,mjdmax; + int arg=0; + char *datafile,*catalog; + orbit_t orb0; + + // Decode options + while ((arg=getopt(argc,argv,"d:c:i:ha"))!=-1) { + switch(arg) { + case 'd': + datafile=optarg; + break; + + case 'c': + catalog=optarg; + break; + + case 'i': + satno=atoi(optarg); + break; + + case 'h': + usage(); + return 0; + break; + + case 'a': + adjust=1; + break; + + default: + usage(); + return 0; + } + } + + // Read data + d=read_data(datafile); + time_range(&mjdmin,&mjdmax,1); + + // Read TLE + if (satno>=0) { + orb=read_tle(catalog,satno); + } + + freopen("/tmp/stderr.txt","w",stderr); + + // Adjust + if (adjust==1) { + orb0=orb; + adjust_fit(); + fit(orb,ia); + printf("%05d %8.3f %8.3f %8.3f %s %8.3f\n",satno,DEG(orb.mnan-orb0.mnan),DEG(orb.ascn-orb0.ascn),d.rms,datafile,mjdmin-(SGDP4_jd0-2400000.5)); + plot_residuals=1; + redraw=1; + quit=1; + } + + cpgopen("/xs"); + cpgask(0); + + // For ever loop + for (;;) { + if (redraw==1) { + cpgpage(); + cpgsvp(0.1,0.95,0.0,0.18); + cpgswin(0.0,1.0,-0.5,0.5); + + // Buttons + cpgtext(0.12,-0.05,"Inclination"); + cpgtext(0.372,-0.05,"Eccentricity"); + cpgtext(0.62,-0.05,"Mean Anomaly"); + cpgtext(0.87,-0.05,"B\\u*\\d"); + cpgtext(0.12,-0.3,"Ascending Node"); + cpgtext(0.37,-0.3,"Arg. of Perigee"); + cpgtext(0.62,-0.3,"Mean Motion"); + + // Toggles + for (i=0;i<7;i++) { + cpgpt1(dx[i],dy[i],19); + if (ia[i]==1) { + cpgsci(2); + cpgpt1(dx[i],dy[i],16); + cpgsci(1); + } + } + + // Plot map + cpgsvp(0.1,0.9,0.2,0.9); + cpgswin(xmax,xmin,ymin,ymax); + cpgbox("BCTSN",0.,0,"BCTSN",0.,0); + cpglab("Right Ascension","Declination"," "); + + if (satno>0) { + // Plot tle + format_tle(orb,line1,line2); + cpgmtxt("T",2.0,0.0,0.0,line1); + cpgmtxt("T",1.0,0.0,0.0,line2); + } + + // Plot points + for (i=0;i=1) { + cpgpt1(d.p[i].ra,d.p[i].de,17); + sprintf(text," %d",i+1); + cpgtext(d.p[i].ra,d.p[i].de,text); + if (plot_residuals==1) { + cpgmove(d.p[i].ra,d.p[i].de); + cpgdraw(d.p[i].rac,d.p[i].dec); + } + if (d.p[i].flag==2) { + cpgsci(2); + cpgpt1(d.p[i].ra,d.p[i].de,4); + cpgsci(1); + } + } + } + } + + // Quit + if (quit==1) + break; + + // Get cursor + cpgband(mode,posn,x0,y0,&x,&y,&c); + + // Quit + if (c=='q' || c=='Q') + break; + + // Fit + if (c=='f') { + // Count points + for (i=0,nobs=0;i=1 && c-'0'<8) { + if (ia[c-49]==0) + ia[c-49]=1; + else if (ia[c-49]==1) + ia[c-49]=0; + redraw=1; + continue; + } + + // Circular fit + if (c=='C') { + satno=circular_fit(); + plot_residuals=1; + printf("%.3f\n",d.rms); + ia[0]=ia[1]=ia[4]=ia[5]=1; + redraw=1; + } + + // Change + if (c=='c') { + printf("(1) Inclination, (2) Ascending Node, (3) Eccentricity,\n(4) Arg. of Perigee, (5) Mean Anomaly, (6) Mean Motion,\n(7) B* drag, (8) Epoch, (9) Satellite ID\n\nWhich parameter to change: "); + scanf("%i",&i); + if (i>0 && i<=9) { + printf("\nNew value: "); + fgets(string,64,stdin); + scanf("%s",string); + // if (i==0) strcpy(d.satname,string); + if (i==1) orb.eqinc=RAD(atof(string)); + if (i==2) orb.ascn=RAD(atof(string)); + if (i==3) orb.ecc=atof(string); + if (i==4) orb.argp=RAD(atof(string)); + if (i==5) orb.mnan=RAD(atof(string)); + if (i==6) orb.rev=atof(string); + if (i==7) orb.bstar=atof(string); + if (i==8) { + orb.ep_year=2000+(int) floor(atof(string)/1000.0); + orb.ep_day=atof(string)-1000*floor(atof(string)/1000.0); + } + if (i==9) orb.satno=atoi(string); + redraw=1; + continue; + } + printf("\n================================================================================\n"); + } + + // Zoom + if (c=='z') { + click=1; + mode=2; + } + + // Execute zoom, or box delete + if (c=='A') { + if (click==0) { + click=1; + } else if (click==1 && mode==2) { + xmin=(x0x) ? x0 : x; + ymin=(y0y) ? y0 : y; + + click=0; + mode=0; + redraw=1; + continue; + } else { + click=0; + mode=0; + redraw=1; + continue; + } + } + + // Unzoom + if (c=='r') { + xmin=0.0; + xmax=360.0; + ymin=-90.0; + ymax=90.0; + mode=0; + click=0; + redraw=1; + continue; + } + + // Default tle + if (c=='t') { + orb.satno=99999; + orb.eqinc=0.5*M_PI; + orb.ascn=0.0; + orb.ecc=0.0; + orb.argp=0.0; + orb.mnan=0.0; + orb.rev=14.0; + orb.bstar=0.5e-4; + orb.ep_day=mjd2doy(0.5*(mjdmin+mjdmax),&orb.ep_year); + satno=99999; + print_orb(&orb); + printf("\n================================================================================\n"); + click=0; + redraw=1; + continue; + } + + // Save + x0=x; + y0=y; + } + + cpgend(); + + free(d.p); + + fclose(stderr); + return 0; +} + +// Get observing site +struct site get_site(int site_id) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64]; + struct site s; + char *env,filename[LIM]; + + env=getenv("ST_DATADIR"); + sprintf(filename,"%s/data/sites.txt",env); + + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + // Change to km + alt/=1000.0; + + // Copy site + if (id==site_id) { + s.lat=lat; + s.lng=lng; + s.alt=alt; + s.id=id; + strcpy(s.observer,observer); + } + + } + fclose(file); + + return s; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Greenwich Mean Sidereal Time +double dgmst(double mjd) +{ + double t,dgmst; + + t=(mjd-51544.5)/36525.0; + + dgmst=360.98564736629+t*(0.000387933-t/38710000); + + return dgmst; +} + +// Observer position +void obspos_xyz(double mjd,double lng,double lat,float alt,xyz_t *pos,xyz_t *vel) +{ + double ff,gc,gs,theta,s,dtheta; + + s=sin(lat*D2R); + ff=sqrt(1.0-FLAT*(2.0-FLAT)*s*s); + gc=1.0/ff+alt/XKMPER; + gs=(1.0-FLAT)*(1.0-FLAT)/ff+alt/XKMPER; + + theta=gmst(mjd)+lng; + dtheta=dgmst(mjd)*D2R/86400; + + pos->x=gc*cos(lat*D2R)*cos(theta*D2R)*XKMPER; + pos->y=gc*cos(lat*D2R)*sin(theta*D2R)*XKMPER; + pos->z=gs*sin(lat*D2R)*XKMPER; + vel->x=-gc*cos(lat*D2R)*sin(theta*D2R)*XKMPER*dtheta; + vel->y=gc*cos(lat*D2R)*cos(theta*D2R)*XKMPER*dtheta; + vel->z=0.0; + + return; +} + +// Precess a celestial position +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de) +{ + double t0,t; + double zeta,z,theta; + double a,b,c; + + // Angles in radians + ra0*=D2R; + de0*=D2R; + + // Time in centuries + t0=(mjd0-51544.5)/36525.0; + t=(mjd-mjd0)/36525.0; + + // Precession angles + zeta=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + zeta+=(0.30188-0.000344*t0)*t*t+0.017998*t*t*t; + zeta*=D2R/3600.0; + z=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + z+=(1.09468+0.000066*t0)*t*t+0.018203*t*t*t; + z*=D2R/3600.0; + theta=(2004.3109-0.85330*t0-0.000217*t0*t0)*t; + theta+=-(0.42665+0.000217*t0)*t*t-0.041833*t*t*t; + theta*=D2R/3600.0; + + a=cos(de0)*sin(ra0+zeta); + b=cos(theta)*cos(de0)*cos(ra0+zeta)-sin(theta)*sin(de0); + c=sin(theta)*cos(de0)*cos(ra0+zeta)+cos(theta)*sin(de0); + + *ra=(atan2(a,b)+z)*R2D; + *de=asin(c)*R2D; + + if (*ra<360.0) + *ra+=360.0; + if (*ra>360.0) + *ra-=360.0; + + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// Decode IOD Observations +struct point decode_iod_observation(char *iod_line) +{ + int year,month,iday,hour,min; + int format,epoch,me,xe,sign; + int site_id; + double sec,ra,mm,ss,de,dd,ds,day,mjd0; + char secbuf[6],sn[2],degbuf[3]; + struct point p; + struct site s; + xyz_t vel; + + // Strip newline + iod_line[strlen(iod_line)-1]='\0'; + + // Copy full line + strcpy(p.iod_line,iod_line); + + // Set flag + p.flag=1; + + // Get SSN + sscanf(iod_line,"%5d",&p.satno); + + // Get site + sscanf(iod_line+16,"%4d",&site_id); + s=get_site(site_id); + + // Decode date/time + sscanf(iod_line+23,"%4d%2d%2d%2d%2d%5s",&year,&month,&iday,&hour,&min,secbuf); + sec=atof(secbuf); + sec/=pow(10,strlen(secbuf)-2); + day=(double) iday+(double) hour/24.0+(double) min/1440.0+(double) sec/86400.0; + p.mjd=date2mjd(year,month,day); + + // Get uncertainty in time + sscanf(iod_line+41,"%1d%1d",&me,&xe); + p.st=(float) me*pow(10,xe-8); + + // Get observer position + obspos_xyz(p.mjd,s.lng,s.lat,s.alt,&p.obspos,&vel); + + // Skip empty observations + if (strlen(iod_line)<64 || (iod_line[54]!='+' && iod_line[54]!='-')) + p.flag=0; + + // Get format, epoch + sscanf(iod_line+44,"%1d%1d",&format,&epoch); + + // Read position + sscanf(iod_line+47,"%2lf%2lf%3lf%1s",&ra,&mm,&ss,sn); + sscanf(iod_line+55,"%2lf%2lf%2s",&de,&dd,degbuf); + ds=atof(degbuf); + if (strlen(degbuf)==1) + ds*=10; + sign=(sn[0]=='-') ? -1 : 1; + sscanf(iod_line+62,"%1d%1d",&me,&xe); + p.sr=(float) me*pow(10,xe-8); + + // Decode position + switch(format) + { + // Format 1: RA/DEC = HHMMSSs+DDMMSS MX (MX in seconds of arc) + case 1 : + ra+=mm/60+ss/36000; + de=sign*(de+dd/60+ds/3600); + p.sr/=3600.0; + break; + // Format 2: RA/DEC = HHMMmmm+DDMMmm MX (MX in minutes of arc) + case 2: + ra+=mm/60+ss/60000; + de=sign*(de+dd/60+ds/6000); + p.sr/=60.0; + break; + // Format 3: RA/DEC = HHMMmmm+DDdddd MX (MX in degrees of arc) + case 3 : + ra+=mm/60+ss/60000; + de=sign*(de+dd/100+ds/10000); + break; + // Format 7: RA/DEC = HHMMSSs+DDdddd MX (MX in degrees of arc) + case 7 : + ra+=mm/60+ss/36000; + de=sign*(de+dd/100+ds/10000); + break; + default : + printf("IOD Format not implemented\n"); + p.flag=0; + break; + } + // Convert to degrees + ra*=15.0; + + // Get precession epoch + if (epoch==0) { + p.ra=ra; + p.de=de; + return p; + } else if (epoch==4) { + mjd0=33281.9235; + } else if (epoch==5) { + mjd0=51544.5; + } else { + printf("Observing epoch not implemented\n"); + p.flag=0; + } + + // Precess position + precess(mjd0,ra,de,p.mjd,&p.ra,&p.de); + + return p; +} + +// Get a x and y from an AZI, ALT +void forward(double ra0,double de0,double ra,double de,double *x,double *y) +{ + int i; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset("STG",&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd("STG",ra,de,&cel,&phi,&theta,&prj,x,y)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + + return; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\t') + c=' '; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Read data +struct data read_data(char *filename) +{ + int i=0; + char line[LIM]; + FILE *file; + struct data d; + + // Open file + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"Failed to open %s\n",filename); + exit(1); + } + + // Count lines + while (fgetline(file,line,LIM)>0) + i++; + d.n=i; + + // Allocate + d.p=(struct point *) malloc(sizeof(struct point)*d.n); + + // Rewind file + rewind(file); + + // Read data + i=0; + while (fgetline(file,line,LIM)>0) + d.p[i++]=decode_iod_observation(line); + + // Close file + fclose(file); + + return d; +} + +// Chi-squared +double chisq(double a[]) +{ + int i,imode,nsel; + double chisq,rms; + xyz_t satpos,satvel; + double dx,dy,dz; + double r; + + // Construct struct + // a[0]: inclination + // a[1]: RA of ascending node + // a[2]: eccentricity + // a[3]: argument of periastron + // a[4]: mean anomaly + // a[5]: revs per day + + if (a[2]<0.0) + a[2]=0.0; + if (a[0]<0.0) { + a[0]*=-1; + a[1]+=180.0; + } else if (a[0]>180.0) { + a[0]=180.0; + } + + + // Set parameters + orb.eqinc=RAD(a[0]); + orb.ascn=RAD(modulo(a[1],360.0)); + orb.ecc=a[2]; + orb.argp=RAD(modulo(a[3],360.0)); + orb.mnan=RAD(modulo(a[4],360.0)); + orb.rev=a[5]; + orb.bstar=a[6]; + + // Initialize + imode=init_sgdp4(&orb); + if (imode==SGDP4_ERROR) + printf("Error\n"); + + // Loop over points + for (i=0,nsel=0,chisq=0.0,rms=0.0;i0) + rms=sqrt(rms/(float) nsel); + + d.chisq=chisq; + d.rms=rms; + d.nsel=nsel; + + return chisq; +} + +// Read tle +orbit_t read_tle(char *filename,int satno) +{ + int i; + FILE *file; + orbit_t orb; + + file=fopen(filename,"r"); + if (file==NULL) + fatal_error("Failed to open %s\n",filename); + + // Read TLE + read_twoline(file,satno,&orb); + fclose(file); + + return orb; +} + +// MJD to DOY +double mjd2doy(double mjd,int *yr) +{ + int year,month,k=2; + double day,doy; + + mjd2date(mjd,&year,&month,&day); + + if (year%4==0 && year%400!=0) + k=1; + + doy=floor(275.0*month/9.0)-k*floor((month+9.0)/12.0)+day-30; + + *yr=year; + + return doy; +} + +// Compute Date from Julian Day +void mjd2date(double mjd,int *year,int *month,double *day) +{ + double f,jd; + int z,alpha,a,b,c,d,e; + + jd=mjd+2400000.5; + jd+=0.5; + + z=floor(jd); + f=fmod(jd,1.); + + if (z<2299161) + a=z; + else { + alpha=floor((z-1867216.25)/36524.25); + a=z+1+alpha-floor(alpha/4.); + } + b=a+1524; + c=floor((b-122.1)/365.25); + d=floor(365.25*c); + e=floor((b-d)/30.6001); + + *day=b-d-floor(30.6001*e)+f; + if (e<14) + *month=e-1; + else + *month=e-13; + + if (*month>2) + *year=c-4716; + else + *year=c-4715; + + return; +} + +// Format TLE +void format_tle(orbit_t orb,char *line1,char *line2) +{ + int i,csum; + char sbstar[]=" 00000-0",bstar[13]; + + // Format Bstar term + if (fabs(orb.bstar)>1e-9) { + sprintf(bstar,"%11.4e",10*orb.bstar); + sbstar[0] = bstar[0]; sbstar[1] = bstar[1]; sbstar[2] = bstar[3]; sbstar[3] = bstar[4]; + sbstar[4] = bstar[5]; sbstar[5] = bstar[6]; sbstar[6] = bstar[8]; sbstar[7] = bstar[10]; sbstar[8] = '\0'; + } + // Print lines + sprintf(line1,"1 %05dU %2d%012.8f .00000000 00000-0 %8s 0 0",orb.satno,orb.ep_year-2000,orb.ep_day,sbstar); + sprintf(line2,"2 %05d %8.4f %8.4f %07.0f %8.4f %8.4f %11.8f 0",orb.satno,DEG(orb.eqinc),DEG(orb.ascn),1E7*orb.ecc,DEG(orb.argp),DEG(orb.mnan),orb.rev); + + // Compute checksums + for (i=0,csum=0;ix) ? x0 : x; + ymin=(y0y) ? y0 : y; + for (i=0;ixmin && d.p[i].raymin && d.p[i].de *mjdmax) *mjdmax=d.p[i].mjd; + n++; + } + } + c=0.1*(*mjdmax- *mjdmin); + *mjdmin-=c; + *mjdmax+=c; + + return; +} + +// Print TLE +void print_tle(orbit_t orb,char *filename) +{ + int i,n; + FILE *file; + double mjdmin,mjdmax; + int year,month; + double day; + char line1[70],line2[70]; + + // Count number of points + for (i=0,n=0;imjdmax) mjdmax=d.p[i].mjd; + n++; + } + } + + // Write TLE + file=fopen(filename,"w"); + format_tle(orb,line1,line2); + fprintf(file,"OBJ\n%s\n%s\n",line1,line2); + + mjd2date(mjdmin,&year,&month,&day); + fprintf(file,"# %4d%02d%05.2lf-",year,month,day); + mjd2date(mjdmax,&year,&month,&day); + fprintf(file,"%4d%02d%05.2lf, %d measurements, %.3lf kHz rms\n",year,month,day,n,d.rms); + fclose(file); + + return; +} + +// Fit +void fit(orbit_t orb,int *ia) +{ + int i,n; + double a[7],da[7]; + double db[7]={5.0,5.0,0.1,5.0,5.0,0.5,0.0001}; + + a[0]=orb.eqinc*R2D; + a[1]=orb.ascn*R2D; + a[2]=orb.ecc; + a[3]=orb.argp*R2D; + a[4]=orb.mnan*R2D; + a[5]=orb.rev; + a[6]=orb.bstar; + + for (i=0;i<7;i++) { + if (ia[i]==1) + da[i]=db[i]; + else + da[i]=0.0; + } + + // Construct struct + // a[0]: inclination + // a[1]: RA of ascending node + // a[2]: eccentricity + // a[3]: argument of periastron + // a[4]: mean anomaly + // a[5]: revs per day + // a[6]: bstar + + // Count highlighted points + for (i=0,n=0;i0) + versafit(n,7,a,da,chisq,0.0,1e-6,"n"); + + // Return parameters + orb.eqinc=RAD(a[0]); + orb.ascn=RAD(modulo(a[1],360.0)); + orb.ecc=a[2]; + orb.argp=RAD(modulo(a[3],360.0)); + orb.mnan=RAD(modulo(a[4],360.0)); + orb.rev=a[5]; + orb.bstar=a[6]; + + return; +} + +void usage() +{ + printf("satfit -d -c [tle catalog] -i [satno] -h\n\ndata file: Tabulated doppler curve\ntle catalog: Catalog with TLE's (optional)\nsatno: Satellite to load from TLE catalog (optional)\n\n"); + + return; +} diff --git a/satid.c b/satid.c new file mode 100644 index 0000000..102837a --- /dev/null +++ b/satid.c @@ -0,0 +1,666 @@ +#include +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" +#include "sgdp4h.h" + +#define LIM 80 +#define NMAX 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define XKMPAU 149597879.691 // AU in km +#define FLAT (1.0/298.257) +#define STDMAG 6.0 +#define MMAX 10 + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct map { + double lat,lng; + float alt; + char observer[32]; + int site_id; +} m; +struct image { + char filename[64]; + int naxis1,naxis2,nframes; + float *zavg,*zstd,*zmax,*znum; + double ra0,de0; + float x0,y0; + float a[3],b[3],xrms,yrms; + double mjd; + float *dt,exptime; + char nfd[32]; + int cospar; +}; +struct sat { + long Isat; + char state[10]; + float mag; + double jd; + double dx,dy,dz; + double x,y,z,vx,vy,vz; + double rsun,rearth; + double psun,pearth,p,phase; + double r,v,ra,de; + double azi,alt; + double rx,ry; +}; +struct image read_fits(char *filename); +struct sat apparent_position(double mjd); +double modulo(double,double); +void obspos_xyz(double,xyz_t *,xyz_t *); +void sunpos_xyz(double,xyz_t *); +double gmst(double); +double dgmst(double); +void forward(double ra0,double de0,double ra,double de,double *x,double *y); +void reverse(double ra0,double de0,double x,double y,double *ra,double *de); + +// Precess a celestial position +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de) +{ + double t0,t; + double zeta,z,theta; + double a,b,c; + + // Angles in radians + ra0*=D2R; + de0*=D2R; + + // Time in centuries + t0=(mjd0-51544.5)/36525.0; + t=(mjd-mjd0)/36525.0; + + // Precession angles + zeta=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + zeta+=(0.30188-0.000344*t0)*t*t+0.017998*t*t*t; + zeta*=D2R/3600.0; + z=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + z+=(1.09468+0.000066*t0)*t*t+0.018203*t*t*t; + z*=D2R/3600.0; + theta=(2004.3109-0.85330*t0-0.000217*t0*t0)*t; + theta+=-(0.42665+0.000217*t0)*t*t-0.041833*t*t*t; + theta*=D2R/3600.0; + + a=cos(de0)*sin(ra0+zeta); + b=cos(theta)*cos(de0)*cos(ra0+zeta)-sin(theta)*sin(de0); + c=sin(theta)*cos(de0)*cos(ra0+zeta)+cos(theta)*sin(de0); + + *ra=(atan2(a,b)+z)*R2D; + *de=asin(c)*R2D; + + if (*ra<360.0) + *ra+=360.0; + if (*ra>360.0) + *ra-=360.0; + + return; +} + +void plot_satellites(char *tlefile,struct image img,long satno,double mjd0,float dt,int color) +{ + int i; + orbit_t orb; + struct sat s; + int imode,flag,textflag; + FILE *fp=NULL,*file;; + xyz_t satpos,obspos,satvel,sunpos; + double mjd,jd,dx,dy,dz; + double rx,ry,ra,de,azi,alt,r,t,d; + float x,y,x0,y0; + char norad[7],satname[30]; + float isch; + float rsun,rearth,psun,pearth,p; + char filename[128]; + + cpgqch(&isch); + + // Image determinant + d=img.a[1]*img.b[2]-img.a[2]*img.b[1]; + + // Open TLE file + fp=fopen(tlefile,"rb"); + if (fp==NULL) + fatal_error("File open failed for reading %s\n",tlefile); + + cpgsci(color); + + // Open file + sprintf(filename,"%s.id",img.filename); + file=fopen(filename,"a"); + + // Read TLEs + while (read_twoline(fp,satno,&orb)==0) { + Isat=orb.satno; + imode=init_sgdp4(&orb); + + sprintf(norad," %05ld",Isat); + + if (imode==SGDP4_ERROR) + continue; + + for (flag=0,textflag=0,i=0;i-s.psun && s.p-s.pearths.psun) { + cpgsls(1); + } + + // Print name if in viewport + if (x>0.0 && x0.0 && y-s.psun && s.p-s.pearths.psun) + strcpy(s.state,"sunlit"); + + // Position differences + dx=satpos.x-obspos.x; + dy=satpos.y-obspos.y; + dz=satpos.z-obspos.z; + dvx=satvel.x-obsvel.x; + dvy=satvel.y-obsvel.y; + dvz=satvel.z-obsvel.z; + + // Celestial position + s.r=sqrt(dx*dx+dy*dy+dz*dz); + s.v=(dvx*dx+dvy*dy+dvz*dz)/s.r; + ra=modulo(atan2(dy,dx)*R2D,360.0); + de=asin(dz/s.r)*R2D; + + // Precess + precess(mjd,ra,de,mjd0,&s.ra,&s.de); + + // Phase + s.phase=acos(((obspos.x-satpos.x)*(sunpos.x-satpos.x)+(obspos.y-satpos.y)*(sunpos.y-satpos.y)+(obspos.z-satpos.z)*(sunpos.z-satpos.z))/(rsun*s.r))*R2D; + + // Magnitude + if (strcmp(s.state,"sunlit")==0) + s.mag=STDMAG-15.0+5*log10(s.r)-2.5*log10(sin(s.phase*D2R)+(M_PI-s.phase*D2R)*cos(s.phase*D2R)); + else + s.mag=15; + + /* + // Convert and project + if (strcmp(m.orientation,"horizontal")==0) { + equatorial2horizontal(mjd,s.ra,s.de,&s.azi,&s.alt); + forward(s.azi,s.alt,&s.rx,&s.ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(s.ra,s.de,&s.rx,&s.ry); + } + */ + return s; +} + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// Observer position +void obspos_xyz(double mjd,xyz_t *pos,xyz_t *vel) +{ + double ff,gc,gs,theta,s,dtheta; + + s=sin(m.lat*D2R); + ff=sqrt(1.0-FLAT*(2.0-FLAT)*s*s); + gc=1.0/ff+m.alt/XKMPER; + gs=(1.0-FLAT)*(1.0-FLAT)/ff+m.alt/XKMPER; + + theta=gmst(mjd)+m.lng; + dtheta=dgmst(mjd)*D2R/86400; + + pos->x=gc*cos(m.lat*D2R)*cos(theta*D2R)*XKMPER; + pos->y=gc*cos(m.lat*D2R)*sin(theta*D2R)*XKMPER; + pos->z=gs*sin(m.lat*D2R)*XKMPER; + vel->x=-gc*cos(m.lat*D2R)*sin(theta*D2R)*XKMPER*dtheta; + vel->y=gc*cos(m.lat*D2R)*cos(theta*D2R)*XKMPER*dtheta; + vel->z=0.0; + + return; +} + +// Solar position +void sunpos_xyz(double mjd,xyz_t *pos) +{ + double jd,t,l0,m,e,c,r; + double n,s,ecl,ra,de; + + jd=mjd+2400000.5; + t=(jd-2451545.0)/36525.0; + l0=modulo(280.46646+t*(36000.76983+t*0.0003032),360.0)*D2R; + m=modulo(357.52911+t*(35999.05029-t*0.0001537),360.0)*D2R; + e=0.016708634+t*(-0.000042037-t*0.0000001267); + c=(1.914602+t*(-0.004817-t*0.000014))*sin(m)*D2R; + c+=(0.019993-0.000101*t)*sin(2.0*m)*D2R; + c+=0.000289*sin(3.0*m)*D2R; + + r=1.000001018*(1.0-e*e)/(1.0+e*cos(m+c)); + n=modulo(125.04-1934.136*t,360.0)*D2R; + s=l0+c+(-0.00569-0.00478*sin(n))*D2R; + ecl=(23.43929111+(-46.8150*t-0.00059*t*t+0.001813*t*t*t)/3600.0+0.00256*cos(n))*D2R; + + ra=atan2(cos(ecl)*sin(s),cos(s)); + de=asin(sin(ecl)*sin(s)); + + pos->x=r*cos(de)*cos(ra)*XKMPAU; + pos->y=r*cos(de)*sin(ra)*XKMPAU; + pos->z=r*sin(de)*XKMPAU; + + return; +} + +// Greenwich Mean Sidereal Time +double dgmst(double mjd) +{ + double t,dgmst; + + t=(mjd-51544.5)/36525.0; + + dgmst=360.98564736629+t*(0.000387933-t/38710000); + + return dgmst; +} + +// Get a x and y from a RA and Decl +void forward(double ra0,double de0,double ra,double de,double *x,double *y) +{ + int i; + char pcode[4]="TAN"; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(pcode,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd(pcode,ra,de,&cel,&phi,&theta,&prj,x,y)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + *x*=3600.; + *y*=3600.; + + return; +} + +// Get a RA and Decl from x and y +void reverse(double ra0,double de0,double x,double y,double *ra,double *de) +{ + int i; + char pcode[4]="TAN"; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + x/=3600.; + y/=3600.; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(pcode,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celrev(pcode,x,y,&prj,&phi,&theta,&cel,ra,de)) { + printf("Error in Projection (celrev)\n"); + return; + } + } + return; +} diff --git a/satmap.c b/satmap.c new file mode 100644 index 0000000..67d4f7c --- /dev/null +++ b/satmap.c @@ -0,0 +1,869 @@ +#include +#include +#include +#include +#include +#include +#include "cpgplot.h" +#include "sgdp4h.h" + +#define LIM 80 +#define NMAX 1024 +#define MMAX 28368 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define FLAT (1.0/298.257) +#define XKMPAU 149597879.691 // AU in km + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct map { + long satno; + double lat,lng; + double mjd; + float alt,timezone; + int length; + char nfd[LIM],tlefile[LIM],observer[32]; + char datadir[LIM],tledir[LIM]; + int site_id; + float l0,b0; +} m; +struct globe { + int n; + float l[MMAX],b[MMAX]; +} glb; +struct sat { + long Isat; + double jd; + double dx,dy,dz; + double x,y,z,vx,vy,vz; + double rsun,rearth; + double psun,pearth,p; + double r,ra,de; + double azi,alt; + double rx,ry; +}; +void read_globe(void); +void plot_globe(void); +void initialize_setup(void); +double nfd2mjd(char *date); +double date2mjd(int year,int month,double day); +void mjd2date(double mjd,char *date,int length); +void usage(); +void nfd_now(char *s); +double gmst(double); +double dgmst(double); +double modulo(double,double); +void sunpos_xyz(double,xyz_t *,double *,double *); +void rotate(int axis,float angle,float *x,float *y,float *z); +void get_site(int site_id); + +void plot_terminator(void) +{ + int i,j,j0,k,flag; + xyz_t sunpos; + double sra,sde,r,h; + float l0,b0,l[NMAX+4],b[NMAX+4]; + float x,y,z; + int isci; + float theta,ang[]={0.0,-6.0,-12.0,-18.0}; + + // Solar position + sunpos_xyz(m.mjd,&sunpos,&sra,&sde); + + // GMST + h=gmst(m.mjd); + + // Solar subpoint + l0=modulo(sra-h,360.0); + b0=sde; + if (l0>180.0) + l0-=360.0; + + // Loop over terminator boundaries + for (k=0;k<4;k++) { + for (i=0,j=0,flag=0;i180.0) + l[j]-=360.0; + if (l[j]<-180.0) + l[j]+=360.0; + + // Passing limit left to right + if (l[j]*l[j-1]<0.0 && fabs(l[j])>45.0 && flag==0 && k==0) { + l[j+4]=l[j]; + b[j+4]=b[j]; + b[j]=b[j-1]; + b[j+3]=b[j-1]; + if (l[j-1]0 && l[i-1]*l[i]<0.0 && fabs(l[i-1]-l[i])>10.0) + flag=0; + + if (flag==0) { + cpgmove(l[i],b[i]); + flag=1; + } else { + cpgdraw(l[i],b[i]); + } + } + cpgsci(1); + } + } + + // Save sub solar position + m.l0=l0; + m.b0=b0; + + return; +} + +void init_plot(char *psfile,float width,float aspect) +{ + + cpgopen(psfile); + cpgslw(2); + cpgpap(width,aspect); + + return; +} + +// Plot observing sites +void plot_sites(void) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64],text[8],filename[LIM]; + float isch; + + cpgqch(&isch); + + sprintf(filename,"%s/data/sites.txt",m.datadir); + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + sprintf(text," %04d",id); + cpgsci(2); + cpgsch(0.5); + cpgpt1(lng,lat,4); + cpgtext(lng,lat,text); + cpgsci(1); + } + fclose(file); + cpgsch(isch); + + return; +} +// Computes apparent position +struct sat apparent_position(double mjd) +{ + struct sat s; + double jd,rsun,rearth; + double dx,dy,dz; + xyz_t satpos,obspos,satvel,sunpos; + double sra,sde; + + // Sat ID + s.Isat=Isat; + + // Get Julian Date + jd=mjd+2400000.5; + + // Get positions + satpos_xyz(jd,&satpos,&satvel); + sunpos_xyz(mjd,&sunpos,&sra,&sde); + + // Sat positions + s.x=satpos.x; + s.y=satpos.y; + s.z=satpos.z; + s.vx=satvel.x; + s.vy=satvel.y; + s.vz=satvel.y; + + // Sun position from satellite + dx=-satpos.x+sunpos.x; + dy=-satpos.y+sunpos.y; + dz=-satpos.z+sunpos.z; + + // Distances + rsun=sqrt(dx*dx+dy*dy+dz*dz); + rearth=sqrt(satpos.x*satpos.x+satpos.y*satpos.y+satpos.z*satpos.z); + // Angles + s.psun=asin(696.0e3/rsun)*R2D; + s.pearth=asin(6378.135/rearth)*R2D; + s.p=acos((-dx*satpos.x-dy*satpos.y-dz*satpos.z)/(rsun*rearth))*R2D; + // s.p=acos(((sunpos.x+satpos.x)*satpos.x+(sunpos.y+satpos.y)*satpos.y+(sunpos.z+satpos.z)*satpos.z)/(rsun*rearth))*R2D; + + s.p-=s.pearth; + + // Celestial position + s.r=sqrt(satpos.x*satpos.x+satpos.y*satpos.y+satpos.z*satpos.z); + s.ra=atan2(satpos.y,satpos.x)*R2D; + s.de=asin(satpos.z/s.r)*R2D; + + return s; +} + +// plot satellite track +void track_plot_track(char *tlefile,long satno,double mjd0) +{ + int i=0,nstep=500; + orbit_t orb; + xyz_t pos,vel; + double jd,dt,h,l,b,l0,mjd; + FILE *fp=NULL; + float x,y,z,r,v; + long imode; + int isci; + float isch; + char norad[7]; + struct sat s; + + cpgqci(&isci); + cpgqch(&isch); + cpgsci(7); + + fp = fopen(tlefile, "rb"); + if(fp == NULL) { + fatal_error("File open failed for reading \"%s\"", tlefile); + } + + while(read_twoline(fp, satno, &orb) == 0) { + // print_orb(&orb); + + Isat = orb.satno; + imode = init_sgdp4(&orb); + + if(imode == SGDP4_ERROR) continue; + + jd=mjd0+2400000.5; + for (i=0;;i++) { + // if(satpos_xyz(jd, &pos, &vel) == SGDP4_ERROR) break; + mjd=jd-2400000.5; + s=apparent_position(mjd); + + h=gmst(mjd); + + x=s.x; + y=s.y; + z=s.z; + + // Celestial position + r=sqrt(x*x+y*y+z*z); + l=atan2(y,x)*R2D; + b=asin(z/r)*R2D; + l-=h; + l=modulo(l,360.0); + if (l>180.0) + l-=360.0; + if (l<-180.0) + l+=360.0; + + // Visibility + if (s.p<-s.psun) + cpgsci(14); + else if (s.p>-s.psun && s.ps.psun) + cpgsci(7); + + // Plot + if (i==0) { + sprintf(norad," %5ld",Isat); + cpgsch(0.6); + cpgtext(l,b,norad); + cpgsch(isch); + cpgpt1(l,b,17); + l0=l; + } + if (i==0 || fabs(l-l0)>10.0) + cpgmove(l,b); + else + cpgdraw(l,b); + l0=l; + + // Do timestep + r=sqrt(s.x*s.x+s.y*s.y+s.z*s.z); + v=sqrt(s.vx*s.vx+s.vy*s.vy+s.vz*s.vz); + dt=2.0*M_PI*r/(0.75*v*nstep); + jd+=dt/86400.0; + + if (i==nstep) + break; + } + } + cpgsci(isci); + cpgsch(isch); + + + return; +} + +void plot_map(void) +{ + int redraw=1; + char text[256]; + float x,y; + char c; + + for (;;) { + if (redraw>0) { + // Get present mjd + if (m.mjd<0.0) { + nfd_now(m.nfd); + m.mjd=nfd2mjd(m.nfd); + } + + cpgscr(0,0.0,0.0,0.0); + cpgeras(); + + // Create window + cpgsvp(0.01,0.99,0.01,0.99); + cpgwnad(-180.0,180.0,-90.0,90.0); + + // Set background + cpgscr(0,0.0,0.0,0.5); + cpgsci(0); + cpgrect(-180.0,180.0,-90.0,90.0); + cpgsci(1); + cpgscr(0,0.0,0.0,0.0); + cpgbox("BC",0.,0,"BC",0.,0); + + // Top left string + cpgsch(0.8); + mjd2date(m.mjd,m.nfd,0); + sprintf(text,"%s UTC",m.nfd); + cpgmtxt("T",0.6,0.0,0.0,text); + + // Bottom string + sprintf(text,"l: %d s",m.length); + cpgmtxt("B",1.0,0.0,0.0,text); + cpgsch(1.0); + + // Plot terminator + plot_terminator(); + cpgsci(14); + cpgbox("ABCG",30.,3,"ABCG",30.,3); + cpgsci(1); + + // Plot globe + plot_globe(); + cpgsci(1); + cpgbox("BCTS",30.,3,"BCTS",30.,3); + + // Plot sites + plot_sites(); + + // Plot satellites + track_plot_track(m.tlefile,m.satno,m.mjd); + + // Plot sub solar position + cpgsci(7); + cpgpt1(m.l0,m.b0,17); + cpgsci(1); + } + + // Reset redraw + redraw=0; + + // Get cursor + cpgcurs(&x,&y,&c); + + // Redraw + if (c=='r') { + m.mjd=-1.0; + m.length=60; + redraw=1; + } + // Increase/decrease time + if (c=='.') { + m.mjd+=m.length/86400.0; + redraw=1; + } + if (c==',') { + m.mjd-=m.length/86400.0; + redraw=1; + } + + // Increase/decrease step + if (c=='>') { + m.length*=2.0; + redraw=2; + } + if (c=='<') { + m.length/=2.0; + redraw=2; + } + + // Exit + if (c=='q' || c=='Q') { + cpgend(); + exit(0); + } + } + + return; +} + + + +int main(int argc,char *argv[]) +{ + int arg=0; + + // Initialize setup + initialize_setup(); + + // Decode options + while ((arg=getopt(argc,argv,"t:c:i:s:l:h"))!=-1) { + switch (arg) { + + case 't': + strcpy(m.nfd,optarg); + m.mjd=nfd2mjd(m.nfd); + break; + + case 'c': + strcpy(m.tlefile,optarg); + break; + + case 's': + get_site(atoi(optarg)); + break; + + case 'i': + m.satno=atoi(optarg); + break; + + case 'l': + m.length=atoi(optarg); + break; + + case 'h': + usage(); + return 0; + break; + + default: + usage(); + return 0; + } + } + + // Read data + read_globe(); + + // Initialize plot + init_plot("/xs",8,0.75); + + plot_map(); + + cpgend(); + + + + return 0; +} + +void read_globe(void) +{ + int i,status; + FILE *file; + char filename[LIM]; + + sprintf(filename,"%s/data/globe.dat",m.datadir); + file=fopen(filename,"r"); + + for (i=0;itm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); + + return; +} + +// nfd2mjd +double nfd2mjd(char *date) +{ + int year,month,day,hour,min,sec; + double mjd,dday; + + sscanf(date,"%04d-%02d-%02dT%02d:%02d:%02d",&year,&month,&day,&hour,&min,&sec); + dday=day+hour/24.0+min/1440.0+sec/86400.0; + + mjd=date2mjd(year,month,dday); + + return mjd; +} + +void usage() +{ + return; +} + +// Compute Date from Julian Day +void mjd2date(double mjd,char *date,int length) +{ + double f,jd,dday; + int z,alpha,a,b,c,d,e; + int year,month,day,hour,min; + float sec,x; + + jd=mjd+2400000.5; + jd+=0.5; + + z=floor(jd); + f=fmod(jd,1.); + + if (z<2299161) + a=z; + else { + alpha=floor((z-1867216.25)/36524.25); + a=z+1+alpha-floor(alpha/4.); + } + b=a+1524; + c=floor((b-122.1)/365.25); + d=floor(365.25*c); + e=floor((b-d)/30.6001); + + dday=b-d-floor(30.6001*e)+f; + if (e<14) + month=e-1; + else + month=e-13; + + if (month>2) + year=c-4716; + else + year=c-4715; + + day=(int) floor(dday); + x=24.0*(dday-day); + x=3600.*fabs(x); + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + hour=x; + sec=floor(1000.0*sec)/1000.0; + + if (length==3) + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%06.3f",year,month,day,hour,min,sec); + else if (length==0) + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%02.0f",year,month,day,hour,min,sec); + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// Solar position +void sunpos_xyz(double mjd,xyz_t *pos,double *ra,double *de) +{ + double jd,t,l0,m,e,c,r; + double n,s,ecl; + + jd=mjd+2400000.5; + t=(jd-2451545.0)/36525.0; + l0=modulo(280.46646+t*(36000.76983+t*0.0003032),360.0)*D2R; + m=modulo(357.52911+t*(35999.05029-t*0.0001537),360.0)*D2R; + e=0.016708634+t*(-0.000042037-t*0.0000001267); + c=(1.914602+t*(-0.004817-t*0.000014))*sin(m)*D2R; + c+=(0.019993-0.000101*t)*sin(2.0*m)*D2R; + c+=0.000289*sin(3.0*m)*D2R; + + r=1.000001018*(1.0-e*e)/(1.0+e*cos(m+c)); + n=modulo(125.04-1934.136*t,360.0)*D2R; + s=l0+c+(-0.00569-0.00478*sin(n))*D2R; + ecl=(23.43929111+(-46.8150*t-0.00059*t*t+0.001813*t*t*t)/3600.0+0.00256*cos(n))*D2R; + + *ra=atan2(cos(ecl)*sin(s),cos(s))*R2D; + *de=asin(sin(ecl)*sin(s))*R2D; + + pos->x=r*cos(*de*D2R)*cos(*ra*D2R)*XKMPAU; + pos->y=r*cos(*de*D2R)*sin(*ra*D2R)*XKMPAU; + pos->z=r*sin(*de*D2R)*XKMPAU; + + return; +} + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Greenwich Mean Sidereal Time +double dgmst(double mjd) +{ + double t,dgmst; + + t=(mjd-51544.5)/36525.0; + + dgmst=360.98564736629+t*(0.000387933-t/38710000); + + return dgmst; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// rotate vector +void rotate(int axis,float angle,float *x,float *y,float *z) +{ + float xx,yy,zz; + float ca,sa; + + ca=cos(angle*D2R); + sa=sin(angle*D2R); + + if (axis==0) { + xx= *x; + yy= *y*ca- *z*sa; + zz= *z*ca+ *y*sa; + } + if (axis==1) { + xx= *x*ca- *z*sa; + yy= *y; + zz= *z*ca+ *x*sa; + } + if (axis==2) { + xx= *x*ca- *y*sa; + yy= *y*ca+ *x*sa; + zz= *z; + } + + *x=xx; + *y=yy; + *z=zz; + + return; +} + +// Get observing site +void get_site(int site_id) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64]; + char filename[LIM]; + + sprintf(filename,"%s/data/sites.txt",m.datadir); + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + // Change to km + alt/=1000.0; + + if (id==site_id) { + m.lat=lat; + m.lng=lng; + m.alt=alt; + m.site_id=id; + strcpy(m.observer,observer); + } + + } + fclose(file); + + return; +} diff --git a/satorbit.c b/satorbit.c new file mode 100644 index 0000000..c29085d --- /dev/null +++ b/satorbit.c @@ -0,0 +1,1066 @@ +#include +#include +#include +#include +#include +#include +#include "cpgplot.h" +#include "sgdp4h.h" + +#define LIM 80 +#define NMAX 1024 +#define MMAX 28368 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define FLAT (1.0/298.257) +#define XKMPAU 149597879.691 // AU in km + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct map { + long satno; + double l0,b0,h0; + double lat,lng; + double mjd; + float alt,timezone; + int length; + char orientation[LIM]; + char nfd[LIM],tlefile[LIM],observer[32]; + char datadir[LIM],tledir[LIM]; + int site_id; + float w; +} m; +struct globe { + int n; + float l[MMAX],b[MMAX],x[MMAX],y[MMAX],z[MMAX]; +} glb; +struct sat { + long Isat; + double jd; + double dx,dy,dz; + double x,y,z,vx,vy,vz; + double rsun,rearth; + double psun,pearth,p; + double r,ra,de; + double azi,alt; + double rx,ry; + double lng,lat; +}; +void read_globe(void); +void plot_globe(void); +double nfd2mjd(char *date); +double date2mjd(int year,int month,double day); +void mjd2date(double mjd,char *date,int length); +void usage(); +void nfd_now(char *s); +void rotate(int axis,float angle,float *x,float *y,float *z); +void sunpos_xyz(double mjd,xyz_t *pos,double *ra,double *de); +double gmst(double); +double dgmst(double); +double modulo(double,double); +void get_site(int site_id); + +// Initialize setup +void initialize_setup(void) +{ + char *env; + + // Default parameters + m.satno=0; + m.timezone=0.0; + m.length=60; + strcpy(m.orientation,"terrestial"); + nfd_now(m.nfd); + m.mjd=nfd2mjd(m.nfd); + m.w=1.2; + m.h0=gmst(m.mjd); + + // Default settings + strcpy(m.observer,"Unknown"); + m.site_id=0; + + // Get environment variables + env=getenv("ST_DATADIR"); + if (env!=NULL) { + strcpy(m.datadir,env); + } else { + printf("ST_DATADIR environment variable not found.\n"); + } + env=getenv("ST_COSPAR"); + if (env!=NULL) { + get_site(atoi(env)); + } else { + printf("ST_COSPAR environment variable not found.\n"); + } + env=getenv("ST_TLEDIR"); + if (env!=NULL) { + strcpy(m.tledir,env); + } else { + printf("ST_TLEDIR environment variable not found.\n"); + } + sprintf(m.tlefile,"%s/classfd.tle",m.tledir); + + return; +} + +void plot_footprint(struct sat s) +{ + int i,j,flag; + float range,alpha,dist,zz,theta,rr; + float x,y,z,x0,y0,z0,r0,r; + + // Foot print size + range=sqrt(s.x*s.x+s.y*s.y+s.z*s.z); + dist=sqrt(range*range-XKMPER*XKMPER); + alpha=acos(XKMPER/range); + zz=range-dist*sin(alpha); + rr=sqrt(XKMPER*XKMPER-zz*zz); + + // Sub satellite point + z=cos(s.lng*D2R)*cos(s.lat*D2R)*XKMPER; + x=sin(s.lng*D2R)*cos(s.lat*D2R)*XKMPER; + y=sin(s.lat*D2R)*XKMPER; + rotate(1,m.l0,&x,&y,&z); + rotate(0,m.b0,&x,&y,&z); + r=sqrt(x*x+y*y); + z0=cos(s.lng*D2R)*cos(s.lat*D2R)*range; + x0=sin(s.lng*D2R)*cos(s.lat*D2R)*range; + y0=sin(s.lat*D2R)*range; + rotate(1,m.l0,&x0,&y0,&z0); + rotate(0,m.b0,&x0,&y0,&z0); + r0=sqrt(x0*x0+y0*y0); + + if (rXKMPER || (r00.0)) { + cpgmove(x0,y0); + cpgdraw(x,y); + } + if (z>0.0) + cpgpt1(x,y,4); + + for (i=0,j=0,flag=0;i0.0) + flag=1; + else + flag=0; + } + + return; +} + +// Computes apparent position +struct sat apparent_position(double mjd) +{ + struct sat s; + double jd,rsun,rearth; + double dx,dy,dz; + xyz_t satpos,obspos,satvel,sunpos; + double sra,sde; + + // Sat ID + s.Isat=Isat; + + // Get Julian Date + jd=mjd+2400000.5; + + // Get positions + satpos_xyz(jd,&satpos,&satvel); + sunpos_xyz(mjd,&sunpos,&sra,&sde); + + // Sat positions + s.x=satpos.x; + s.y=satpos.y; + s.z=satpos.z; + s.vx=satvel.x; + s.vy=satvel.y; + s.vz=satvel.y; + + // Sun position from satellite + dx=-satpos.x+sunpos.x; + dy=-satpos.y+sunpos.y; + dz=-satpos.z+sunpos.z; + + // Distances + rsun=sqrt(dx*dx+dy*dy+dz*dz); + rearth=sqrt(satpos.x*satpos.x+satpos.y*satpos.y+satpos.z*satpos.z); + // Angles + s.psun=asin(696.0e3/rsun)*R2D; + s.pearth=asin(6378.135/rearth)*R2D; + s.p=acos((-dx*satpos.x-dy*satpos.y-dz*satpos.z)/(rsun*rearth))*R2D; + // s.p=acos(((sunpos.x+satpos.x)*satpos.x+(sunpos.y+satpos.y)*satpos.y+(sunpos.z+satpos.z)*satpos.z)/(rsun*rearth))*R2D; + + s.p-=s.pearth; + + // Celestial position + s.r=sqrt(satpos.x*satpos.x+satpos.y*satpos.y+satpos.z*satpos.z); + s.ra=atan2(satpos.y,satpos.x)*R2D; + s.de=asin(satpos.z/s.r)*R2D; + + // Latitude and longitude + s.lng=s.ra-gmst(m.mjd);; + s.lat=s.de; + + return s; +} + +// plot satellite track +void plot_track(void) +{ + int i=0,nstep=500; + orbit_t orb; + xyz_t pos,vel; + double jd,dt,h,mjd; + FILE *fp=NULL; + float x,y,z,r,v; + long imode; + int isci; + float isch; + char norad[7]; + struct sat s; + + cpgqci(&isci); + cpgqch(&isch); + cpgsci(7); + + fp=fopen(m.tlefile,"rb"); + if (fp==NULL) { + fatal_error("File open failed for reading \"%s\"",m.tlefile); + } + + while (read_twoline(fp,m.satno,&orb) == 0) { + // print_orb(&orb); + + Isat=orb.satno; + imode=init_sgdp4(&orb); + + if(imode == SGDP4_ERROR) continue; + + jd=m.mjd+2400000.5; + h=gmst(m.mjd); + + for (i=0,dt=0.0;;i++) { + //if(satpos_xyz(jd, &pos, &vel) == SGDP4_ERROR) break; + mjd=jd-2400000.5; + s=apparent_position(mjd); + + x=s.x; + y=s.y; + z=s.z; + + rotate(0,-90.0,&x,&y,&z); + rotate(1,90.0,&x,&y,&z); + rotate(1,m.l0+h,&x,&y,&z); + rotate(0,m.b0,&x,&y,&z); + + // Visibility + if (s.p<-s.psun) + cpgsci(14); + else if (s.p>-s.psun && s.ps.psun) + cpgsci(7); + + // Plot + if (i==0) { + plot_footprint(s); + if (!(sqrt(x*x+y*y)0.0) { + if (flag==0) { + cpgmove(x,y); + flag=1; + } else { + cpgdraw(x,y); + } + } else { + flag=0; + } + } + + return; +} + +// plot grid +void plot_grid(void) +{ + int i,j,flag; + float l,b; + float x,y,z; + + for (l=0.0;l<=360.0;l+=30.0) { + for (b=-90.0,flag=0;b<=90.0;b+=1.0) { + z=cos(l*D2R)*cos(b*D2R)*XKMPER; + x=sin(l*D2R)*cos(b*D2R)*XKMPER; + y=sin(b*D2R)*XKMPER; + + rotate(1,m.l0,&x,&y,&z); + rotate(0,m.b0,&x,&y,&z); + + if (flag==0) + cpgmove(x,y); + else + cpgdraw(x,y); + + if (z>0.0) + flag=1; + else + flag=0; + } + } + + for (b=-90.0;b<=90.0;b+=30.0) { + for (l=0.0,flag=0;l<=360.0;l+=1.0) { + z=cos(l*D2R)*cos(b*D2R)*XKMPER; + x=sin(l*D2R)*cos(b*D2R)*XKMPER; + y=sin(b*D2R)*XKMPER; + + rotate(1,m.l0,&x,&y,&z); + rotate(0,m.b0,&x,&y,&z); + + if (flag==0) + cpgmove(x,y); + else + cpgdraw(x,y); + + if (z>0.0) + flag=1; + else + flag=0; + } + } + + return; +} + +// Plot terminator +void plot_terminator(void) +{ + int i,j,k,flag,j1,j2; + double jd; + xyz_t s; + float r,h; + float l,b,l0,b0; + float x0,y0,z0; + float x,y,z,t0,t1,t2,t; + float xx[NMAX],yy[NMAX],zz[NMAX]; + float xt[NMAX],yt[NMAX],zt[NMAX]; + int isci; + double sra,sde; + float theta; + float ang[]={0.0,-6.0,-12.0,-18.0}; + + cpgqci(&isci); + + // Get positions + sunpos_xyz(m.mjd,&s,&sra,&sde); + + // GMST + h=gmst(m.mjd); + + // Solar subpoint + l0=modulo(sra-h,360.0); + b0=sde; + if (l0>180.0) + l0-=360.0; + + // Convert + z0=cos(l0*D2R)*cos(b0*D2R)*XKMPER; + x0=sin(l0*D2R)*cos(b0*D2R)*XKMPER; + y0=sin(b0*D2R)*XKMPER; + + rotate(1,m.l0,&x0,&y0,&z0); + rotate(0,m.b0,&x0,&y0,&z0); + + t0=atan2(y0,x0)*R2D; + + // Loop over terminator boundaries + for (i=0,j=0,flag=0;i0 && zz[i]*zz[i-1]<0.0) { + if (zz[i]>0.0 && zz[i-1]<0.0) { + t1=atan2(yy[i],xx[i])*R2D; + j1=i; + } else { + t2=atan2(yy[i],xx[i])*R2D; + j2=i; + } + } + } + // angles + t0=modulo(t0,360); + t1=modulo(t1,360); + t2=modulo(t2,360); + if (t1512) + j2++; + if (abs(j2-j1)<512) + j2--; + if (j1>j2) { + for (i=0,j=0;i0.0) { + cpgsci(7); + cpgpt1(x0,y0,17); + } + + // Loop over terminator boundaries + for (k=0;k<4;k++) { + if (k==0) + cpgsci(2); + else + cpgsci(4); + for (i=0,j=0,flag=0;i0.0) + flag=1; + else + flag=0; + } + } + cpgsci(isci); + return; +} + +void plot_map(void) +{ + int redraw=1,status; + char text[256]; + float x,y,z; + char c; + + for (;;) { + if (redraw>0) { + // Get present mjd + if (m.mjd<0.0) { + nfd_now(m.nfd); + m.mjd=nfd2mjd(m.nfd); + m.h0=gmst(m.mjd); + } + + // Update position + if (strcmp(m.orientation,"terrestial")==0) { + m.l0=m.lng; + m.b0=m.lat; + } else if (strcmp(m.orientation,"sidereal")==0) { + m.l0=m.lng-gmst(m.mjd)+m.h0; + m.b0=m.lat; + } + + cpgscr(0,0.0,0.0,0.0); + cpgeras(); + + // Create window + cpgsvp(0.05,0.95,0.05,0.95); + cpgwnad(-m.w*XKMPER,m.w*XKMPER,-m.w*XKMPER,m.w*XKMPER); + + // Set background + cpgscr(0,0.0,0.0,0.5); + cpgsci(0); + cpgwnad(-m.w*XKMPER,m.w*XKMPER,-m.w*XKMPER,m.w*XKMPER); + cpgsci(1); + cpgscr(0,0.0,0.0,0.0); + + // Top left string + cpgsch(0.8); + mjd2date(m.mjd,m.nfd,0); + sprintf(text,"%s UTC",m.nfd); + cpgmtxt("T",0.6,0.0,0.0,text); + + // Bottom string + sprintf(text,"l: %d s",m.length); + cpgmtxt("B",1.0,0.0,0.0,text); + cpgsch(1.0); + + // Plot terminator + plot_terminator(); + + // Plot Grid + cpgsls(2); + cpgsci(14); + plot_grid(); + cpgsls(1); + cpgsci(1); + + // Plot globe + plot_globe(); + cpgsfs(2); + cpgcirc(0.0,0.0,XKMPER); + cpgpt1(0.0,0.0,2); + cpgsci(1); + cpgbox("BC",0.,0,"BC",0.,0); + + // Plot track + plot_track(); + } + + // Reset redraw + redraw=0; + + // Get cursor + cpgcurs(&x,&y,&c); + + // Redraw + if (c=='r') { + m.mjd=-1.0; + m.length=60; + redraw=1; + } + + // Orientation + if (c=='o') { + if (strcmp(m.orientation,"terrestial")==0) + strcpy(m.orientation,"sidereal"); + else if (strcmp(m.orientation,"sidereal")==0) + strcpy(m.orientation,"terrestial"); + redraw=1; + } + + // Recenter + if (sqrt(x*x+y*y)') { + m.length*=2.0; + redraw=1; + } + if (c=='<') { + m.length/=2.0; + redraw=1; + } + if (c==',') { + m.mjd-=m.length/86400.0; + redraw=1; + } + if (c=='.') { + m.mjd+=m.length/86400.0; + redraw=1; + } + + // Integration lenght + if (c=='l') { + printf("Enter integration length (s): "); + status=scanf("%d",&m.length); + redraw=1; + } + + // Exit + if (c=='q' || c=='Q') { + cpgend(); + exit(0); + } + } + + return; +} + +int main(int argc,char *argv[]) +{ + int arg=0; + + // Initialize setup + initialize_setup(); + + // Decode options + while ((arg=getopt(argc,argv,"t:c:i:s:l:h"))!=-1) { + switch (arg) { + + case 't': + strcpy(m.nfd,optarg); + m.mjd=nfd2mjd(m.nfd); + break; + + case 'c': + strcpy(m.tlefile,optarg); + break; + + case 's': + get_site(atoi(optarg)); + break; + + case 'i': + m.satno=atoi(optarg); + break; + + case 'l': + m.length=atoi(optarg); + break; + + case 'h': + usage(); + return 0; + break; + + default: + usage(); + return 0; + } + } + + read_globe(); + + cpgopen("/xs"); + + plot_map(); + + cpgend(); + + + return 0; +} + +// Present nfd +void nfd_now(char *s) +{ + time_t rawtime; + struct tm *ptm; + + // Get UTC time + time(&rawtime); + ptm=gmtime(&rawtime); + + sprintf(s,"%04d-%02d-%02dT%02d:%02d:%02d",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); + + return; +} + +// nfd2mjd +double nfd2mjd(char *date) +{ + int year,month,day,hour,min,sec; + double mjd,dday; + + sscanf(date,"%04d-%02d-%02dT%02d:%02d:%02d",&year,&month,&day,&hour,&min,&sec); + dday=day+hour/24.0+min/1440.0+sec/86400.0; + + mjd=date2mjd(year,month,dday); + + return mjd; +} + +void usage() +{ + return; +} + +// Compute Date from Julian Day +void mjd2date(double mjd,char *date,int length) +{ + double f,jd,dday; + int z,alpha,a,b,c,d,e; + int year,month,day,hour,min; + float sec,x; + + jd=mjd+2400000.5; + jd+=0.5; + + z=floor(jd); + f=fmod(jd,1.); + + if (z<2299161) + a=z; + else { + alpha=floor((z-1867216.25)/36524.25); + a=z+1+alpha-floor(alpha/4.); + } + b=a+1524; + c=floor((b-122.1)/365.25); + d=floor(365.25*c); + e=floor((b-d)/30.6001); + + dday=b-d-floor(30.6001*e)+f; + if (e<14) + month=e-1; + else + month=e-13; + + if (month>2) + year=c-4716; + else + year=c-4715; + + day=(int) floor(dday); + x=24.0*(dday-day); + x=3600.*fabs(x); + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + hour=x; + sec=floor(1000.0*sec)/1000.0; + + if (length==3) + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%06.3f",year,month,day,hour,min,sec); + else if (length==0) + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%02.0f",year,month,day,hour,min,sec); + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// rotate vector +void rotate(int axis,float angle,float *x,float *y,float *z) +{ + float xx,yy,zz; + float ca,sa; + + ca=cos(angle*D2R); + sa=sin(angle*D2R); + + if (axis==0) { + xx= *x; + yy= *y*ca- *z*sa; + zz= *z*ca+ *y*sa; + } + if (axis==1) { + xx= *x*ca- *z*sa; + yy= *y; + zz= *z*ca+ *x*sa; + } + if (axis==2) { + xx= *x*ca- *y*sa; + yy= *y*ca+ *x*sa; + zz= *z; + } + + *x=xx; + *y=yy; + *z=zz; + + return; +} + +// Solar position +void sunpos_xyz(double mjd,xyz_t *pos,double *ra,double *de) +{ + double jd,t,l0,m,e,c,r; + double n,s,ecl; + + jd=mjd+2400000.5; + t=(jd-2451545.0)/36525.0; + l0=modulo(280.46646+t*(36000.76983+t*0.0003032),360.0)*D2R; + m=modulo(357.52911+t*(35999.05029-t*0.0001537),360.0)*D2R; + e=0.016708634+t*(-0.000042037-t*0.0000001267); + c=(1.914602+t*(-0.004817-t*0.000014))*sin(m)*D2R; + c+=(0.019993-0.000101*t)*sin(2.0*m)*D2R; + c+=0.000289*sin(3.0*m)*D2R; + + r=1.000001018*(1.0-e*e)/(1.0+e*cos(m+c)); + n=modulo(125.04-1934.136*t,360.0)*D2R; + s=l0+c+(-0.00569-0.00478*sin(n))*D2R; + ecl=(23.43929111+(-46.8150*t-0.00059*t*t+0.001813*t*t*t)/3600.0+0.00256*cos(n))*D2R; + + *ra=atan2(cos(ecl)*sin(s),cos(s))*R2D; + *de=asin(sin(ecl)*sin(s))*R2D; + + pos->x=r*cos(*de*D2R)*cos(*ra*D2R)*XKMPAU; + pos->y=r*cos(*de*D2R)*sin(*ra*D2R)*XKMPAU; + pos->z=r*sin(*de*D2R)*XKMPAU; + + return; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Greenwich Mean Sidereal Time +double dgmst(double mjd) +{ + double t,dgmst; + + t=(mjd-51544.5)/36525.0; + + dgmst=360.98564736629+t*(0.000387933-t/38710000); + + return dgmst; +} + +// Get observing site +void get_site(int site_id) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64]; + char filename[LIM]; + + sprintf(filename,"%s/data/sites.txt",m.datadir); + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + // Change to km + alt/=1000.0; + + if (id==site_id) { + m.lat=lat; + m.lng=lng; + m.alt=alt; + m.site_id=id; + strcpy(m.observer,observer); + } + + } + fclose(file); + + return; +} diff --git a/satutl.c b/satutl.c new file mode 100644 index 0000000..72376c7 --- /dev/null +++ b/satutl.c @@ -0,0 +1,221 @@ +/* > satutl.c + * + */ + + +#include "sgdp4h.h" + +#include + +static char *st_start(char *buf); +static long i_read(char *str, int start, int stop); +static double d_read(char *str, int start, int stop); + + /* ==================================================================== + Read a string from key board, remove CR/LF etc. + ==================================================================== */ + +void read_kb(char *buf) +{ +int ii; + + fgets(buf, ST_SIZE-1, stdin); + + /* Remove the CR/LF etc. */ + for(ii = 0; ii < ST_SIZE; ii++) + { + if(buf[ii] == '\r' || buf[ii] == '\n') + { + buf[ii] = '\0'; + break; + } + } +} + +/* ==================================================================== + Read orbit parameters for "satno" in file "filename", return -1 if + failed to find the corresponding data. Call with satno = 0 to get the + next elements of whatever sort. + ==================================================================== */ + +int read_twoline(FILE *fp, long search_satno, orbit_t *orb) +{ + static char search[ST_SIZE]; + static char line1[ST_SIZE]; + static char line2[ST_SIZE]; + char *st1, *st2; + int found; + double bm, bx; + + st1 = line1; + st2 = line2; + + do { + if(fgets(line1, ST_SIZE-1, fp) == NULL) return -1; + st1 = st_start(line1); + } while(st1[0] != '1'); + + if(search_satno > 0) + { + found = 0; + } + else + { + found = 1; + search_satno = atol(st1+2); + } + + sprintf(search, "1 %05ld", search_satno); + + do { + st1 = st_start(line1); + if(strncmp(st1, search, 7) == 0) + { + found = 1; + break; + } + } while(fgets(line1, ST_SIZE-1, fp) != NULL); + + + sprintf(search, "2 %05ld", search_satno); + + if(found) + { + fgets(line2, ST_SIZE-1, fp); + st2 = st_start(line2); + } + + if(!found || strncmp(st2, search, 7) != 0) + { + return -1; + } + + orb->ep_year = (int)i_read(st1, 19, 20); + + if(orb->ep_year < 57) orb->ep_year += 2000; + else orb->ep_year += 1900; + + orb->ep_day = d_read(st1, 21, 32); + + bm = d_read(st1, 54, 59) * 1.0e-5; + bx = d_read(st1, 60, 61); + orb->bstar = bm * pow(10.0, bx); + + orb->eqinc = RAD(d_read(st2, 9, 16)); + orb->ascn = RAD(d_read(st2, 18, 25)); + orb->ecc = d_read(st2, 27, 33) * 1.0e-7; + orb->argp = RAD(d_read(st2, 35, 42)); + orb->mnan = RAD(d_read(st2, 44, 51)); + orb->rev = d_read(st2, 53, 63); + orb->norb = i_read(st2, 64, 68); + + orb->satno = search_satno; + + return 0; +} + +/* ================================================================== + Locate the first non-white space character, return location. + ================================================================== */ + +static char *st_start(char *buf) +{ + if(buf == NULL) return buf; + + while(*buf != '\0' && isspace(*buf)) buf++; + +return buf; +} + +/* ================================================================== + Mimick the FORTRAN formatted read (assumes array starts at 1), copy + characters to buffer then convert. + ================================================================== */ + +static long i_read(char *str, int start, int stop) +{ +long itmp=0; +char *buf, *tmp; +int ii; + + start--; /* 'C' arrays start at 0 */ + stop--; + + tmp = buf = (char *)vector(stop-start+2, sizeof(char)); + + for(ii = start; ii <= stop; ii++) + { + *tmp++ = str[ii]; /* Copy the characters. */ + } + *tmp = '\0'; /* NUL terminate */ + + itmp = atol(buf); /* Convert to long integer. */ + free(buf); + +return itmp; +} + +/* ================================================================== + Mimick the FORTRAN formatted read (assumes array starts at 1), copy + characters to buffer then convert. + ================================================================== */ + +static double d_read(char *str, int start, int stop) +{ +double dtmp=0; +char *buf, *tmp; +int ii; + + start--; + stop--; + + tmp = buf = (char *)vector(stop-start+2, sizeof(char)); + + for(ii = start; ii <= stop; ii++) + { + *tmp++ = str[ii]; /* Copy the characters. */ + } + *tmp = '\0'; /* NUL terminate */ + + dtmp = atof(buf); /* Convert to long integer. */ + free(buf); + +return dtmp; +} + +/* ================================================================== + Allocate and check an all-zero array of memory (storage vector). + ================================================================== */ + +void *vector(size_t num, size_t size) +{ +void *ptr; + + ptr = calloc(num, size); + if(ptr == NULL) + { + fatal_error("vector: Allocation failed %u * %u", num, size); + } + +return ptr; +} + +/* ================================================================== + Print out orbital parameters. + ================================================================== */ + +void print_orb(orbit_t *orb) +{ + printf("# Satellite ID = %ld\n", (long)orb->satno); + printf("# Epoch year = %d day = %.8f\n", orb->ep_year, orb->ep_day); + printf("# Eccentricity = %.7f\n", orb->ecc); + printf("# Equatorial inclination = %.4f deg\n", DEG(orb->eqinc)); + printf("# Argument of perigee = %.4f deg\n", DEG(orb->argp)); + printf("# Mean anomaly = %.4f deg\n", DEG(orb->mnan)); + printf("# Right Ascension of Ascending Node = %.4f deg\n", DEG(orb->ascn)); + printf("# Mean Motion (number of rev/day) = %.8f\n", orb->rev); + printf("# BSTAR drag = %.4e\n", orb->bstar); + printf("# Orbit number = %ld\n", orb->norb); +} + +/* ====================================================================== */ diff --git a/satutl.h b/satutl.h new file mode 100644 index 0000000..04e1969 --- /dev/null +++ b/satutl.h @@ -0,0 +1,30 @@ +/* > satutl.h + * + */ + +#ifndef _SATUTL_H +#define _SATUTL_H + +#define ST_SIZE 256 + +#ifdef __cplusplus +extern "C" { +#endif + +/** satutl.c **/ +void read_kb(char *buf); +int read_twoline(FILE *fp, long satno, orbit_t *orb); +void *vector(size_t num, size_t size); +void print_orb(orbit_t *orb); + +/** aries.c **/ +double gha_aries(double jd); + +/** ferror.c **/ +void fatal_error(const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _SATUTL_H */ diff --git a/sgdp4.c b/sgdp4.c new file mode 100644 index 0000000..f48e198 --- /dev/null +++ b/sgdp4.c @@ -0,0 +1,828 @@ +/* > sgdp4.c + * + * 1.00 around 1980 - Felix R. Hoots & Ronald L. Roehrich, from original + * SDP4.FOR and SGP4.FOR + * + ************************************************************************ + * + * Made famous by the spacetrack report No.3: + * "Models for Propogation of NORAD Element Sets" + * Edited and subsequently distributed by Dr. T. S. Kelso. + * + ************************************************************************ + * + * This conversion by: + * Paul S. Crawford and Andrew R. Brooks + * Dundee University + * + * NOTE ! + * This code is supplied "as is" and without warranty of any sort. + * + * (c) 1994-2004, Paul Crawford, Andrew Brooks + * + ************************************************************************ + * + * 1.07 arb Oct 1994 - Transcribed by arb Oct 1994 into 'C', then + * modified to fit Dundee systems by psc. + * + * 1.08 psc Mon Nov 7 1994 - replaced original satpos.c with SGP4 model. + * + * 1.09 psc Wed Nov 9 1994 - Corrected a few minor translation errors after + * testing with example two-line elements. + * + * 1.10 psc Mon Nov 21 1994 - A few optimising tweeks. + * + * 1.11 psc Wed Nov 30 1994 - No longer uses eloset() and minor error in the + * SGP4 code corrected. + * + * 2.00 psc Tue Dec 13 1994 - arb discovered the archive.afit.af.mil FTP site + * with the original FORTRAN code in machine form. + * Tidied up and added support for the SDP4 model. + * + * 2.01 psc Fri Dec 23 1994 - Tested out the combined SGP4/SDP4 code against + * the original FORTRAN versions. + * + * 2.02 psc Mon Jan 02 1995 - Few more tweeks and tidied up the + * doccumentation for more general use. + * + * 3.00 psc Mon May 29 1995 - Cleaned up for general use & distrabution (to + * remove Dundee specific features). + * + * 3.01 psc Mon Jan 12 2004 - Minor bug fix for day calculation. + * + * 3.02 psc Mon Jul 10 2006 - Added if(rk < (real)1.0) test for sub-orbital decay. + * + * 3.03 psc Sat Aug 05 2006 - Added trap for divide-by-zero when calculating xlcof. + * + */ + +static const char SCCSid[] = "@(#)sgdp4.c 3.03 (C) 1995 psc SatLib: Orbital Model"; + +#include +#include +#include + +/* ================ single / double precision fix-ups =============== */ + +#include "sgdp4h.h" + +#define ECC_ZERO ((real)0.0) /* Zero eccentricity case ? */ +#define ECC_ALL ((real)1.0e-4) /* For all drag terms in GSFC case. */ +#define ECC_EPS ((real)1.0e-6) /* Too low for computing further drops. */ +#define ECC_LIMIT_LOW ((real)-1.0e-3) /* Exit point for serious decaying of orbits. */ +#define ECC_LIMIT_HIGH ((real)(1.0 - ECC_EPS)) /* Too close to 1 */ + +#define EPS_COSIO (1.5e-12) /* Minimum divisor allowed for (...)/(1+cos(IO)) */ + +#define TOTHRD (2.0/3.0) + +#if defined( SGDP4_SNGL ) || 0 +#define NR_EPS ((real)(1.0e-6)) /* Minimum ~1e-6 min for float. */ +#else +#define NR_EPS ((real)(1.0e-12)) /* Minimum ~1e-14 for double. */ +//#define NR_EPS ((real)(1.0e-14)) /* Minimum ~1e-14 for double. */ +//#define NR_EPS ((real)(1.0e-8)) /* Minimum ~1e-14 for double. */ +#endif + +#define Q0 ((real)120.0) +#define S0 ((real)78.0) +#define XJ2 ((real)1.082616e-3) +#define XJ3 ((real)-2.53881e-6) +#define XJ4 ((real)-1.65597e-6) +#define XKMPER (6378.135) /* Km per earth radii */ +#define XMNPDA (1440.0) /* Minutes per day */ +#define AE (1.0) /* Earth radius in "chosen units". */ + +#if 0 +/* Original code constants. */ +#define XKE (0.743669161e-1) +#define CK2 ((real)5.413080e-4) /* (0.5 * XJ2 * AE * AE) */ +#define CK4 ((real)0.62098875e-6) /* (-0.375 * XJ4 * AE * AE * AE * AE) */ +#define QOMS2T ((real)1.88027916e-9) /* (pow((Q0 - S0)*AE/XKMPER, 4.0)) */ +#define KS ((real)1.01222928) /* (AE * (1.0 + S0/XKMPER)) */ +#else +/* GSFC improved coeficient resolution. */ +#define XKE ((real)7.43669161331734132e-2) +#define CK2 ((real)(0.5 * XJ2 * AE * AE)) +#define CK4 ((real)(-0.375 * XJ4 * AE * AE * AE * AE)) +#define QOMS2T ((real)1.880279159015270643865e-9) /* (pow((Q0 - S0)*AE/XKMPER, 4.0)) */ +#define KS ((real)(AE * (1.0 + S0/XKMPER))) +#endif +static const real a3ovk2 = (real)(-XJ3 / CK2 * (AE * AE * AE)); + +/* ================= Copy of the orbital elements ==================== */ + +static double xno; /* Mean motion (rad/min) */ +static real xmo; /* Mean "mean anomaly" at epoch (rad). */ +static real eo; /* Eccentricity. */ +static real xincl; /* Equatorial inclination (rad). */ +static real omegao; /* Mean argument of perigee at epoch (rad). */ +static real xnodeo; /* Mean longitude of ascending node (rad, east). */ +static real bstar; /* Drag term. */ + +double SGDP4_jd0; /* Julian Day for epoch (available to outside functions. */ + +/* ================== Local "global" variables for SGP4 ================= */ + +static int imode = SGDP4_NOT_INIT; +static real sinIO, cosIO, sinXMO, cosXMO; +static real c1, c2, c3, c4, c5, d2, d3, d4; +static real omgcof, xmcof, xlcof, aycof; +static real t2cof, t3cof, t4cof, t5cof; +static real xnodcf, delmo, x7thm1, x3thm1, x1mth2; +static real aodp, eta, omgdot, xnodot; +static double xnodp, xmdot; + +static long Isat=0; /* 16-bit compilers need 'long' integer for higher space catalogue numbers. */ +double perigee, period, apogee; + +long Icount = 0; +int MaxNR=0; +extern int Set_LS_zero; /* From deep.c */ + +/* ======================================================================= + The init_sgdp4() function passes all of the required orbital elements to + the sgdp4() function together with the pre-calculated constants. There is + some basic error traps and the detemination of the orbital model is made. + For near-earth satellites (xnodp < 225 minutes according to the NORAD + classification) the SGP4 model is used, with truncated terms for low + perigee heights when the drag terms are high. For deep-space satellites + the SDP4 model is used and the deep-space terms initialised (a slow + process). For orbits with an eccentricity of less than ECC_EPS the model + reverts to a very basic circular model. This is not physically meaningfull + but such a circluar orbit is not either! It is fast though. + Callinr arguments: + + orb : Input, structure with the orbital elements from NORAD 2-line + element data in radian form. + + The return value indicates the orbital model used. + ======================================================================= */ + +int init_sgdp4(orbit_t *orb) +{ +LOCAL_REAL theta2, theta4, xhdot1, x1m5th; +LOCAL_REAL s4, del1, del0; +LOCAL_REAL betao, betao2, coef, coef1; +LOCAL_REAL etasq, eeta, qoms24; +LOCAL_REAL pinvsq, tsi, psisq, c1sq; +LOCAL_DOUBLE a0, a1, epoch; +real temp0, temp1, temp2, temp3; +long iday, iyear; + + /* Copy over elements. */ + /* Convert year to Gregorian with century as 1994 or 94 type ? */ + + iyear = (long)orb->ep_year; + + if (iyear < 1960) + { + /* Assume 0 and 100 both refer to 2000AD */ + iyear += (iyear < 60 ? 2000 : 1900); + } + + if (iyear < 1901 || iyear > 2099) + { + fatal_error("init_sgdp4: Satellite ep_year error %ld", iyear); + imode = SGDP4_ERROR; + return imode; + } + + Isat = orb->satno; + + /* Compute days from 1st Jan 1900 (works 1901 to 2099 only). */ + + iday = ((iyear - 1901)*1461L)/4L + 364L + 1L; + + SGDP4_jd0 = JD1900 + iday + (orb->ep_day - 1.0); /* Julian day number. */ + + epoch = (iyear - 1900) * 1.0e3 + orb->ep_day; /* YYDDD.DDDD as from 2-line. */ + +#ifdef DEBUG + fprintf(stderr, "Epoch = %f SGDP4_jd0 = %f\n", epoch, SGDP4_jd0); +#endif + + eo = (real)orb->ecc; + xno = (double)orb->rev * TWOPI/XMNPDA; /* Radian / unit time. */ + xincl = (real)orb->eqinc; + xnodeo = (real)orb->ascn; + omegao = (real)orb->argp; + xmo = (real)orb->mnan; + bstar = (real)orb->bstar; + + /* A few simple error checks here. */ + + if (eo < (real)0.0 || eo > ECC_LIMIT_HIGH) + { + fatal_error("init_sgdp4: Eccentricity out of range for %ld (%le)", Isat, (double)eo); + imode = SGDP4_ERROR; + return imode; + } + + if (xno < 0.035*TWOPI/XMNPDA || xno > 18.0*TWOPI/XMNPDA) + { + fatal_error("init_sgdp4: Mean motion out of range %ld (%le)", Isat, xno); + imode = SGDP4_ERROR; + return imode; + } + + if (xincl < (real)0.0 || xincl > (real)PI) + { + fatal_error("init_sgdp4: Equatorial inclination out of range %ld (%le)", Isat, DEG(xincl)); + imode = SGDP4_ERROR; + return imode; + } + + /* Start the initialisation. */ + + if (eo < ECC_ZERO) + imode = SGDP4_ZERO_ECC; /* Special mode for "ideal" circular orbit. */ + else + imode = SGDP4_NOT_INIT; + + /* + Recover original mean motion (xnodp) and semimajor axis (aodp) + from input elements. + */ + + SINCOS(xincl, &sinIO, &cosIO); + + theta2 = cosIO * cosIO; + theta4 = theta2 * theta2; + x3thm1 = (real)3.0 * theta2 - (real)1.0; + x1mth2 = (real)1.0 - theta2; + x7thm1 = (real)7.0 * theta2 - (real)1.0; + + a1 = pow(XKE / xno, TOTHRD); + betao2 = (real)1.0 - eo * eo; + betao = SQRT(betao2); + temp0 = (real)(1.5 * CK2) * x3thm1 / (betao * betao2); + del1 = temp0 / (a1 * a1); + a0 = a1 * (1.0 - del1 * (1.0/3.0 + del1 * (1.0 + del1 * 134.0/81.0))); + del0 = temp0 / (a0 * a0); + xnodp = xno / (1.0 + del0); + aodp = (real)(a0 / (1.0 - del0)); + perigee = (aodp * (1.0 - eo) - AE) * XKMPER; + apogee = (aodp * (1.0 + eo) - AE) * XKMPER; + period = (TWOPI * 1440.0 / XMNPDA) / xnodp; + + /* + printf("Perigee = %lf km period = %lf min del0 = %e\n", + perigee, period, del0); + */ + if (perigee <= 0.0) + { + fprintf(stderr, "# Satellite %ld sub-orbital (apogee = %.1f km, perigee = %.1f km)\n", Isat, apogee, perigee); + } + + if (imode == SGDP4_ZERO_ECC) return imode; + + if (period >= 225.0 && Set_LS_zero < 2) + { + imode = SGDP4_DEEP_NORM; /* Deep-Space model(s). */ + } + else if (perigee < 220.0) + { + /* + For perigee less than 220 km the imode flag is set so the + equations are truncated to linear variation in sqrt A and + quadratic variation in mean anomaly. Also the c3 term, the + delta omega term and the delta m term are dropped. + */ + imode = SGDP4_NEAR_SIMP; /* Near-space, simplified equations. */ + } + else + { + imode = SGDP4_NEAR_NORM; /* Near-space, normal equations. */ + } + + /* For perigee below 156 km the values of S and QOMS2T are altered */ + + if (perigee < 156.0) + { + s4 = (real)(perigee - 78.0); + + if(s4 < (real)20.0) + { + fprintf(stderr, "# Very low s4 constant for sat %ld (perigee = %.2f)\n", Isat, perigee); + s4 = (real)20.0; + } + else + { + fprintf(stderr, "# Changing s4 constant for sat %ld (perigee = %.2f)\n", Isat, perigee); + } + + qoms24 = POW4((real)((120.0 - s4) * (AE / XKMPER))); + s4 = (real)(s4 / XKMPER + AE); + } + else + { + s4 = KS; + qoms24 = QOMS2T; + } + + pinvsq = (real)1.0 / (aodp * aodp * betao2 * betao2); + tsi = (real)1.0 / (aodp - s4); + eta = aodp * eo * tsi; + etasq = eta * eta; + eeta = eo * eta; + psisq = FABS((real)1.0 - etasq); + coef = qoms24 * POW4(tsi); + coef1 = coef / POW(psisq, 3.5); + + c2 = coef1 * (real)xnodp * (aodp * + ((real)1.0 + (real)1.5 * etasq + eeta * ((real)4.0 + etasq)) + + (real)(0.75 * CK2) * tsi / psisq * x3thm1 * + ((real)8.0 + (real)3.0 * etasq * ((real)8.0 + etasq))); + + c1 = bstar * c2; + + c4 = (real)2.0 * (real)xnodp * coef1 * aodp * betao2 * (eta * + ((real)2.0 + (real)0.5 * etasq) + eo * ((real)0.5 + (real)2.0 * + etasq) - (real)(2.0 * CK2) * tsi / (aodp * psisq) * ((real)-3.0 * + x3thm1 * ((real)1.0 - (real)2.0 * eeta + etasq * + ((real)1.5 - (real)0.5 * eeta)) + (real)0.75 * x1mth2 * ((real)2.0 * + etasq - eeta * ((real)1.0 + etasq)) * COS((real)2.0 * omegao))); + + c5 = c3 = omgcof = (real)0.0; + + if (imode == SGDP4_NEAR_NORM) + { + /* BSTAR drag terms for normal near-space 'normal' model only. */ + c5 = (real)2.0 * coef1 * aodp * betao2 * + ((real)1.0 + (real)2.75 * (etasq + eeta) + eeta * etasq); + + if(eo > ECC_ALL) + { + c3 = coef * tsi * a3ovk2 * (real)xnodp * (real)AE * sinIO / eo; + } + + omgcof = bstar * c3 * COS(omegao); + } + + temp1 = (real)(3.0 * CK2) * pinvsq * (real)xnodp; + temp2 = temp1 * CK2 * pinvsq; + temp3 = (real)(1.25 * CK4) * pinvsq * pinvsq * (real)xnodp; + + xmdot = xnodp + ((real)0.5 * temp1 * betao * x3thm1 + (real)0.0625 * + temp2 * betao * ((real)13.0 - (real)78.0 * theta2 + + (real)137.0 * theta4)); + + x1m5th = (real)1.0 - (real)5.0 * theta2; + + omgdot = (real)-0.5 * temp1 * x1m5th + (real)0.0625 * temp2 * + ((real)7.0 - (real)114.0 * theta2 + (real)395.0 * theta4) + + temp3 * ((real)3.0 - (real)36.0 * theta2 + (real)49.0 * theta4); + + xhdot1 = -temp1 * cosIO; + xnodot = xhdot1 + ((real)0.5 * temp2 * ((real)4.0 - (real)19.0 * theta2) + + (real)2.0 * temp3 * ((real)3.0 - (real)7.0 * theta2)) * cosIO; + + xmcof = (real)0.0; + if(eo > ECC_ALL) + { + xmcof = (real)(-TOTHRD * AE) * coef * bstar / eeta; + } + + xnodcf = (real)3.5 * betao2 * xhdot1 * c1; + t2cof = (real)1.5 * c1; + + /* Check for possible divide-by-zero for X/(1+cosIO) when calculating xlcof */ + temp0 = (real)1.0 + cosIO; + + if(fabs(temp0) < EPS_COSIO) temp0 = (real)SIGN(EPS_COSIO, temp0); + + xlcof = (real)0.125 * a3ovk2 * sinIO * + ((real)3.0 + (real)5.0 * cosIO) / temp0; + + aycof = (real)0.25 * a3ovk2 * sinIO; + + SINCOS(xmo, &sinXMO, &cosXMO); + delmo = CUBE((real)1.0 + eta * cosXMO); + + if (imode == SGDP4_NEAR_NORM) + { + c1sq = c1 * c1; + d2 = (real)4.0 * aodp * tsi * c1sq; + temp0 = d2 * tsi * c1 / (real)3.0; + d3 = ((real)17.0 * aodp + s4) * temp0; + d4 = (real)0.5 * temp0 * aodp * tsi * ((real)221.0 * aodp + + (real)31.0 * s4) * c1; + t3cof = d2 + (real)2.0 * c1sq; + t4cof = (real)0.25 * ((real)3.0 * d3 + c1 * ((real)12.0 * d2 + + (real)10.0 * c1sq)); + t5cof = (real)0.2 * ((real)3.0 * d4 + (real)12.0 * c1 * d3 + + (real)6.0 * d2 * d2 + (real)15.0 * c1sq * ((real)2.0 * + d2 + c1sq)); + } + else if (imode == SGDP4_DEEP_NORM) + { +#ifdef NO_DEEP_SPACE + fatal_error("init_sgdp4: Deep space equations not supported"); +#else + imode = SGDP4_dpinit(epoch, omegao, xnodeo, xmo, eo, xincl, + aodp, xmdot, omgdot, xnodot, xnodp); +#endif /* !NO_DEEP_SPACE */ + } + +return imode; +} + +/* ======================================================================= + The sgdp4() function computes the Keplarian elements that describe the + position and velocity of the satellite. Depending on the initialisation + (and the compile options) the deep-space perturbations are also included + allowing sensible predictions for most satellites. These output elements + can be transformed to Earth Centered Inertial coordinates (X-Y-Z) and/or + to sub-satellite latitude and longitude as required. The terms for the + velocity solution are often not required so the 'withvel' flag can be used + to by-pass that step as required. This function is normally called through + another since the input 'tsince' is the time from epoch. + Calling arguments: + + tsince : Input, time from epoch (minutes). + + withvel : Input, non-zero if velocity terms required. + + kep : Output, the Keplarian position / velocity of the satellite. + + The return value indicates the orbital mode used. + + ======================================================================= */ + +int sgdp4(double tsince, int withvel, kep_t *kep) +{ +LOCAL_REAL rk, uk, xnodek, xinck, em, xinc; +LOCAL_REAL xnode, delm, axn, ayn, omega; +LOCAL_REAL capu, epw, elsq, invR, beta2, betal; +LOCAL_REAL sinu, sin2u, cosu, cos2u; +LOCAL_REAL a, e, r, u, pl; +LOCAL_REAL sinEPW, cosEPW, sinOMG, cosOMG; +LOCAL_DOUBLE xmp, xl, xlt; +const int MAXI = 10; + +#ifndef NO_DEEP_SPACE +LOCAL_DOUBLE xn, xmam; +#endif /* !NO_DEEP_SPACE */ + +real esinE, ecosE, maxnr; +real temp0, temp1, temp2, temp3; +real tempa, tempe, templ; +int ii; + +#ifdef SGDP4_SNGL +real ts = (real)tsince; +#else +#define ts tsince +#endif /* ! SGDP4_SNGL */ + + /* Update for secular gravity and atmospheric drag. */ + + em = eo; + xinc = xincl; + + xmp = (double)xmo + xmdot * tsince; + xnode = xnodeo + ts * (xnodot + ts * xnodcf); + omega = omegao + omgdot * ts; + + switch(imode) + { + case SGDP4_ZERO_ECC: + /* Not a "real" orbit but OK for fast computation searches. */ + kep->smjaxs = kep->radius = (double)aodp * XKMPER/AE; + kep->theta = fmod(PI + xnodp * tsince, TWOPI) - PI; + kep->eqinc = (double)xincl; + kep->ascn = xnodeo; + + kep->argp = 0; + kep->ecc = 0; + + kep->rfdotk = 0; + if(withvel) + kep->rfdotk = aodp * xnodp * (XKMPER/AE*XMNPDA/86400.0); /* For km/sec */ + else + kep->rfdotk = 0; + + return imode; + + case SGDP4_NEAR_SIMP: + tempa = (real)1.0 - ts * c1; + tempe = bstar * ts * c4; + templ = ts * ts * t2cof; + a = aodp * tempa * tempa; + e = em - tempe; + xl = xmp + omega + xnode + xnodp * templ; + break; + + case SGDP4_NEAR_NORM: + delm = xmcof * (CUBE((real)1.0 + eta * COS(xmp)) - delmo); + temp0 = ts * omgcof + delm; + xmp += (double)temp0; + omega -= temp0; + tempa = (real)1.0 - (ts * (c1 + ts * (d2 + ts * (d3 + ts * d4)))); + tempe = bstar * (c4 * ts + c5 * (SIN(xmp) - sinXMO)); + templ = ts * ts * (t2cof + ts * (t3cof + ts * (t4cof + ts * t5cof))); + //xmp += (double)temp0; + a = aodp * tempa * tempa; + e = em - tempe; + xl = xmp + omega + xnode + xnodp * templ; + break; + +#ifndef NO_DEEP_SPACE + case SGDP4_DEEP_NORM: + case SGDP4_DEEP_RESN: + case SGDP4_DEEP_SYNC: + tempa = (real)1.0 - ts * c1; + tempe = bstar * ts * c4; + templ = ts * ts * t2cof; + xn = xnodp; + + SGDP4_dpsec(&xmp, &omega, &xnode, &em, &xinc, &xn, tsince); + + a = POW(XKE / xn, TOTHRD) * tempa * tempa; + e = em - tempe; + xmam = xmp + xnodp * templ; + + SGDP4_dpper(&e, &xinc, &omega, &xnode, &xmam, tsince); + + if (xinc < (real)0.0) + { + xinc = (-xinc); + xnode += (real)PI; + omega -= (real)PI; + } + + xl = xmam + omega + xnode; + + /* Re-compute the perturbed values. */ + SINCOS(xinc, &sinIO, &cosIO); + + { + real theta2 = cosIO * cosIO; + + x3thm1 = (real)3.0 * theta2 - (real)1.0; + x1mth2 = (real)1.0 - theta2; + x7thm1 = (real)7.0 * theta2 - (real)1.0; + + /* Check for possible divide-by-zero for X/(1+cosIO) when calculating xlcof */ + temp0 = (real)1.0 + cosIO; + + if(fabs(temp0) < EPS_COSIO) temp0 = (real)SIGN(EPS_COSIO, temp0); + + xlcof = (real)0.125 * a3ovk2 * sinIO * + ((real)3.0 + (real)5.0 * cosIO) / temp0; + + aycof = (real)0.25 * a3ovk2 * sinIO; + } + + break; +#endif /* ! NO_DEEP_SPACE */ + + default: + fatal_error("sgdp4: Orbit not initialised"); + return SGDP4_ERROR; + } + + if(a < (real)1.0) + { + fprintf(stderr, "sgdp4: Satellite %05ld crashed at %.3f (a = %.3f Earth radii)\n", Isat, ts, a); + return SGDP4_ERROR; + } + + if(e < ECC_LIMIT_LOW) + { + fprintf(stderr, "sgdp4: Satellite %05ld modified eccentricity too low (ts = %.3f, e = %e < %e)\n", Isat, ts, e, ECC_LIMIT_LOW); + return SGDP4_ERROR; + } + + if(e < ECC_EPS) + { + /*fprintf(stderr, "# ecc %f at %.3f for for %05ld\n", e, ts, Isat);*/ + e = ECC_EPS; + } + else if(e > ECC_LIMIT_HIGH) + { + /*fprintf(stderr, "# ecc %f at %.3f for for %05ld\n", e, ts, Isat);*/ + e = ECC_LIMIT_HIGH; + } + + beta2 = (real)1.0 - e * e; + + /* Long period periodics */ + SINCOS(omega, &sinOMG, &cosOMG); + + temp0 = (real)1.0 / (a * beta2); + axn = e * cosOMG; + ayn = e * sinOMG + temp0 * aycof; + xlt = xl + temp0 * xlcof * axn; + + elsq = axn * axn + ayn * ayn; + if (elsq >= (real)1.0) + { + fprintf(stderr, "sgdp4: SQR(e) >= 1 (%.3f at tsince = %.3f for sat %05ld)\n", elsq, tsince, Isat); + return SGDP4_ERROR; + } + + /* Sensibility check for N-R correction. */ + kep->ecc = sqrt(elsq); + + /* + * Solve Kepler's equation using Newton-Raphson root solving. Here 'capu' is + * almost the "Mean anomaly", initialise the "Eccentric Anomaly" term 'epw'. + * The fmod() saves reduction of angle to +/-2pi in SINCOS() and prevents + * convergence problems. + * + * Later modified to support 2nd order NR method which saves roughly 1 iteration + * for only a couple of arithmetic operations. + */ + + epw = capu = fmod(xlt - xnode, TWOPI); + + maxnr = kep->ecc; + + for(ii = 0; ii < MAXI; ii++) + { + double nr, f, df; + SINCOS(epw, &sinEPW, &cosEPW); + + ecosE = axn * cosEPW + ayn * sinEPW; + esinE = axn * sinEPW - ayn * cosEPW; + + f = capu - epw + esinE; + if (fabs(f) < NR_EPS) break; + + df = 1.0 - ecosE; + + /* 1st order Newton-Raphson correction. */ + nr = f / df; + + if (ii == 0 && FABS(nr) > 1.25*maxnr) + nr = SIGN(maxnr, nr); +#if 1 + /* 2nd order Newton-Raphson correction. */ + else + nr = f / (df + 0.5*esinE*nr); /* f/(df - 0.5*d2f*f/df) */ +#endif + + epw += nr; /* Newton-Raphson correction of -F/DF. */ + //if (fabs(nr) < NR_EPS) break; + } + + /* Short period preliminary quantities */ + temp0 = (real)1.0 - elsq; + betal = SQRT(temp0); + pl = a * temp0; + r = a * ((real)1.0 - ecosE); + invR = (real)1.0 / r; + temp2 = a * invR; + temp3 = (real)1.0 / ((real)1.0 + betal); + cosu = temp2 * (cosEPW - axn + ayn * esinE * temp3); + sinu = temp2 * (sinEPW - ayn - axn * esinE * temp3); + u = ATAN2(sinu, cosu); + sin2u = (real)2.0 * sinu * cosu; + cos2u = (real)2.0 * cosu * cosu - (real)1.0; + temp0 = (real)1.0 / pl; + temp1 = CK2 * temp0; + temp2 = temp1 * temp0; + + /* Update for short term periodics to position terms. */ + + rk = r * ((real)1.0 - (real)1.5 * temp2 * betal * x3thm1) + (real)0.5 * temp1 * x1mth2 * cos2u; + uk = u - (real)0.25 * temp2 * x7thm1 * sin2u; + xnodek = xnode + (real)1.5 * temp2 * cosIO * sin2u; + xinck = xinc + (real)1.5 * temp2 * cosIO * sinIO * cos2u; + + if(rk < (real)1.0) + { +#if 1 + fprintf(stderr, "sgdp4: Satellite %05ld crashed at %.3f (rk = %.3f Earth radii)\n", Isat, ts, rk); +#endif + return SGDP4_ERROR; + } + + kep->radius = rk * XKMPER/AE; /* Into km */ + kep->theta = uk; + kep->eqinc = xinck; + kep->ascn = xnodek; + kep->argp = omega; + kep->smjaxs = a * XKMPER/AE; + + /* Short period velocity terms ?. */ + if (withvel) + { + /* xn = XKE / pow(a, 1.5); */ + temp0 = SQRT(a); + temp2 = (real)XKE / (a * temp0); + + kep->rdotk = ((real)XKE * temp0 * esinE * invR - + temp2 * temp1 * x1mth2 * sin2u) * + (XKMPER/AE*XMNPDA/86400.0); /* Into km/sec */ + + kep->rfdotk = ((real)XKE * SQRT(pl) * invR + temp2 * temp1 * + (x1mth2 * cos2u + (real)1.5 * x3thm1)) * + (XKMPER/AE*XMNPDA/86400.0); + } + else + { + kep->rdotk = kep->rfdotk = 0; + } + +#ifndef SGDP4_SNGL +#undef ts +#endif + +return imode; +} + +/* ==================================================================== + + Transformation from "Kepler" type coordinates to cartesian XYZ form. + Calling arguments: + + K : Kepler structure as filled by sgdp4(); + + pos : XYZ structure for position. + + vel : same for velocity. + + ==================================================================== */ + +void kep2xyz(kep_t *K, xyz_t *pos, xyz_t *vel) +{ +real xmx, xmy; +real ux, uy, uz, vx, vy, vz; +real sinT, cosT, sinI, cosI, sinS, cosS; + + /* Orientation vectors for X-Y-Z format. */ + + SINCOS((real)K->theta, &sinT, &cosT); + SINCOS((real)K->eqinc, &sinI, &cosI); + SINCOS((real)K->ascn, &sinS, &cosS); + + xmx = -sinS * cosI; + xmy = cosS * cosI; + + ux = xmx * sinT + cosS * cosT; + uy = xmy * sinT + sinS * cosT; + uz = sinI * sinT; + + /* Position and velocity */ + + if(pos != NULL) + { + pos->x = K->radius * ux; + pos->y = K->radius * uy; + pos->z = K->radius * uz; + } + + if(vel != NULL) + { + vx = xmx * cosT - cosS * sinT; + vy = xmy * cosT - sinS * sinT; + vz = sinI * cosT; + + vel->x = K->rdotk * ux + K->rfdotk * vx; + vel->y = K->rdotk * uy + K->rfdotk * vy; + vel->z = K->rdotk * uz + K->rfdotk * vz; + } + +} + +/* ====================================================================== + Compute the satellite position and/or velocity for a given time (in the + form of Julian day number.) + Calling arguments are: + + jd : Time as Julian day number. + + pos : Pointer to posiition vector, km (NULL if not required). + + vel : Pointer to velocity vector, km/sec (NULL if not required). + + ====================================================================== */ + +int satpos_xyz(double jd, xyz_t *pos, xyz_t *vel) +{ +kep_t K; +int withvel, rv; +double tsince; + + tsince = (jd - SGDP4_jd0) * XMNPDA; + +#ifdef DEBUG + fprintf(stderr, "Tsince = %f\n", tsince); +#endif + + if(vel != NULL) + withvel = 1; + else + withvel = 0; + + rv = sgdp4(tsince, withvel, &K); + + kep2xyz(&K, pos, vel); + +return rv; +} + +/* ==================== End of file sgdp4.c ========================== */ diff --git a/sgdp4h.h b/sgdp4h.h new file mode 100644 index 0000000..8806be2 --- /dev/null +++ b/sgdp4h.h @@ -0,0 +1,360 @@ +/* > sgdp4h.h + * + * + * Paul S. Crawford and Andrew R. Brooks + * Dundee University + * + * NOTE ! + * This code is supplied "as is" and without warranty of any sort. + * + * (c) 1994-2004, Paul Crawford, Andrew Brooks + * + * + * 2.00 psc Sun May 28 1995 - Modifed for non-Dundee use. + * + */ + +#ifndef _SGDP4H_H +#define _SGDP4H_H + +/* + * Set up standard system-dependent names UNIX, LINUX, RISCOS, MSDOS, WIN32 + */ + +#if defined( unix ) +# define UNIX +# if defined( linux ) && !defined( LINUX ) +# define LINUX +# endif +#elif defined( __riscos ) && !defined( RISCOS ) +# define RISCOS +#elif !defined( MSDOS ) && !defined( WIN32 ) && !defined( __CYGWIN__ ) +# define MSDOS +#endif + +/* + * Include files + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef UNIX +#include +#endif + +#ifdef SUN4 +#include +#endif + +#ifdef sun +#include /* solaris 7 has struct timeval in here */ +#include /* for sincos() which is in libsunmath */ +#endif + +#ifdef linux +#include +void sincos(double x, double *s, double *c); /* declared where? */ +#endif + +/* + * ================= SYSTEM SPECIFIC DEFINITIONS ===================== + */ + +/* Use INLINE keyword when declaring inline functions */ +#ifdef WIN32 +#define INLINE __inline +#elif defined( MSDOS ) +#define INLINE +#else +/*UNIX?*/ +#define INLINE inline +#endif + +/* Sun C compiler has automatic inline and doesn't understand inline keyword */ +#ifdef __SUNPRO_C +#undef INLINE +#define INLINE +#define MACROS_ARE_SAFE +#endif + +/* Some very common constants. */ + +#ifndef M_PI +#define M_PI 3.141592653589793 +#endif /* MSDOS */ + +#ifndef PI +#define PI M_PI +#endif + +#define TWOPI (2.0*PI) /* Optimising compiler will deal with this! */ +#define PB2 (0.5*PI) +#define PI180 (PI/180.0) + +#define SOLAR_DAY (1440.0) /* Minutes per 24 hours */ +#define SIDERIAL_DAY (23.0*60.0 + 56.0 + 4.09054/60.0) /* Against stars */ + +#define EQRAD (6378.137) /* Earth radius at equator, km */ +#define LATCON (1.0/298.257) /* Latitude radius constant */ +#define ECON ((1.0-LATCON)*(1.0-LATCON)) + +#define JD1900 2415020.5 /* Julian day number for Jan 1st, 00:00 hours 1900 */ + + +/* + * =============================== MACROS ============================ + * + * + * Define macro for sign transfer, double to nearest (long) integer, + * to square an expression (not nested), and A "safe" square, uses test + * to force correct sequence of evaluation when the macro is nested. + */ + +/* + * These macros are safe since they make no assignments. + */ +#define SIGN(a, b) ((b) >= 0 ? fabs(a) : -fabs(a)) +/* Coordinate conversion macros */ +#define DEG(x) ((x)/PI180) +#define RAD(x) ((x)*PI180) +#define GEOC(x) (atan(ECON*tan(x))) /* Geographic to geocentric. */ +#define GEOG(x) (atan(tan(x)/ECON)) + +/* + * All other compilers can have static inline functions. + * (SQR is used badly here: do_cal.c, glat2lat.c, satpos.c, vmath.h). + */ +static INLINE int NINT(double a) { return (int)(a > 0 ? a+0.5 : a-0.5); } +static INLINE long NLONG(double a) { return (long)(a > 0 ? a+0.5 : a-0.5); } + +static INLINE double DSQR(double a) { return(a*a); } +static INLINE float FSQR(float a) { return(a*a); } +static INLINE int ISQR(int a) { return(a*a); } + +static INLINE double DCUBE(double a) { return(a*a*a); } +static INLINE float FCUBE(float a) { return(a*a*a); } +static INLINE int ICUBE(int a) { return(a*a*a); } + +static INLINE double DPOW4(double a) { a*=a; return(a*a); } +static INLINE float FPOW4(float a) { a*=a; return(a*a); } +static INLINE int IPOW4(int a) { a*=a; return(a*a); } + +static INLINE double DMAX(double a,double b) { if (a>b) return a; else return b; } +static INLINE float FMAX(float a, float b) { if (a>b) return a; else return b; } +static INLINE int IMAX(int a, int b) { if (a>b) return a; else return b; } + +static INLINE double DMIN(double a,double b) { if (a +#include + +double **simplex(int n,double *a,double *da) +{ + int i,j; + double **p; + + // Allocate pointers to rows + p=(double **) malloc(sizeof(double *) * (n+1)); + + // Allocate rows and set pointers + for (i=0;i<=n;i++) + p[i]=(double *) malloc(sizeof(double) * (n+1)*n); + + // Fill simplex + for (i=0;i<=n;i++) { + for (j=0;jj) p[i][j]=a[j]-da[j]; + } + } + + return p; +} + diff --git a/skymap.c b/skymap.c new file mode 100644 index 0000000..e4d566d --- /dev/null +++ b/skymap.c @@ -0,0 +1,2423 @@ +#include +#include +#include +#include +#include +#include +#include "cpgplot.h" +#include "cel.h" +#include "sgdp4h.h" + +#define LIM 128 +#define NMAX 256 +#define MMAX 1024 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define XKMPER 6378.135 // Earth radius in km +#define XKMPAU 149597879.691 // AU in km +#define FLAT (1.0/298.257) +#define STDMAG 6.0 + +long Isat=0; +long Isatsel=0; +extern double SGDP4_jd0; + +struct map { + double alpha0,delta0,ra0,de0,azi0,alt0; + double fov,mjd,gmst,w,wl,wb; + float length; + float minmag,maxmag,minrad,maxrad; + char orientation[LIM],projection[4],observer[32]; + char nfd[LIM],starfile[LIM],tlefile[LIM],iodfile[LIM]; + char datadir[LIM],tledir[LIM]; + double lat,lng; + double h,sra,sde,sazi,salt; + float alt,timezone; + float fw,fh; + int level,grid,site_id; + int leoflag,iodflag,iodpoint,visflag,planar,pssatno,psnr; + float psrmin,psrmax,rvis; +} m; +struct sat { + long Isat; + char state[10]; + float mag,age; + double jd; + double dx,dy,dz; + double x,y,z,vx,vy,vz; + double rsun,rearth,h; + double psun,pearth,p,phase; + double r,v,ra,de; + double azi,alt; + double rx,ry; +}; +struct star { + double ra,de; + float pmra,pmde; + float mag; +}; +struct observation { + int ssn,site; + char iod_line[LIM]; + double mjd,ra,de,azi,alt; + double lng,lat; + float elv; + float dt,st,dr,sr,dx,dy,t; + int flag; +}; +int fgetline(FILE *,char *,int); +double modulo(double,double); +void reverse(double,double,double *,double *); +void forward(double,double,double *,double *); +void init_plot(char *,float,float); +void skymap_plot_renew(void); +double gmst(double); +double dgmst(double); +void skymap_plothorizontal_grid(); +void skymap_plotequatorial_grid(); +void skymap_plotconstellations(char *); +void equatorial2horizontal(double,double,double,double *,double *); +void horizontal2equatorial(double,double,double,double *,double *); +void skymap_plotstars(char *); +void obspos_xyz(double,xyz_t *,xyz_t *); +void sunpos_xyz(double,xyz_t *,double *,double *); +void skymap_plotsatellite(char *,int,double,double); +double date2mjd(int,int,double); +struct sat apparent_position(double); +long identify_satellite(char *,int,double,float,float); +int plot_skymap(void); +void rotate(int,float,float *,float *,float *); +int print_tle(char *,int); +void mjd2date(double mjd,char *date); +void dec2sex(double x,char *s,int f,int len); +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de); +double nfd2mjd(char *date); +void nfd_now(char *s); +double sex2dec(char *s); +double doy2mjd(int year,double doy); +struct observation decode_iod_observation(char *iod_line); +void plot_iod(char *filename); +void get_site(int site_id); + +void usage() +{ + return; +} + +void init_skymap(void) +{ + int i; + char *env; + + // Default Map parameters + m.azi0=0; + m.alt0=90.0; + m.w=120.0; + m.wl=180.0; + m.wb=180.0; + m.level=1; + m.minmag=-2.0; + m.maxmag=5.0; + m.maxrad=2.0; + m.minrad=0.02; + strcpy(m.orientation,"horizontal"); + strcpy(m.starfile,"hip6mag.dat"); + strcpy(m.projection,"STG"); + + m.lat=0.0; + m.lng=0.0; + m.alt=0.0; + m.timezone=+0.0; + m.grid=1; + m.length=60.0; + m.mjd=-1.0; + m.leoflag=1; + m.iodflag=0; + m.visflag=0; + m.planar=0; + + // Default settings + strcpy(m.observer,"Unknown"); + m.site_id=0; + + // Get environment variables + env=getenv("ST_DATADIR"); + if (env!=NULL) { + strcpy(m.datadir,env); + } else { + printf("ST_DATADIR environment variable not found.\n"); + } + env=getenv("ST_COSPAR"); + if (env!=NULL) { + get_site(atoi(env)); + } else { + printf("ST_COSPAR environment variable not found.\n"); + } + env=getenv("ST_TLEDIR"); + if (env!=NULL) { + strcpy(m.tledir,env); + } else { + printf("ST_TLEDIR environment variable not found.\n"); + } + sprintf(m.tlefile,"%s/classfd.tle",m.tledir); + + return; +} + +// Get observing site +void get_site(int site_id) +{ + int i=0; + char line[LIM]; + FILE *file; + int id; + double lat,lng; + float alt; + char abbrev[3],observer[64],filename[LIM]; + + sprintf(filename,"%s/data/sites.txt",m.datadir); + file=fopen(filename,"r"); + if (file==NULL) { + printf("File with site information not found!\n"); + return; + } + while (fgets(line,LIM,file)!=NULL) { + // Skip + if (strstr(line,"#")!=NULL) + continue; + + // Strip newline + line[strlen(line)-1]='\0'; + + // Read data + sscanf(line,"%4d %2s %lf %lf %f", + &id,abbrev,&lat,&lng,&alt); + strcpy(observer,line+38); + + // Change to km + alt/=1000.0; + + if (id==site_id) { + m.lat=lat; + m.lng=lng; + m.alt=alt; + m.site_id=id; + strcpy(m.observer,observer); + } + + } + fclose(file); + + return; +} + +void read_iod(char *filename,int iobs) +{ + int i=0; + char line[LIM]; + FILE *file; + struct observation obs; + + file=fopen(filename,"r"); + // Read data + while (fgets(line,LIM,file)!=NULL) { + if (strlen(line)<10) + continue; + if (strstr(line,"#")==NULL) { + obs=decode_iod_observation(line); + if (i==iobs) { + printf("%s\n",obs.iod_line); + break; + } + i++; + } + } + fclose(file); + + // Set parameters + get_site(obs.site); + m.mjd=obs.mjd; + m.ra0=obs.ra; + m.de0=obs.de; + strcpy(m.orientation,"equatorial"); + m.level=6; + + return; +} + + +int main(int argc,char *argv[]) +{ + int i,arg=0; + + // Redirect stderr + freopen("/dev/null","w",stderr); + + init_skymap(); + + // Decode options + while ((arg=getopt(argc,argv,"t:c:i:r:d:hs:I:l:P:R:V:"))!=-1) { + switch(arg) { + + case 't': + strcpy(m.nfd,optarg); + m.mjd=nfd2mjd(m.nfd); + break; + + case 'c': + strcpy(m.tlefile,optarg); + break; + + case 'I': + strcpy(m.iodfile,optarg); + m.iodpoint=0; + m.leoflag=0; + read_iod(m.iodfile,m.iodpoint); + m.iodflag=1; + break; + + case 'l': + m.length=atof(optarg); + break; + + case 's': + get_site(atoi(optarg)); + break; + + case 'i': + Isatsel=atoi(optarg); + m.leoflag=0; + break; + + case 'P': + m.planar=1; + m.pssatno=atoi(optarg); + m.psrmin=300; + m.psrmax=1000; + m.psnr=8; + break; + + case 'R': + m.psrmin=atof(optarg); + m.psrmax=atof(optarg); + m.psnr=1; + break; + + case 'V': + m.visflag=1; + m.rvis=atof(optarg); + break; + + case 'r': + m.ra0=15.0*sex2dec(optarg); + strcpy(m.orientation,"equatorial"); + m.level=5; + break; + + case 'd': + m.de0=sex2dec(optarg); + strcpy(m.orientation,"equatorial"); + m.level=5; + break; + + case 'h': + usage(); + return 0; + break; + + default: + usage(); + return 0; + } + } + + init_plot("/xs",10,0.75); + + plot_skymap(); + + cpgend(); + + fclose(stderr); + + return 0; +} + +// Plot visibility contours +void plot_visibility(float h) +{ + int i,j,k,nx=300,ny=200,nc; + float xmin,xmax,ymin,ymax; + double rx,ry,azi,alt,ra,de; + xyz_t obspos,obsvel,satpos,sunpos; + double dx,dy,dz,r,ax,ay,az,d,dr,sra,sde; + float rsun,rearth,psun,pearth,p,phase,mag; + char state[10]; + float *cont,cmax; + float tr[6]; + float c[]={0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0}; + + // Allocate + cont=(float *) malloc(sizeof(float)*nx*ny); + + // Limits + xmin=-1.5*m.w; + xmax=1.5*m.w; + ymin=-m.w; + ymax=m.w; + + // Transformation matrix + tr[2]=0.0; + tr[1]=(xmax-xmin)/(float) nx; + tr[0]=xmin-0.5*tr[1]; + tr[4]=0.0; + tr[5]=(ymax-ymin)/(float) ny; + tr[3]=ymin-0.5*tr[5]; + + // Get observer and solar position + obspos_xyz(m.mjd,&obspos,&obsvel); + sunpos_xyz(m.mjd,&sunpos,&sra,&sde); + + for (i=0;i-psun && ppsun) { + strcpy(state,"sunlit"); + cpgsci(7); + } + + // Phase + phase=acos(((obspos.x-satpos.x)*(sunpos.x-satpos.x)+(obspos.y-satpos.y)*(sunpos.y-satpos.y)+(obspos.z-satpos.z)*(sunpos.z-satpos.z))/(rsun*d))*R2D; + + // Magnitude + if (strcmp(state,"sunlit")==0) + mag=STDMAG-15.0+5*log10(d)-2.5*log10(sin(phase*D2R)+(M_PI-phase*D2R)*cos(phase*D2R)); + else + mag=15; + } + k=i+nx*j; + cont[k]=mag; + } + } + + // Find maximum contour value + for (i=0,j=0;icmax) cmax=cont[i]; + j++; + } + } + nc=(int) cmax+1.0; + + // Plot contours + cpgsci(7); + cpgcont(cont,nx,ny,1,nx,1,ny,c,nc,tr); + + // Label contours + cpgsch(0.8); + for (i=0;i120.0) + m.w=120.0; + + if (m.level==1) { + m.w=120; + m.minmag=-2.0; + m.maxmag=5.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/hip6mag.dat",m.datadir); + } + if (m.level==2) { + m.w=90; + m.minmag=-1.5; + m.maxmag=5.5; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/hip6mag.dat",m.datadir); + } + if (m.level==3) { + m.w=60; + m.minmag=-1.0; + m.maxmag=6.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/hip6mag.dat",m.datadir); + } + if (m.level==4) { + m.w=30; + m.minmag=-0.5; + m.maxmag=6.5; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc8mag.dat",m.datadir); + } + if (m.level==5) { + m.w=20; + m.minmag=0.0; + m.maxmag=7.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc8mag.dat",m.datadir); + } + if (m.level==6) { + m.w=10; + m.minmag=1.0; + m.maxmag=8.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc8mag.dat",m.datadir); + } + if (m.level==7) { + m.w=5; + m.minmag=2.0; + m.maxmag=9.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc10mag.dat",m.datadir); + } + if (m.level==8) { + m.w=2; + m.minmag=3.0; + m.maxmag=10.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc10mag.dat",m.datadir); + } + if (m.level==9) { + m.w=1; + m.minmag=3.0; + m.maxmag=10.0; + m.maxrad=2.0; + m.minrad=0.02; + sprintf(m.starfile,"%s/data/tyc12mag.dat",m.datadir); + } + /* + // Star files + if (m.w>90.0 && m.w<=120.0) { + strcpy(m.starfile,"data/hip6mag.dat"); + m.minmag=-2.0; + m.maxmag=4.5; + } else if (m.w>60.0 && m.w<=90.0) { + strcpy(m.starfile,"data/hip6mag.dat"); + m.minmag=-2.0; + m.maxmag=5.0; + } else if (m.w>20.0 && m.w<=60.0) { + strcpy(m.starfile,"data/hip6mag.dat"); + m.minmag=0.0; + m.maxmag=6.0; + } else if (m.w<=20.0) { + strcpy(m.starfile,"data/tyc8mag.dat"); + m.minmag=2.0; + m.maxmag=8.0; + } + */ + + + return; +} + +// Get a x and y from an AZI, ALT +void forward(double alpha,double delta,double *x,double *y) +{ + int i; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + if (strcmp(m.orientation,"horizontal")==0) { + cel.ref[0]=m.azi0; + cel.ref[1]=m.alt0; + } else if (strcmp(m.orientation,"equatorial")==0) { + cel.ref[0]=m.ra0; + cel.ref[1]=m.de0; + } + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(m.projection,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd(m.projection,alpha,delta,&cel,&phi,&theta,&prj,x,y)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + + // Flip equatorial axis + if (strcmp(m.orientation,"equatorial")==0) + *x*=-1; + + return; +} + +// Get an AZI, ALT from x and y +void reverse(double x,double y,double *alpha,double *delta) +{ + int i; + double phi,theta; + struct celprm cel; + struct prjprm prj; + + // Flip equatorial axis + if (strcmp(m.orientation,"equatorial")==0) + x*=-1; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angless + if (strcmp(m.orientation,"horizontal")==0) { + cel.ref[0]=m.azi0; + cel.ref[1]=m.alt0; + } else if (strcmp(m.orientation,"equatorial")==0) { + cel.ref[0]=m.ra0; + cel.ref[1]=m.de0; + } + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(m.projection,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celrev(m.projection,x,y,&prj,&phi,&theta,&cel,alpha,delta)) { + printf("Error in Projection (celrev)\n"); + return; + } + } + return; +} + + +// Greenwich Mean Sidereal Time +double gmst(double mjd) +{ + double t,gmst; + + t=(mjd-51544.5)/36525.0; + + gmst=modulo(280.46061837+360.98564736629*(mjd-51544.5)+t*t*(0.000387933-t/38710000),360.0); + + return gmst; +} + +// Return x modulo y [0,y) +double modulo(double x,double y) +{ + x=fmod(x,y); + if (x<0.0) x+=y; + + return x; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Plot field of view +void skymap_plot_fov() +{ + int i,j,n=50; + double rx,ry,azi,alt; + float x,y; + // double azi0[]={44.0,7.0,30.0,44.0,44.0}; + // double alt0[]={16.0,16.0,60.0,60.0,16.0}; + double azi0[]={45.0,7.0,7.0,45.0,45.0}; + double alt0[]={20.0,20.0,50.0,50.0,20.0}; + double azi1[]={150.0,140.0,140.0,150.0,150.0}; + double alt1[]={26.0,26.0,31.0,31.0,26.0}; + + /* + for (i=0;i0) { + if (strlen(line)<2) { + i=0; + continue; + } + status=sscanf(line,"%lf %lf",&azi,&alt); + forward(azi+180.0,alt,&rx,&ry); + x=(float) rx; + y=(float) ry; + if (i==0) cpgmove(x,y); + if (fabs(x)<=1.5*m.w && fabs(y)<=m.w) + cpgdraw(x,y); + else + cpgmove(x,y); + i++; + } + fclose(file); + } + + return; +} + +// Plot an equatorial grid +void skymap_plotequatorial_grid() +{ + int i,j; + double rx,ry,ra,de; + float x,y; + float sch; + int sci,sls; + + // Get setup + cpgqch(&sch); + cpgqls(&sls); + cpgqci(&sci); + + // Plot grid + cpgsci(15); + cpgsls(2); + + // Declinations + if (m.grid==1) { + for (de=-80.0;de<=80.0;de+=20.0) { + for (i=0;i0;i++) { + if (strchr(line,'#')!=NULL) continue; + sscanf(line,"%lu %i %lf %lf\n",&tyc,&flag,&ra0,&de0); + precess(mjd0,ra0,de0,m.mjd,&ra,&de); + if (strcmp(m.orientation,"horizontal")==0) { + equatorial2horizontal(m.mjd,ra,de,&azi,&alt); + forward(azi,alt,&rx,&ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(ra,de,&rx,&ry); + } + x=(float) rx; + y=(float) ry; + if (i==0) cpgmove(x,y); + // if (fabs(x)<=m.w && fabs(y)<=m.w) { + if (flag==0) cpgmove(x,y); + if (flag==1) cpgdraw(x,y); + // } else + // cpgmove(x,y); + } + fclose(file); + cpgsci(1); + + return; +} + +// Convert equatorial into horizontal coordinates +void equatorial2horizontal(double mjd,double ra,double de,double *azi,double *alt) +{ + double h; + + h=gmst(mjd)+m.lng-ra; + + *azi=modulo(atan2(sin(h*D2R),cos(h*D2R)*sin(m.lat*D2R)-tan(de*D2R)*cos(m.lat*D2R))*R2D,360.0); + *alt=asin(sin(m.lat*D2R)*sin(de*D2R)+cos(m.lat*D2R)*cos(de*D2R)*cos(h*D2R))*R2D; + + return; +} + +// Convert horizontal into equatorial coordinates +void horizontal2equatorial(double mjd,double azi,double alt,double *ra,double *de) +{ + double h; + + h=atan2(sin(azi*D2R),cos(azi*D2R)*sin(m.lat*D2R)+tan(alt*D2R)*cos(m.lat*D2R))*R2D; + *ra=modulo(gmst(mjd)+m.lng-h,360.0); + *de=asin(sin(m.lat*D2R)*sin(alt*D2R)-cos(m.lat*D2R)*cos(alt*D2R)*cos(azi*D2R))*R2D; + if (*ra<0.0) + *ra+=360.0; + + return; +} + + +// Plot the stars +void skymap_plotstars(char *filename) +{ + int i,j; + unsigned long hip; + double rx,ry,azi,alt; + double ra,de,ra0,de0,mjd0=51544.5; + // double mjd0=48348.3125; // J1991.25 + float x,y,vmag; + float rad; + char line[LIM]; + FILE *file; + struct star s; + + /* + // 20130118 Testing of the binary catalog also used in plotfits + file=fopen("software/sattools/data/tycho2.dat","rb"); + while (!feof(file)) { + fread(&s,sizeof(struct star),1,file); + vmag=s.mag; + ra0=s.ra; + de0=s.de; + if (vmag<=m.maxmag) { + precess(mjd0,ra0,de0,m.mjd,&ra,&de); + if (strcmp(m.orientation,"horizontal")==0) { + equatorial2horizontal(m.mjd,ra,de,&azi,&alt); + forward(azi,alt,&rx,&ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(ra,de,&rx,&ry); + } + x=(float) rx; + y=(float) ry; + + // Star size + rad=m.maxrad+(m.minrad-m.maxrad)*(vmag-m.minmag)/(m.maxmag-m.minmag); + rad*=m.w/90.0; + cpgsci(0); + cpgcirc(x,y,1.3*rad); + cpgsci(1); + cpgcirc(x,y,rad); + } + } + fclose(file); + */ + + // Loop over file + file=fopen(filename,"r"); + if (file==NULL) { + printf("Star file not found\n"); + exit(1); + } + while (fgetline(file,line,LIM)>0) { + // Failed for Tycho files + // sscanf(line,"%i %lf %lf %f\n",&hip,&ra,&de,&vmag); + // Skipping star ID + sscanf(line+13,"%lf %lf %f\n",&ra0,&de0,&vmag); + + if (vmag<=m.maxmag) { + precess(mjd0,ra0,de0,m.mjd,&ra,&de); + if (strcmp(m.orientation,"horizontal")==0) { + equatorial2horizontal(m.mjd,ra,de,&azi,&alt); + forward(azi,alt,&rx,&ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(ra,de,&rx,&ry); + } + x=(float) rx; + y=(float) ry; + + if (fabs(rx)<0.02 && fabs(ry)<0.02) + printf("%lf %lf %lf %lf %f\n",ra0,de0,ra,de,vmag); + + // Star size + rad=m.maxrad+(m.minrad-m.maxrad)*(vmag-m.minmag)/(m.maxmag-m.minmag); + rad*=m.w/90.0; + cpgsci(0); + cpgcirc(x,y,1.3*rad); + cpgsci(1); + cpgcirc(x,y,rad); + } + } + fclose(file); + + return; +} + +// Observer position +void obspos_xyz(double mjd,xyz_t *pos,xyz_t *vel) +{ + double ff,gc,gs,theta,s,dtheta; + + s=sin(m.lat*D2R); + ff=sqrt(1.0-FLAT*(2.0-FLAT)*s*s); + gc=1.0/ff+m.alt/XKMPER; + gs=(1.0-FLAT)*(1.0-FLAT)/ff+m.alt/XKMPER; + + theta=gmst(mjd)+m.lng; + dtheta=dgmst(mjd)*D2R/86400; + + pos->x=gc*cos(m.lat*D2R)*cos(theta*D2R)*XKMPER; + pos->y=gc*cos(m.lat*D2R)*sin(theta*D2R)*XKMPER; + pos->z=gs*sin(m.lat*D2R)*XKMPER; + vel->x=-gc*cos(m.lat*D2R)*sin(theta*D2R)*XKMPER*dtheta; + vel->y=gc*cos(m.lat*D2R)*cos(theta*D2R)*XKMPER*dtheta; + vel->z=0.0; + + return; +} + +// Plot satellite track +void skymap_plotsatellite(char *filename,int satno,double mjd0,double dt) +{ + orbit_t orb; + struct sat s; + int imode,flag,fflag,i; + FILE *fp=NULL; + xyz_t satpos,obspos,satvel,sunpos; + double mjd,jd,dx,dy,dz; + double rx,ry,ra,de,azi,alt,r,t; + float x,y; + char norad[7],satname[30],date[24];; + float isch; + float rsun,rearth,psun,pearth,p; + int priority[]={24680,28888,15071,26934,37348,5678,5679,5680,5681,5682,8818,8835,8836,8884,10502,10529,10544,10594,11720,11731,11732,11745,13791,13844,13845,13874}; + + // Open TLE file + fp=fopen(filename,"rb"); + if (fp==NULL) + fatal_error("File open failed for reading %s\n",filename); + + // Read TLEs + while (read_twoline(fp,satno,&orb)==0) { + Isat=orb.satno; + imode=init_sgdp4(&orb); + + if (m.leoflag==1 && orb.rev<3) + continue; + if (m.leoflag==2 && orb.rev>=3) + continue; + + sprintf(norad," %ld",Isat); + + if (imode==SGDP4_ERROR) + continue; + + for (flag=0,fflag=0,t=0.0;t-s.psun && s.p-s.pearths.psun) { + for (i=0;ix=r*cos(*de*D2R)*cos(*ra*D2R)*XKMPAU; + pos->y=r*cos(*de*D2R)*sin(*ra*D2R)*XKMPAU; + pos->z=r*sin(*de*D2R)*XKMPAU; + + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// Present nfd +void nfd_now(char *s) +{ + time_t rawtime; + struct tm *ptm; + + // Get UTC time + time(&rawtime); + ptm=gmtime(&rawtime); + + sprintf(s,"%04d-%02d-%02dT%02d:%02d:%02d",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); + + return; +} + +// nfd2mjd +double nfd2mjd(char *date) +{ + int year,month,day,hour,min,sec; + double mjd,dday; + + sscanf(date,"%04d-%02d-%02dT%02d:%02d:%02d",&year,&month,&day,&hour,&min,&sec); + dday=day+hour/24.0+min/1440.0+sec/86400.0; + + mjd=date2mjd(year,month,dday); + + return mjd; +} + + +// Computes apparent position +struct sat apparent_position(double mjd) +{ + struct sat s; + double jd,rsun,rearth,rsat; + double dx,dy,dz,dvx,dvy,dvz; + xyz_t satpos,obspos,obsvel,satvel,sunpos; + double sra,sde; + + // Sat ID + s.Isat=Isat; + + // Get Julian Date + jd=mjd+2400000.5; + + // Get positions + obspos_xyz(mjd,&obspos,&obsvel); + satpos_xyz(jd,&satpos,&satvel); + sunpos_xyz(mjd,&sunpos,&sra,&sde); + + // Age + s.age=jd-SGDP4_jd0; + + // Sat positions + s.x=satpos.x; + s.y=satpos.y; + s.z=satpos.z; + s.vx=satvel.x; + s.vy=satvel.y; + s.vz=satvel.z; + + // Sun position from satellite + dx=-satpos.x+sunpos.x; + dy=-satpos.y+sunpos.y; + dz=-satpos.z+sunpos.z; + + // Distances + rsun=sqrt(dx*dx+dy*dy+dz*dz); + rearth=sqrt(satpos.x*satpos.x+satpos.y*satpos.y+satpos.z*satpos.z); + s.h=rearth-XKMPER; + // Angles + s.psun=asin(696.0e3/rsun)*R2D; + s.pearth=asin(6378.135/rearth)*R2D; + s.p=acos((-dx*satpos.x-dy*satpos.y-dz*satpos.z)/(rsun*rearth))*R2D; + + // Visibility state + if (s.p-s.pearth<-s.psun) + strcpy(s.state,"eclipsed"); + else if (s.p-s.pearth>-s.psun && s.p-s.pearths.psun) + strcpy(s.state,"sunlit"); + + // Position differences + dx=satpos.x-obspos.x; + dy=satpos.y-obspos.y; + dz=satpos.z-obspos.z; + dvx=satvel.x-obsvel.x; + dvy=satvel.y-obsvel.y; + dvz=satvel.z-obsvel.z; + + // Celestial position + s.r=sqrt(dx*dx+dy*dy+dz*dz); + s.v=(dvx*dx+dvy*dy+dvz*dz)/s.r; + s.ra=modulo(atan2(dy,dx)*R2D,360.0); + s.de=asin(dz/s.r)*R2D; + + // Phase + s.phase=acos(((obspos.x-satpos.x)*(sunpos.x-satpos.x)+(obspos.y-satpos.y)*(sunpos.y-satpos.y)+(obspos.z-satpos.z)*(sunpos.z-satpos.z))/(rsun*s.r))*R2D; + + // Magnitude + if (strcmp(s.state,"sunlit")==0) + s.mag=STDMAG-15.0+5*log10(s.r)-2.5*log10(sin(s.phase*D2R)+(M_PI-s.phase*D2R)*cos(s.phase*D2R)); + else + s.mag=15; + + + // Convert and project + if (strcmp(m.orientation,"horizontal")==0) { + equatorial2horizontal(mjd,s.ra,s.de,&s.azi,&s.alt); + forward(s.azi,s.alt,&s.rx,&s.ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(s.ra,s.de,&s.rx,&s.ry); + } + + return s; +} + +// Planar search +void planar_search(char *filename,int satno,float rmin,float rmax,int nr) +{ + int i,j,imode; + FILE *fp; + orbit_t orb; + kep_t K; + int withvel,rv; + double tsince,radius,jd; + double st,ct,sn,cn,si,ci,t; + xyz_t satpos,obspos,obsvel,sunpos; + double r,ra,de,dx,dy,dz,rsun,rearth,psun,pearth,p,azi,alt,rx,ry,rx0,ry0,ra0,de0; + double sra,sde; + float phase,mag,mmin; + char state[10]; + + // Open TLE file + fp=fopen(filename,"rb"); + if (fp==NULL) + fatal_error("File open failed for reading %s\n",filename); + + // Read TLEs + while (read_twoline(fp,satno,&orb)==0) { + Isat=orb.satno; + imode=init_sgdp4(&orb); + + if (imode==SGDP4_ERROR) + continue; + + } + fclose(fp); + + // Get Julian Date + jd=m.mjd+2400000.5; + + // Get kepler + tsince=1440.0*(jd-SGDP4_jd0); + rv=sgdp4(tsince,1,&K); + + // Angles + sn=sin(K.ascn); + cn=cos(K.ascn); + si=sin(K.eqinc); + ci=cos(K.eqinc); + + // Loop over radii + for (j=0;j1) + radius=rmin+(rmax-rmin)*(float) j/(float) (nr-1); + else + radius=rmin; + + // Loop over angles + for (i=0,mmin=15.0;i-psun && ppsun) { + strcpy(state,"sunlit"); + cpgsci(7); + } + + // Phase + phase=acos(((obspos.x-satpos.x)*(sunpos.x-satpos.x)+(obspos.y-satpos.y)*(sunpos.y-satpos.y)+(obspos.z-satpos.z)*(sunpos.z-satpos.z))/(rsun*r))*R2D; + + // Magnitude + if (strcmp(state,"sunlit")==0) + mag=STDMAG-15.0+5*log10(r)-2.5*log10(sin(phase*D2R)+(M_PI-phase*D2R)*cos(phase*D2R)); + else + mag=15; + + if (mag0) { + sscanf(line+2,"%ld",&Isat); + if (line[0]=='1' && (long) Isat==satno) { + printf("\n%s%s",pline,line); + fgetline(fp,line,LIM); + printf("%s\n",line); + flag=1; + } + + strcpy(pline,line); + } + fclose(fp); + + return flag; +} + +// Identify satellite +long identify_satellite(char *filename,int satno,double mjd,float rx,float ry) +{ + long Isatmin=0; + int imode; + FILE *fp; + struct sat s,smin; + orbit_t orb; + float dr,drmin,agemin; + char line[LIM],pline[LIM]; + char sra[16],sde[16]; + + // Open TLE file + fp=fopen(filename,"rb"); + if (fp==NULL) + fatal_error("File open failed for reading %s\n",filename); + + // Read TLEs + while (read_twoline(fp,satno,&orb)==0) { + Isat=orb.satno; + imode=init_sgdp4(&orb); + + if (imode==SGDP4_ERROR) + continue; + + // Compute apparent position + s=apparent_position(mjd); + + // Offset + dr=sqrt(pow(rx-s.rx,2)+pow(ry-s.ry,2)); + + if (Isatmin==0 || dr60.0) + cpgcirc((float) rx,(float) ry,2.0); + else + cpgcirc((float) rx,(float) ry,0.25); + cpgsci(1); + + return; +} + +// plot skymap +int plot_skymap(void) +{ + int redraw=1,fov=2,status; + float x,y; + char c,text[256],sra[16],sde[16],filename[LIM]; + double ra,de,azi,alt,rx,ry; + xyz_t sunpos; + float focallength[]={28,35,50,100,200,300}; + + for (;;) { + if (redraw>0) { + // Get present mjd + if (m.mjd<0.0) { + nfd_now(m.nfd); + m.mjd=nfd2mjd(m.nfd); + } + + // Get locations + if (strcmp(m.orientation,"horizontal")==0) + horizontal2equatorial(m.mjd,m.azi0,m.alt0,&m.ra0,&m.de0); + else if (strcmp(m.orientation,"equatorial")==0) + equatorial2horizontal(m.mjd,m.ra0,m.de0,&m.azi0,&m.alt0); + + // Get sun position + sunpos_xyz(m.mjd,&sunpos,&m.sra,&m.sde); + equatorial2horizontal(m.mjd,m.sra,m.sde,&m.sazi,&m.salt); + + cpgscr(0,0.0,0.0,0.0); + cpgeras(); + + // Create window + cpgsvp(0.01,0.99,0.01,0.99); + cpgwnad(-1.5*m.w,1.5*m.w,-m.w,m.w); + + // Set background + if (m.salt>0.0) + cpgscr(0,0.0,0.0,0.4); + else if (m.salt>-6.0) + cpgscr(0,0.0,0.0,0.3); + else if (m.salt>-12.0) + cpgscr(0,0.0,0.0,0.2); + else if (m.salt>-18.0) + cpgscr(0,0.0,0.0,0.1); + else + cpgscr(0,0.0,0.0,0.0); + cpgsci(0); + cpgrect(-1.5*m.w,1.5*m.w,-m.w,m.w); + cpgsci(1); + + cpgbox("BC",0.,0,"BC",0.,0); + cpgpt1(0.0,0.0,2); + + // Plot field-of-view + if (fov>=0) { + cpgsfs(2); + + m.fw=atan(0.5*6.3265/focallength[fov])*R2D; + m.fh=atan(0.5*4.6389/focallength[fov])*R2D; + //m.fw=atan(0.5*22.3/focallength[fov])*R2D; + //m.fh=atan(0.5*14.9/focallength[fov])*R2D; + + cpgrect(-m.fw,m.fw,-m.fh,m.fh); + cpgsfs(1); + } + + + // Top left string + cpgsch(0.8); + mjd2date(m.mjd,m.nfd); + sprintf(text,"%s UTC; %s (%04d) [%+.4f\\u\\(2218)\\d, %+.4f\\u\\(2218)\\d, %.0fm]",m.nfd,m.observer,m.site_id,m.lat,m.lng,m.alt*1000.0); + cpgmtxt("T",0.6,0.0,0.0,text); + + // Top right string + if (m.planar==0) { + if (Isatsel==0) { + if (m.leoflag==-1) + sprintf(text,"None"); + else if (m.leoflag==0) + sprintf(text,"All"); + else if (m.leoflag==1) + sprintf(text,"LEO"); + else if (m.leoflag==2) + sprintf(text,"HEO/GEO"); + } else if (Isatsel>0) { + sprintf(text,"%05d",(int) Isatsel); + } else { + strcpy(text,""); + } + } else { + if (Isatsel==0) { + if (m.leoflag==0) + sprintf(text,"Planar search: %05d; All",m.pssatno); + else if (m.leoflag==1) + sprintf(text,"Planar search: %05d; LEO",m.pssatno); + else if (m.leoflag==2) + sprintf(text,"Planar search: %05d; HEO/GEO",m.pssatno); + } else if (Isatsel>0) { + sprintf(text,"Planar search: %05d; %05d",m.pssatno,(int) Isatsel); + } else { + sprintf(text,"Planar search: %05d",m.pssatno); + } + } + cpgmtxt("T",0.6,1.0,1.0,text); + + // Bottom string + dec2sex(m.ra0/15.0,sra,0,5); + dec2sex(m.de0,sde,0,4); + sprintf(text,"R: %s; D: %s; A: %.1f; E: %.1f; S: %.1fx%.1f deg; L: %d; O: %s; m < %.1f; f: %.0f mm; l: %.0f s",sra,sde,modulo(m.azi0-180.0,360.0),m.alt0,3.0*m.w,2.0*m.w,m.level,m.orientation,m.maxmag,focallength[fov],m.length); + cpgmtxt("B",1.0,0.0,0.0,text); + cpgsch(1.0); + + // Plot everything + if (strcmp(m.orientation,"horizontal")==0) { + skymap_plothorizontal_grid(); + horizontal2equatorial(m.mjd,m.azi0,m.alt0,&m.ra0,&m.de0); + // skymap_plot_fov(); + } else if (strcmp(m.orientation,"equatorial")==0) { + skymap_plotequatorial_grid(); + equatorial2horizontal(m.mjd,m.ra0,m.de0,&m.azi0,&m.alt0); + } + sprintf(filename,"%s/data/constfig.dat",m.datadir); + skymap_plotconstellations(filename); + skymap_plotstars(m.starfile); + + if (Isatsel>=0 && m.leoflag>=0) + skymap_plotsatellite(m.tlefile,Isatsel,m.mjd,m.length); + + skymap_plotsun(); + } + // Reset redraw + redraw=0; + + // Plot planar search + if (m.planar==1) { + if (m.pssatno==0) + printf("Please select a satellite.\n"); + else + planar_search(m.tlefile,m.pssatno,m.psrmin,m.psrmax,m.psnr); + } + + // Plot IOD points + if (m.iodflag==1) + plot_iod(m.iodfile); + + // Plot visibility + if (m.visflag==1 && strcmp(m.orientation,"horizontal")==0) + plot_visibility(m.rvis); + + // Get time + cpgcurs(&x,&y,&c); + + // Help + if (c=='h' || c=='H') { + printf("q quit\n"); + printf("i Identify satellite\n"); + printf("r Reset satellite selection/real time\n"); + printf("f Select satellite\n"); + printf("l Set integration length\n"); + printf("m Measure cursor RA/Dec, Alt/Azi\n"); + printf("g Toggle grid (on/off)\n"); + printf("o Toggle orientation (horizontal/equatorial)\n"); + printf("c Center on cursor\n"); + printf("z Center on zenith\n"); + printf("n Center on North\n"); + printf("s Center on South\n"); + printf("e Center on East\n"); + printf("w Center on West\n"); + printf("1-9 Zoom level\n"); + printf("+ Zoom in one level\n"); + printf("- Zoom out one level\n"); + printf(". Increase time by 1 step\n"); + printf(", Decrease time by 1 step\n"); + printf("> Increase step size\n"); + printf("< Decrease step size\n"); + printf("P Toggle planar search\n"); + printf("R Read catalog\n"); + printf("L Toggle satellite selection (All, LEO, HEO/GEO, none)\n"); + printf("v Toggle visibility contours\n"); + printf("F Toggle focal length\n"); + printf("TAB Cycle IOD observations\n"); + printf("S Save position/time to schedule\n"); + } + + // Cycle IOD points + if (c=='\t') { + m.iodpoint++; + read_iod(m.iodfile,m.iodpoint); + redraw=1; + } + + // Toggle planar search + if (c=='P') { + if (m.planar==0) { + printf("Provide altitude range (km): [min max num] "); + status=scanf("%f %f %d",&m.psrmin,&m.psrmax,&m.psnr); + m.pssatno=Isatsel; + m.planar=1; + } else if (m.planar==1) { + m.pssatno=0; + m.planar=0; + } + redraw=1; + } + + + + // Toggle visibility contours + if (c=='v') { + if (m.visflag==0) { + printf("Provide altitude (km): "); + status=scanf("%f",&m.rvis); + m.visflag=1; + } else if (m.visflag==1) { + m.visflag=0; + } + redraw=1; + } + + // Identify + if (c=='i') + identify_satellite(m.tlefile,Isatsel,m.mjd,x,y); + + + // Read catalog + if (c=='R') { + printf("TLE catalog name: "); + status=scanf("%s",m.tlefile); + redraw=1; + } + + // Increase/decrease time + if (c=='.') { + m.mjd+=m.length/86400.0; + redraw=1; + } + if (c==',') { + m.mjd-=m.length/86400.0; + redraw=1; + } + + // Increase/decrease step + if (c=='>') { + m.length*=2.0; + redraw=2; + } + if (c=='<') { + m.length/=2.0; + redraw=2; + } + + // Reset + if (c=='r') { + Isatsel=0; + m.length=60.0; + m.mjd=-1.0; + m.iodpoint=-1; + redraw=1; + } + + if (c=='l') { + printf("Enter integration length (s): "); + status=scanf("%f",&m.length); + redraw=1; + } + + // Toggle focal length + if (c=='F') { + fov++; + if (fov>=sizeof(focallength)/sizeof(focallength[0])) + fov=0; + printf("Focallength: %.0f mm\n",focallength[fov]); + m.fw=atan(0.5*6.3265/focallength[fov])*R2D; + m.fh=atan(0.5*4.6389/focallength[fov])*R2D; + //m.fw=atan(0.5*22.3/focallength[fov])*R2D; + //m.fh=atan(0.5*14.9/focallength[fov])*R2D; + printf("FOV: %.1fx%.1f\n",2*m.fw,2*m.fh); + redraw=1; + } + + if (c=='L') { + if (Isatsel==0) { + m.leoflag++; + if (m.leoflag>2) + m.leoflag=-1; + redraw=1; + } else { + printf("Unable, please reset satellite selection\n"); + } + } + + // Find satellite + if (c=='f') { + printf("Enter NORAD Satellite number: "); + status=scanf("%ld",&Isatsel); + + if (Isatsel!=0 && !print_tle(m.tlefile,Isatsel)) { + printf("Satellite %ld not found!\n",Isatsel); + Isatsel=-1; + } + redraw=1; + } + + // Measure + if (c=='m') { + if (strcmp(m.orientation,"horizontal")==0) { + reverse(x,y,&azi,&alt); + horizontal2equatorial(m.mjd,azi,alt,&ra,&de); + } else if (strcmp(m.orientation,"equatorial")==0) { + reverse(x,y,&ra,&de); + equatorial2horizontal(m.mjd,ra,de,&azi,&alt); + } + + printf("RA: %10.4f Dec: %10.4f Azi: %10.4f Alt: %10.4f\n%f %f\n",ra,de,modulo(azi-180.0,360.0),alt,x,y); + } + // Grid on/off + if (c=='g' || c=='G') { + if (m.grid==1) + m.grid=0; + else if (m.grid==0) + m.grid=1; + redraw=1; + } + + // Exit + if (c=='q' || c=='Q') { + cpgend(); + exit(0); + } + + // Recenter + if (c=='c' || c=='C') { + if (strcmp(m.orientation,"horizontal")==0) { + reverse(x,y,&m.azi0,&m.alt0); + horizontal2equatorial(m.mjd,m.azi0,m.alt0,&m.ra0,&m.de0); + } else if (strcmp(m.orientation,"equatorial")==0) { + reverse(x,y,&m.ra0,&m.de0); + horizontal2equatorial(m.mjd,m.ra0,m.de0,&m.azi0,&m.alt0); + } + printf("Centered at: %8.4f %8.4f, %8.4f %8.4f\n",m.ra0,m.de0,m.azi0,m.alt0); + redraw=1; + } + + // Add to schedule + if (c=='S') + schedule(m.nfd,m.ra0,m.de0); + + // Polar + if (c=='z') { + m.azi0=0.0; + m.alt0=90.0; + m.w=120.0; + strcpy(m.orientation,"horizontal"); + m.level=1; + redraw=1; + } + + // South + if (c=='s') { + m.azi0=0.0; + m.alt0=45.0; + strcpy(m.orientation,"horizontal"); + m.level=3; + redraw=1; + } + + // North + if (c=='n') { + m.azi0=180.0; + m.alt0=45.0; + strcpy(m.orientation,"horizontal"); + m.level=3; + redraw=1; + } + + // East + if (c=='e') { + m.azi0=270.0; + m.alt0=45.0; + strcpy(m.orientation,"horizontal"); + m.level=3; + redraw=1; + } + + // West + if (c=='w') { + m.azi0=90.0; + m.alt0=45.0; + strcpy(m.orientation,"horizontal"); + m.level=3; + redraw=1; + } + + // Orientation + if (c=='o' || c=='O') { + if (strcmp(m.orientation,"horizontal")==0) { + strcpy(m.orientation,"equatorial"); + } else if (strcmp(m.orientation,"equatorial")==0) { + strcpy(m.orientation,"horizontal"); + } + redraw=1; + } + + // Level + if (isdigit(c)) { + m.level=c-'0'; + redraw=1; + } + + // Zoom + if (c=='-' && m.level>1) { + m.level--; + redraw=1; + } + if ((c=='+' || c=='=') && m.level<9) { + m.level++; + redraw=1; + } + + // renew + skymap_plot_renew(); + } + + return 0; +} + +// rotate vector +void rotate(int axis,float angle,float *x,float *y,float *z) +{ + float xx,yy,zz; + + if (axis==0) { + xx= *x; + yy= *y*cos(angle*D2R)- *z*sin(angle*D2R); + zz= *z*cos(angle*D2R)+ *y*sin(angle*D2R); + } + if (axis==1) { + xx= *x*cos(angle*D2R)- *z*sin(angle*D2R); + yy= *y; + zz= *z*cos(angle*D2R)+ *x*sin(angle*D2R); + } + if (axis==2) { + xx= *x*cos(angle*D2R)- *y*sin(angle*D2R); + yy= *y*cos(angle*D2R)+ *x*sin(angle*D2R); + zz= *z; + } + + *x=xx; + *y=yy; + *z=zz; + + return; +} + +// Compute Date from Julian Day +void mjd2date(double mjd,char *date) +{ + double f,jd,dday; + int z,alpha,a,b,c,d,e; + int year,month,day,hour,min; + float sec,x; + + jd=mjd+2400000.5; + jd+=0.5; + + z=floor(jd); + f=fmod(jd,1.); + + if (z<2299161) + a=z; + else { + alpha=floor((z-1867216.25)/36524.25); + a=z+1+alpha-floor(alpha/4.); + } + b=a+1524; + c=floor((b-122.1)/365.25); + d=floor(365.25*c); + e=floor((b-d)/30.6001); + + dday=b-d-floor(30.6001*e)+f; + if (e<14) + month=e-1; + else + month=e-13; + + if (month>2) + year=c-4716; + else + year=c-4715; + + day=(int) floor(dday); + x=24.0*(dday-day); + x=3600.*fabs(x); + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + hour=x; + sec=floor(1000.0*sec)/1000.0; + + sprintf(date,"%04d-%02d-%02dT%02d:%02d:%06.3f",year,month,day,hour,min,sec); + + return; +} + +// Convert Decimal into Sexagesimal +void dec2sex(double x,char *s,int f,int len) +{ + int i; + double sec,deg,min; + char sign; + char *form[]={":: ",",, ","hms"," "}; + + sign=(x<0 ? '-' : ' '); + x=3600.*fabs(x); + + sec=fmod(x,60.); + x=(x-sec)/60.; + min=fmod(x,60.); + x=(x-min)/60.; + // deg=fmod(x,60.); + deg=x; + + if (len==7) sprintf(s,"%c%02i%c%02i%c%07.4f%c",sign,(int) deg,form[f][0],(int) min,form[f][1],sec,form[f][2]); + if (len==6) sprintf(s,"%c%02i%c%02i%c%06.3f%c",sign,(int) deg,form[f][0],(int) min,form[f][1],sec,form[f][2]); + if (len==5) sprintf(s,"%c%02i%c%02i%c%05.2f%c",sign,(int) deg,form[f][0],(int) min,form[f][1],sec,form[f][2]); + if (len==4) sprintf(s,"%c%02i%c%02i%c%04.1f%c",sign,(int) deg,form[f][0],(int) min,form[f][1],sec,form[f][2]); + if (len==2) sprintf(s,"%c%02i%c%02i%c%02i%c",sign,(int) deg,form[f][0],(int) min,form[f][1],(int) floor(sec),form[f][2]); + + return; +} + +// Greenwich Mean Sidereal Time +double dgmst(double mjd) +{ + double t,dgmst; + + t=(mjd-51544.5)/36525.0; + + dgmst=360.98564736629+t*(0.000387933-t/38710000); + + return dgmst; +} + +// Precess a celestial position +void precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de) +{ + double t0,t; + double zeta,z,theta; + double a,b,c; + + // Angles in radians + ra0*=D2R; + de0*=D2R; + + // Time in centuries + t0=(mjd0-51544.5)/36525.0; + t=(mjd-mjd0)/36525.0; + + // Precession angles + zeta=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + zeta+=(0.30188-0.000344*t0)*t*t+0.017998*t*t*t; + zeta*=D2R/3600.0; + z=(2306.2181+1.39656*t0-0.000139*t0*t0)*t; + z+=(1.09468+0.000066*t0)*t*t+0.018203*t*t*t; + z*=D2R/3600.0; + theta=(2004.3109-0.85330*t0-0.000217*t0*t0)*t; + theta+=-(0.42665+0.000217*t0)*t*t-0.041833*t*t*t; + theta*=D2R/3600.0; + + a=cos(de0)*sin(ra0+zeta); + b=cos(theta)*cos(de0)*cos(ra0+zeta)-sin(theta)*sin(de0); + c=sin(theta)*cos(de0)*cos(ra0+zeta)+cos(theta)*sin(de0); + + *ra=(atan2(a,b)+z)*R2D; + *de=asin(c)*R2D; + + if (*ra<360.0) + *ra+=360.0; + if (*ra>360.0) + *ra-=360.0; + + return; +} + +// Convert Sexagesimal into Decimal +double sex2dec(char *s) +{ + double x; + float deg,min,sec; + char t[LIM]; + + strcpy(t,s); + + deg=fabs(atof(strtok(t," :"))); + min=fabs(atof(strtok(NULL," :"))); + sec=fabs(atof(strtok(NULL," :"))); + + x=(double) deg+(double) min/60.+(double) sec/3600.; + if (s[0]=='-') x= -x; + + return x; +} + +// DOY to MJD +double doy2mjd(int year,double doy) +{ + int month,k=2; + double day; + + if (year%4==0 && year%400!=0) + k=1; + + month=floor(9.0*(k+doy)/275.0+0.98); + + if (doy<32) + month=1; + + day=doy-floor(275.0*month/9.0)+k*floor((month+9.0)/12.0)+30.0; + + return date2mjd(year,month,day); +} + +// Decode IOD Observations +struct observation decode_iod_observation(char *iod_line) +{ + int year,month,iday,hour,min; + int format,epoch,me,xe,sign; + int site_id; + double sec,ra,mm,ss,de,dd,ds,day,mjd0; + struct observation obs; + char secbuf[6],sn[2],degbuf[3]; + + // Strip newline + iod_line[strlen(iod_line)-1]='\0'; + + // Copy full line + strcpy(obs.iod_line,iod_line); + + // Set usage + obs.flag=1; + + // Get SSN + sscanf(iod_line,"%5d",&obs.ssn); + + // Get site + sscanf(iod_line+16,"%4d",&obs.site); + + // Decode date/time + sscanf(iod_line+23,"%4d%2d%2d%2d%2d%5s",&year,&month,&iday,&hour,&min,secbuf); + sec=atof(secbuf); + sec/=pow(10,strlen(secbuf)-2); + day=(double) iday+(double) hour/24.0+(double) min/1440.0+(double) sec/86400.0; + obs.mjd=date2mjd(year,month,day); + + // Get uncertainty in time + sscanf(iod_line+41,"%1d%1d",&me,&xe); + obs.st=(float) me*pow(10,xe-8); + + // Skip empty observations + if (strlen(iod_line)<64 || (iod_line[54]!='+' && iod_line[54]!='-')) + obs.flag=0; + + // Get format, epoch + sscanf(iod_line+44,"%1d%1d",&format,&epoch); + + // Read position + sscanf(iod_line+47,"%2lf%2lf%3lf%1s",&ra,&mm,&ss,sn); + sscanf(iod_line+55,"%2lf%2lf%2s",&de,&dd,degbuf); + ds=atof(degbuf); + if (strlen(degbuf)==1) + ds*=10; + sign=(sn[0]=='-') ? -1 : 1; + sscanf(iod_line+62,"%1d%1d",&me,&xe); + obs.sr=(float) me*pow(10,xe-8); + + // Decode position + switch(format) + { + // Format 1: RA/DEC = HHMMSSs+DDMMSS MX (MX in seconds of arc) + case 1 : + ra+=mm/60+ss/36000; + de=sign*(de+dd/60+ds/3600); + obs.sr/=3600.0; + break; + // Format 2: RA/DEC = HHMMmmm+DDMMmm MX (MX in minutes of arc) + case 2: + ra+=mm/60+ss/60000; + de=sign*(de+dd/60+ds/6000); + obs.sr/=60.0; + break; + // Format 3: RA/DEC = HHMMmmm+DDdddd MX (MX in degrees of arc) + case 3 : + ra+=mm/60+ss/60000; + de=sign*(de+dd/100+ds/10000); + break; + // Format 7: RA/DEC = HHMMSSs+DDdddd MX (MX in degrees of arc) + case 7 : + ra+=mm/60+ss/36000; + de=sign*(de+dd/100+ds/10000); + break; + default : + printf("%s\n",iod_line); + printf("IOD Format not implemented\n"); + obs.flag=0; + break; + } + // Convert to degrees + ra*=15.0; + + // Get precession epoch + if (epoch==0) { + obs.ra=ra; + obs.de=de; + return obs; + } else if (epoch==4) { + mjd0=33281.9235; + } else if (epoch==5) { + mjd0=51544.5; + } else { + printf("Observing epoch not implemented\n"); + obs.flag=0; + } + + // Precess position + precess(mjd0,ra,de,obs.mjd,&obs.ra,&obs.de); + + // Get horizontal position + equatorial2horizontal(obs.mjd,obs.ra,obs.de,&obs.azi,&obs.alt); + + return obs; +} + +void plot_iod(char *filename) +{ + int i=0; + char line[LIM]; + FILE *file; + struct observation obs; + double azi,alt,rx,ry; + float x,y; + + cpgsci(2); + + file=fopen(filename,"r"); + // Read data + while (fgets(line,LIM,file)!=NULL) { + if (strstr(line,"#")==NULL) { + obs=decode_iod_observation(line); + + if (m.site_id==obs.site) { + if (strcmp(m.orientation,"horizontal")==0) { + forward(obs.azi,obs.alt,&rx,&ry); + } else if (strcmp(m.orientation,"equatorial")==0) { + forward(obs.ra,obs.de,&rx,&ry); + } + x=(float) rx; + y=(float) ry; + + cpgpt1(x,y,4); + } + } + } + fclose(file); + cpgsci(1); + + + return; +} diff --git a/tleinfo.c b/tleinfo.c new file mode 100644 index 0000000..65bc3ad --- /dev/null +++ b/tleinfo.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include "sgdp4h.h" +#include "satutl.h" + +#define LIM 128 +#define XKMPER 6378.135 /* Km per earth radii */ +#define XMNPDA 1440.0 /* Minutes per day */ +#define AE 1.0 /* Earth radius in "chosen units". */ +#define XKE 0.743669161e-1 +#define CK2 5.413080e-4 /* (0.5 * XJ2 * AE * AE) */ +extern double SGDP4_jd0; + +void usage(void) +{ + return; +} + +// Compute Julian Day from Date +double date2mjd(int year,int month,double day) +{ + int a,b; + double jd; + + if (month<3) { + year--; + month+=12; + } + + a=floor(year/100.); + b=2.-a+floor(a/4.); + + if (year<1582) b=0; + if (year==1582 && month<10) b=0; + if (year==1852 && month==10 && day<=4) b=0; + + jd=floor(365.25*(year+4716))+floor(30.6001*(month+1))+day+b-1524.5; + + return jd-2400000.5; +} + +// DOY to MJD +double doy2mjd(int year,double doy) +{ + int month,k=2; + double day; + + if (year%4==0 && year%400!=0) + k=1; + + month=floor(9.0*(k+doy)/275.0+0.98); + + if (doy<32) + month=1; + + day=doy-floor(275.0*month/9.0)+k*floor((month+9.0)/12.0)+30.0; + + return date2mjd(year,month,day); +} + +void orbit(orbit_t orb,float *aodp,float *perigee,float *apogee,float *period) +{ + float xno,eo,xincl; + float a1,betao2,betao,temp0,del1,a0,del0,xnodp; + + xno=orb.rev*2.0*M_PI/XMNPDA; + eo=orb.ecc; + xincl=orb.eqinc; + + a1 = pow(XKE / xno, 2.0/3.0); + betao2 = 1.0 - eo * eo; + betao = sqrt(betao2); + temp0 = (1.5 * CK2) * cos(xincl)*cos(xincl) / (betao * betao2); + del1 = temp0 / (a1 * a1); + a0 = a1 * (1.0 - del1 * (1.0/3.0 + del1 * (1.0 + del1 * 134.0/81.0))); + del0 = temp0 / (a0 * a0); + xnodp = xno / (1.0 + del0); + *aodp = (a0 / (1.0 - del0)); + *perigee = (*aodp * (1.0 - eo) - 1) * XKMPER; + *apogee = (*aodp * (1.0 + eo) - 1) * XKMPER; + *period = (TWOPI * 1440.0 / XMNPDA) / xnodp; + *aodp=(*aodp-1)*XKMPER; + + return; +} + +int main(int argc,char *argv[]) +{ + int arg=0,satno=0,quiet=0; + char tlefile[LIM]; + char line0[70],line1[70],line2[70]; + FILE *file; + orbit_t orb; + float aodp,perigee,apogee,period; + int info=0; + double mjd; + char *env; + + env=getenv("ST_TLEDIR"); + sprintf(tlefile,"%s/classfd.tle",env); + + // Decode options + while ((arg=getopt(argc,argv,"c:i:aq"))!=-1) { + switch (arg) { + + case 'c': + strcpy(tlefile,optarg); + break; + + case 'i': + satno=atoi(optarg); + break; + + case 'a': + info=1; + break; + + case 'q': + quiet=1; + break; + + case 'h': + usage(); + return 0; + break; + + default: + usage(); + return 0; + } + } + + // Open file + file=fopen(tlefile,"rb"); + if (file==NULL) + fatal_error("File open failed for reading \"%s\"",tlefile); + + if (info==0 && quiet==0) + printf("SATNO YEAR DOY INCL ASCN ARGP MA ECC MM\n"); + if (info==1 && quiet==0) + printf("SATNO SEMI PERIGEE APOGEE PERIOD ECC\n"); + + // Loop over file + while (read_twoline(file,satno,&orb)==0) { + orbit(orb,&aodp,&perigee,&apogee,&period); + mjd=doy2mjd(orb.ep_year,orb.ep_day); + if (info==0) printf("%05d %10.4lf %8.4f %8.4f %8.4f %8.4f %8.6f %8.5f\n",orb.satno,mjd,DEG(orb.eqinc),DEG(orb.ascn),DEG(orb.argp),DEG(orb.mnan),orb.ecc,orb.rev); + if (info==1) printf("%05d %9.2f %9.2f %9.2f %8.2f %8.6f %14.8lf\n",orb.satno,aodp,perigee,apogee,period,orb.ecc,mjd); + } + fclose(file); + + return 0; +} diff --git a/uk2iod.c b/uk2iod.c new file mode 100644 index 0000000..13eed3d --- /dev/null +++ b/uk2iod.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#define LIM 128 + +int fgetline(FILE *file,char *s,int lim); +int find_satno(char *desig0) +{ + FILE *file; + int satno=99999,status; + char desig[16]; + char *env,filename[LIM]; + + env=getenv("ST_DATADIR"); + sprintf(filename,"%s/data/desig.txt",env); + file=fopen(filename,"r"); + if (file==NULL) { + fprintf(stderr,"Designation file not found!\n"); + exit(0); + } + while (!feof(file)) { + status=fscanf(file,"%d %s",&satno,desig); + if (strcmp(desig,desig0)==0) + break; + } + fclose(file); + + return satno; +} + + +int main(int argc,char *argv[]) +{ + FILE *file; + char line[LIM]; + int intidy,intido,piece,site,year,month,day,hour,min,sec,fsec,satno; + char desig[16],pdesig[16]; + int format,epoch; + float csec,cang,x; + int rah,ram,rafm,ded,dem,defm; + float tm,tx,am,ax; + char sign; + + file=fopen(argv[1],"r"); + while (fgetline(file,line,LIM)>0) { + // Skip wrong lines + if (!isdigit(line[0])) + continue; + // Skip short lines + if (strlen(line)<55) + continue; + + // Scan line + sscanf(line,"%02d%03d%02d%04d%02d%02d%02d%02d%02d%02d%03d",&intidy,&intido, + &piece, + &site, + &year, + &month, + &day, + &hour, + &min, + &sec, + &fsec); + sscanf(line+27,"%f",&csec); + sscanf(line+33,"%1d",&format); + if (format==2) { + sscanf(line+34,"%02d%02d%d",&rah,&ram,&rafm); + sscanf(line+42,"%c%02d%02d%d",&sign,&ded,&dem,&defm); + } else if (format==3) { + sscanf(line+34,"%02d%02d%d",&rah,&ram,&rafm); + sscanf(line+42,"%c%02d%02d",&sign,&ded,&dem); + } + sscanf(line+50,"%f",&cang); + sscanf(line+54,"%d",&epoch); + + // Year switch + if (year>50) + year+=1900; + else + year+=2000; + + // Format designation + if (piece<26) { + sprintf(desig,"%02d %03d%c",intidy,intido,piece+'A'-1); + sprintf(pdesig,"%02d%03d%c",intidy,intido,piece+'A'-1); + } else { + fprintf(stderr,"Failed to understand designation!\n"); + fprintf(stderr,"%s\n",line); + continue; + } + + // Test data format + if (format==3) { + x=dem*0.6; + dem=(int) floor(x); + defm=(int) (100.0*(x-dem)); + } else if (format!=2) { + fprintf(stderr,"Angle format not implemented!\n"); + fprintf(stderr,"%s\n",line); + continue; + } + + // Fractional seconds + if (fsec<10) + fsec*=100; + else if (fsec<100) + fsec*=10; + + // Time accuracy + if (csec<10) + csec*=0.1; + else if (csec<100) + csec*=0.01; + tx=floor(log10(csec))+8; + tm=floor(csec/pow(10.0,tx-8)); + + // angle accuracy + if (cang<10) + cang*=1; + else if (cang<100) + cang*=0.1; + ax=floor(log10(cang))+8; + am=floor(cang/pow(10.0,ax-8)); + + // Fractional RA + if (rafm<10) + rafm*=100; + else if (rafm<100) + rafm*=10; + + // Fractional DE + if (defm<10) + defm*=10; + else if (defm<100) + defm*=1; + + // Get satellite number + satno=find_satno(pdesig); + + // Format IOD line + printf("%05d %s %04d G %04d%02d%02d%02d%02d%02d%03d %1.0f%1.0f %d%d ",satno,desig,site,year,month,day,hour,min,sec,fsec,tm,tx,format,epoch); + printf("%02d%02d%03d%c%02d%02d%02d %1.0f%1.0f\n",rah,ram,rafm,sign,ded,dem,defm,am,ax); + } + fclose(file); + + return 0; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + diff --git a/versafit.c b/versafit.c new file mode 100644 index 0000000..8b49a44 --- /dev/null +++ b/versafit.c @@ -0,0 +1,168 @@ +// Versatile Fitting Routine +#include +#include +#include +#include + +int OUTPUT=1; // Print output on screen (1 = yes; 0 = no) +int ERRCOMP=0; // Set reduced Chi-Squared to unity (1 = yes; 0 = no) + +int dsmin(double **,double *,int,double,double (*func)(double *)); +double **simplex(int,double *,double *); +double parabolic_root(double,double,double,double); + +// Versafit fitting routine +// +// Inputs: +// m: number of datapoints +// n: number of parameters +// a: parameters +// da: expected spread in parameters +// func: function to fit (Chi-squared function) +// dchisq difference in Chi-squared +// tol: tolerance +// opt: options +// - n: no output +void versafit(int m,int n,double *a,double *da,double (*func)(double *),double dchisq,double tol,char *opt) +{ + int i,j,k,l,nfunk,kmax=50; + double chisqmin; + double *b,*db; + double **p,*y; + double d[2],errcomp; + + // Decode options + if (strchr(opt,'n')!=NULL) OUTPUT=0; + if (strchr(opt,'e')!=NULL) ERRCOMP=1; + + // Intialize y + y=(double *) malloc(sizeof(double) * (n+1)); + + if (dchisq>=0.) { + // Compute simplex and minimize function + p=simplex(n,a,da); + nfunk=dsmin(p,y,n,tol,func); + + // Average parameters + for (i=0;i +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" + +#define LIM 80 +#define NMAX 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI + +struct image { + char filename[64]; + int naxis1,naxis2,naxis3,nframes; + float *zavg,*zstd,*zmax,*znum,*ztrk; + double ra0,de0; + float x0,y0; + float a[3],b[3],xrms,yrms; + double mjd; + float *dt,exptime; + char nfd[32]; + int cospar; +}; +struct image read_fits(char *filename); +void write_pgm(char *filename,struct image img); + +int main(int argc,char *argv[]) +{ + int i; + struct image img; + + img=read_fits(argv[1]); + + write_pgm("avg.pgm",img); + return 0; +} + +// Read fits image +struct image read_fits(char *filename) +{ + int i,j,k,l,m; + qfitsloader ql; + char key[FITS_LINESZ+1]; + char val[FITS_LINESZ+1]; + struct image img; + + // Copy filename + strcpy(img.filename,filename); + + // Image size + img.naxis1=atoi(qfits_query_hdr(filename,"NAXIS1")); + img.naxis2=atoi(qfits_query_hdr(filename,"NAXIS2")); + img.naxis3=atoi(qfits_query_hdr(filename,"NAXIS3")); + + // MJD + img.mjd=(double) atof(qfits_query_hdr(filename,"MJD-OBS")); + strcpy(img.nfd,qfits_query_hdr(filename,"DATE-OBS")); + + // COSPAR ID + img.cospar=atoi(qfits_query_hdr(filename,"COSPAR")); + + // Transformation + img.mjd=atof(qfits_query_hdr(filename,"MJD-OBS")); + img.ra0=atof(qfits_query_hdr(filename,"CRVAL1")); + img.de0=atof(qfits_query_hdr(filename,"CRVAL2")); + img.x0=atof(qfits_query_hdr(filename,"CRPIX1")); + img.y0=atof(qfits_query_hdr(filename,"CRPIX2")); + img.a[0]=0.0; + img.a[1]=3600.0*atof(qfits_query_hdr(filename,"CD1_1")); + img.a[2]=3600.0*atof(qfits_query_hdr(filename,"CD1_2")); + img.b[0]=0.0; + img.b[1]=3600.0*atof(qfits_query_hdr(filename,"CD2_1")); + img.b[2]=3600.0*atof(qfits_query_hdr(filename,"CD2_2")); + img.xrms=3600.0*atof(qfits_query_hdr(filename,"CRRES1")); + img.yrms=3600.0*atof(qfits_query_hdr(filename,"CRRES2")); + img.exptime=atof(qfits_query_hdr(filename,"EXPTIME")); + img.nframes=atoi(qfits_query_hdr(filename,"NFRAMES")); + + // Timestamps + img.dt=(float *) malloc(sizeof(float)*img.nframes); + for (i=0;i=255.0) + z=255.0; + if (z<0.0) + z=0.0; + buffer[l++]=(unsigned char) z; + } + for (i=0;i=255.0) + z=255.0; + if (z<0.0) + z=0.0; + buffer[l++]=(unsigned char) z; + } + } + for (j=0;j=255.0) + z=255.0; + if (z<0.0) + z=0.0; + buffer[l++]=(unsigned char) z; + } + for (i=0;i=255.0) + z=255.0; + if (z<0.0) + z=0.0; + buffer[l++]=(unsigned char) z; + } + } + file=fopen(filename,"wb"); + fprintf(file,"P5\n%d %d\n255\n",2*img.naxis1,2*img.naxis2); + fwrite(buffer,4*n,sizeof(unsigned char),file); + fclose(file); + + return; +} + +// Write pgm file +void write_pgm2(char *filename,struct image img) +{ + int i,j,k; + FILE *file; + float z; + + file=fopen(filename,"w"); + fprintf(file,"P5\n# %.23s\n%d %d\n255\n",img.nfd+1,img.naxis1,img.naxis2); + for (j=0;j255.0) + z=255.0; + if (z<0.0) + z=0.0; + fprintf(file,"%c",(char) z); + } + } + fclose(file); + + return; +} diff --git a/wcsfit.c b/wcsfit.c new file mode 100644 index 0000000..5d322b9 --- /dev/null +++ b/wcsfit.c @@ -0,0 +1,431 @@ +#include +#include +#include +#include +#include "cel.h" +#include "cpgplot.h" +#include "qfits.h" +#include + +#define LIM 256 +#define D2R M_PI/180.0 +#define R2D 180.0/M_PI +#define NMAX 1024 + +struct catalog { + int n; + float x[NMAX],y[NMAX]; + double ra[NMAX],de[NMAX]; + float rx[NMAX],ry[NMAX]; + float xres[NMAX],yres[NMAX],res[NMAX]; + float xrms,yrms,rms; + int usage[NMAX]; +}; +struct image { + int naxis1,naxis2,nframes; + float *zavg,*zstd,*zmax,*znum; + double ra0,de0; + float x0,y0; + float a[2],b[2]; + double mjd; + float *dt; +}; +struct transformation { + double ra0,de0; + float a[3],b[3]; + float x0,y0; +}; +int fgetline(FILE *file,char *s,int lim); +void forward(double ra0,double de0,double ra,double de,float *x,float *y); +void reverse(double ra0,double de0,float x,float y,double *ra,double *de); +struct catalog read_catalog(char *filename); +void lfit2d(float *x,float *y,float *z,int n,float *a); +void add_fits_keywords(struct transformation t,char *filename); +struct image read_fits(char *filename); + +// Modify FITS keywords +void modify_fits_keywords(struct transformation t,char *filename) +{ + char card[FITS_LINESZ+1]; + char key[FITS_LINESZ+1]; + char val[FITS_LINESZ+1]; + char com[FITS_LINESZ+1]; + + sprintf(val,"%f",t.x0); + keytuple2str(card,"CRPIX1",val,""); + qfits_replace_card(filename,"CRPIX1",card); + + sprintf(val,"%f",t.y0); + keytuple2str(card,"CRPIX2",val,""); + qfits_replace_card(filename,"CRPIX2",card); + + sprintf(val,"%f",t.ra0); + keytuple2str(card,"CRVAL1",val,""); + qfits_replace_card(filename,"CRVAL1",card); + + sprintf(val,"%f",t.de0); + keytuple2str(card,"CRVAL2",val,""); + qfits_replace_card(filename,"CRVAL2",card); + + sprintf(val,"%e",t.a[1]/3600.0); + keytuple2str(card,"CD1_1",val,""); + qfits_replace_card(filename,"CD1_1",card); + + sprintf(val,"%e",t.a[2]/3600.0); + keytuple2str(card,"CD1_2",val,""); + qfits_replace_card(filename,"CD1_2",card); + + sprintf(val,"%e",t.b[1]/3600.0); + keytuple2str(card,"CD2_1",val,""); + qfits_replace_card(filename,"CD2_1",card); + + sprintf(val,"%e",t.b[2]/3600.0); + keytuple2str(card,"CD2_2",val,""); + qfits_replace_card(filename,"CD2_2",card); + + return; +} + + +int main(int argc,char *argv[]) +{ + int i,j,k,l,m; + struct catalog c; + struct transformation t; + double ra0,de0; + float rmsmin; + float x[NMAX],y[NMAX],rx[NMAX],ry[NMAX]; + struct image img; + char filename[128]; + + if (argc==1) + strcpy(filename,"test.fits"); + else if (argc==2) + strcpy(filename,argv[1]); + + img=read_fits(filename); + printf("files read\n"); + c=read_catalog("out.dat"); + printf("files read\n"); + + // Initial fit + t.ra0=c.ra[0]; + t.de0=c.de[0]; + t.x0=(float) img.naxis1/2.0; + t.y0=(float) img.naxis2/2.0; + + for (l=0;l<10;l++) { + for (j=0;j<5;j++) { + // Transform + for (i=0;i2*c.rms) + c.usage[i]=0; + } + } + printf("%12.8lf %10.6lf %10.6lf %8.4f %8.4f %8.4f %8.4f\n",img.mjd,t.ra0,t.de0,t.a[1],t.a[2],t.b[1],t.b[2]); + printf("%d/%d %f %f %f\n",m,c.n,c.xrms,c.yrms,c.rms); + + // add_fits_keywords(t,"test.fits"); + modify_fits_keywords(t,filename); + + return 0; +} + +// Read a line of maximum length int lim from file FILE into string s +int fgetline(FILE *file,char *s,int lim) +{ + int c,i=0; + + while (--lim > 0 && (c=fgetc(file)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + return i; +} + +// Read catalog +struct catalog read_catalog(char *filename) +{ + int i=0; + char line[LIM]; + FILE *file; + struct catalog c; + + file=fopen(filename,"r"); + while (fgetline(file,line,LIM)>0) { + sscanf(line,"%f %f %lf %lf",&c.x[i],&c.y[i],&c.ra[i],&c.de[i]); + c.usage[i]=1; + + i++; + } + fclose(file); + c.n=i; + + return c; +} + +// Get a x and y from a RA and Decl +void forward(double ra0,double de0,double ra,double de,float *x,float *y) +{ + int i; + char pcode[4]="TAN"; + double phi,theta; + struct celprm cel; + struct prjprm prj; + double rx,ry; + + // Initialize Projection Parameters + prj.flag=0; + prj.r0=0.; + for (i=0;i<10;prj.p[i++]=0.); + + // Initialize Reference Angles + cel.ref[0]=ra0; + cel.ref[1]=de0; + cel.ref[2]=999.; + cel.ref[3]=999.; + cel.flag=0.; + + if (celset(pcode,&cel,&prj)) { + printf("Error in Projection (celset)\n"); + return; + } else { + if (celfwd(pcode,ra,de,&cel,&phi,&theta,&prj,&rx,&ry)) { + printf("Error in Projection (celfwd)\n"); + return; + } + } + *x=rx*3600.; + *y=ry*3600.; + + return; +} + +// Linear 2D fit +void lfit2d(float *x,float *y,float *z,int n,float *a) +{ + int i,j,m; + double chisq; + gsl_matrix *X,*cov; + gsl_vector *yy,*w,*c; + + X=gsl_matrix_alloc(n,3); + yy=gsl_vector_alloc(n); + w=gsl_vector_alloc(n); + + c=gsl_vector_alloc(3); + cov=gsl_matrix_alloc(3,3); + + // Fill matrices + for(i=0;i