1
0
Fork 0
sattools/addwcs.c

832 lines
19 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wcslib/cel.h>
#include <cpgplot.h>
#include "qfits.h"
#include <gsl/gsl_multifit.h>
#include <getopt.h>
#define NMAX 8192
#define LIM 128
#define D2R M_PI/180.0
#define R2D 180.0/M_PI
struct image {
int naxis,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 precess(double mjd0,double ra0,double de0,double mjd,double *ra,double *de);
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<img.naxis1*img.naxis2;i++)
zavg+=img.zavg[i];
zavg/=(float) img.naxis1*img.naxis2;
for (i=0,zstd=0.0;i<img.naxis1*img.naxis2;i++)
zstd+=pow(img.zavg[i]-zavg,2);
zstd=sqrt(zstd/(float) (img.naxis1*img.naxis2));
zmin=zavg-2*zstd;
zmax=zavg+6*zstd;
cpgopen("1/xs");
cpgwnad(0.0,img.naxis1,0.0,img.naxis2);
cpgctab (heat_l,heat_r,heat_g,heat_b,5,1.0,0.5);
cpgimag(img.zavg,img.naxis1,img.naxis2,1,img.naxis1,1,img.naxis2,zmin,zmax,tr);
cpgbox("BCTSNI",0.,0,"BCTSNI",0.,0);
cpgsci(3);
plot_pixel_catalog(filename);
cpgsci(4);
plot_astrometric_catalog(t,img,mmin);
cpgsci(2);
for (i=0;i<c.n;i++)
cpgpt1(c.x[i]+t.x0,c.y[i]+t.y0,24);
cpgend();
return;
}
void usage(float mmin,float rmin)
{
printf("addwcs: Add/fit World Coordinate System to a FITS file\n\n");
printf("-f <file>: FITS file to add/fit WCS to [required]\n");
printf("-r <file>: FITS file with reference WCS [required]\n");
printf("-m <float>: Magnitude cut-off in Tycho-2 catalog [optional; default %.1f]\n",mmin);
printf("-R <float>: 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 mjd0=51544.5,ra0,de0,ra1,de1;
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) {
precess(mjd0,t.ra0,t.de0,t.mjd,&ra1,&de1);
ra1=modulo(ra1+gmst(img.mjd)-gmst(t.mjd),360.0);
precess(img.mjd,ra1,de1,mjd0,&t.ra0,&t.de0);
}
// Match catalog
c=match_catalogs(catfile,starfile,t,img,rmin,mmin);
// Plot
if (plot==1)
plot_image(img,t,c,catfile,mmin);
// Do fit
if (c.n>10) {
for (l=0;l<10;l++) {
for (j=0;j<5;j++) {
// Transform
for (i=0;i<c.n;i++)
forward(t.ra0,t.de0,c.ra[i],c.de[i],&c.rx[i],&c.ry[i]);
// Select
for (i=0,k=0;i<c.n;i++) {
if (c.usage[i]==1) {
x[k]=c.x[i];
y[k]=c.y[i];
rx[k]=(float) c.rx[i];
ry[k]=(float) c.ry[i];
k++;
}
}
// Fit
lfit2d(x,y,rx,k,t.a);
lfit2d(x,y,ry,k,t.b);
// Move reference point
reverse(t.ra0,t.de0,t.a[0],t.b[0],&ra0,&de0);
t.ra0=ra0;
t.de0=de0;
}
// Compute and plot residuals
for (i=0,t.xrms=0.0,t.yrms=0.0,m=0;i<c.n;i++) {
if (c.usage[i]==1) {
c.xres[i]=c.rx[i]-(t.a[0]+t.a[1]*c.x[i]+t.a[2]*c.y[i]);
c.yres[i]=c.ry[i]-(t.b[0]+t.b[1]*c.x[i]+t.b[2]*c.y[i]);
c.res[i]=sqrt(c.xres[i]*c.xres[i]+c.yres[i]*c.yres[i]);
t.xrms+=c.xres[i]*c.xres[i];
t.yrms+=c.yres[i]*c.yres[i];
t.rms+=c.xres[i]*c.xres[i]+c.yres[i]*c.yres[i];
m++;
}
}
t.xrms=sqrt(t.xrms/(float) m);
t.yrms=sqrt(t.yrms/(float) m);
t.rms=sqrt(t.rms/(float) m);
// Deselect outliers
for (i=0;i<c.n;i++) {
if (c.res[i]>2*t.rms)
c.usage[i]=0;
}
}
} else {
t.xrms=0.0;
t.yrms=0.0;
t.rms=0.0;
}
// Print results
outfile=fopen(calfile,"w");
for (i=0;i<c.n;i++)
if (c.usage[i]==1)
fprintf(outfile,"%10.4f %10.4f %10.6f %10.6f %8.3f %8.3f %8.3f %8.3f %8.3f\n",c.x[i],c.y[i],c.ra[i],c.de[i],c.vmag[i],c.imag[i],c.fb[i],c.fm[i],c.bg[i]);
fclose(outfile);
printf("%s %8.4lf %8.4lf ",fitsfile,t.ra0,t.de0);
printf("%3d/%3d %6.1f %6.1f %6.1f\n",m,c.n,t.xrms,t.yrms,t.rms);
// Add keywords
if (add==1)
add_fits_keywords(t,fitsfile);
else
modify_fits_keywords(t,fitsfile);
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;
// Image size
img.naxis=atoi(qfits_query_hdr(filename,"NAXIS"));
img.naxis1=atoi(qfits_query_hdr(filename,"NAXIS1"));
img.naxis2=atoi(qfits_query_hdr(filename,"NAXIS2"));
// MJD
img.mjd=(double) atof(qfits_query_hdr(filename,"MJD-OBS"));
// Set parameters
ql.xtnum=0;
ql.ptype=PTYPE_FLOAT;
ql.filename=filename;
if (img.naxis==3) {
// Number of frames
img.nframes=atoi(qfits_query_hdr(filename,"NFRAMES"));
// Timestamps
img.dt=(float *) malloc(sizeof(float)*img.nframes);
for (i=0;i<img.nframes;i++) {
sprintf(key,"DT%04d",i);
strcpy(val,qfits_query_hdr(filename,key));
sscanf(val+1,"%f",&img.dt[i]);
// img.dt[i]=atof(qfits_query_hdr(filename,key));
}
// Allocate image memory
img.zavg=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2);
img.zstd=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2);
img.zmax=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2);
img.znum=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2);
// Loop over planes
for (k=0;k<4;k++) {
ql.pnum=k;;
// 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");
// Fill z array
for (i=0,l=0;i<img.naxis1;i++) {
for (j=0;j<img.naxis2;j++) {
if (k==0) img.zavg[l]=ql.fbuf[l];
if (k==1) img.zstd[l]=ql.fbuf[l];
if (k==2) img.zmax[l]=ql.fbuf[l];
if (k==3) img.znum[l]=ql.fbuf[l];
l++;
}
}
}
} else {
// Allocate image memory
img.zavg=(float *) malloc(sizeof(float)*img.naxis1*img.naxis2);
ql.pnum=0;
// 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");
// Fill z array
for (i=0,l=0;i<img.naxis1;i++) {
for (j=0;j<img.naxis2;j++) {
img.zavg[l]=ql.fbuf[l];
l++;
}
}
}
return img;
}
// 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;
}
// 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 && x<img.naxis1 && y>0.0 && y<img.naxis2) {
// Loop over pixel catalog
for (i=0;i<np;i++) {
r=sqrt(pow(xp[i]-x,2)+pow(yp[i]-y,2));
if (i==0 || r<rmin) {
rmin=r;
imin=i;
}
}
// Select
if (rmin<rmax && usage[imin]==1) {
c.x[j]=xp[imin]-t.x0;
c.y[j]=yp[imin]-t.y0;
c.imag[j]=mp[imin];
c.fb[j]=fb[imin];
c.fm[j]=fm[imin];
c.bg[j]=bg[imin];
c.ra[j]=s.ra;
c.de[j]=s.de;
c.vmag[j]=s.mag;
c.usage[j]=1;
usage[imin]=0;
j++;
}
}
}
fclose(file);
c.n=j;
return c;
}
// Plot astrometric catalog
void plot_astrometric_catalog(struct transformation t,struct image img,float mmin)
{
int i=0;
FILE *file;
struct star s;
double rx,ry,d,r;
double ra,de;
float x,y;
char *env,starfile[128];
// Environment variables
env=getenv("ST_DATADIR");
sprintf(starfile,"%s/data/tycho2.dat",env);
d=t.a[1]*t.b[2]-t.a[2]*t.b[1];
file=fopen(starfile,"rb");
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);
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 && x<img.naxis1 && y>0.0 && y<img.naxis2)
cpgpt1(x,y,24);
}
fclose(file);
return;
}
// Plot pixel catalog
void plot_pixel_catalog(char *filename)
{
int i=0;
FILE *file;
char line[LIM];
float x,y,mag;
// Read catalog
file=fopen(filename,"r");
while (fgetline(file,line,LIM)>0) {
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<n;i++) {
gsl_matrix_set(X,i,0,1.0);
gsl_matrix_set(X,i,1,x[i]);
gsl_matrix_set(X,i,2,y[i]);
gsl_vector_set(yy,i,z[i]);
gsl_vector_set(w,i,1.0);
}
// Do fit
gsl_multifit_linear_workspace *work=gsl_multifit_linear_alloc(n,3);
gsl_multifit_wlinear(X,w,yy,c,cov,&chisq,work);
gsl_multifit_linear_free(work);
// Save parameters
for (i=0;i<3;i++)
a[i]=gsl_vector_get(c,(i));
gsl_matrix_free(X);
gsl_vector_free(yy);
gsl_vector_free(w);
gsl_vector_free(c);
gsl_matrix_free(cov);
return;
}
// Add FITS keywords
void add_fits_keywords(struct transformation t,char *filename)
{
int i,j,k,l,m;
int naxis1,naxis2,naxis3;
qfits_header *qh;
qfitsdumper qd;
qfitsloader ql;
char key[FITS_LINESZ+1];
char val[FITS_LINESZ+1];
char com[FITS_LINESZ+1];
char lin[FITS_LINESZ+1];
FILE *file;
float *fbuf;
naxis1=atoi(qfits_query_hdr(filename,"NAXIS1"));
naxis2=atoi(qfits_query_hdr(filename,"NAXIS2"));
naxis3=atoi(qfits_query_hdr(filename,"NAXIS3"));
fbuf=malloc(sizeof(float)*naxis1*naxis2*naxis3);
// Read header
qh=qfits_header_read(filename);
ql.xtnum=0;
ql.ptype=PTYPE_FLOAT;
ql.filename=filename;
for (k=0,l=0;k<naxis3;k++) {
ql.pnum=k;
// 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");
for (i=0,m=0;i<naxis1;i++) {
for (j=0;j<naxis2;j++) {
fbuf[l]=ql.fbuf[m];
l++;
m++;
}
}
}
sprintf(val,"%e",t.yrms/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CRRES2",val," ",NULL);
sprintf(val,"%e",t.xrms/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CRRES1",val," ",NULL);
qfits_header_add_after(qh,"MJD-OBS","CUNIT2","'deg'"," ",NULL);
qfits_header_add_after(qh,"MJD-OBS","CUNIT1","'deg'"," ",NULL);
qfits_header_add_after(qh,"MJD-OBS","CTYPE2","'DEC--TAN'"," ",NULL);
qfits_header_add_after(qh,"MJD-OBS","CTYPE1","'RA---TAN'"," ",NULL);
sprintf(val,"%e",t.b[2]/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CD2_2",val," ",NULL);
sprintf(val,"%e",t.b[1]/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CD2_1",val," ",NULL);
sprintf(val,"%e",t.a[2]/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CD1_2",val," ",NULL);
sprintf(val,"%e",t.a[1]/3600.0);
qfits_header_add_after(qh,"MJD-OBS","CD1_1",val," ",NULL);
sprintf(val,"%f",t.de0);
qfits_header_add_after(qh,"MJD-OBS","CRVAL2",val," ",NULL);
sprintf(val,"%f",t.ra0);
qfits_header_add_after(qh,"MJD-OBS","CRVAL1",val," ",NULL);
sprintf(val,"%f",t.y0);
qfits_header_add_after(qh,"MJD-OBS","CRPIX2",val," ",NULL);
sprintf(val,"%f",t.x0);
qfits_header_add_after(qh,"MJD-OBS","CRPIX1",val," ",NULL);
file=fopen(filename,"w");
qfits_header_dump(qh,file);
fclose(file);
qfits_header_destroy(qh);
qd.filename=filename;
qd.npix=naxis1*naxis2*naxis3;
qd.ptype=PTYPE_FLOAT;
qd.fbuf=fbuf;
qd.out_ptype=-32;
qfits_pixdump(&qd);
free(fbuf);
return;
}
// 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);
sprintf(val,"%e",t.xrms/3600.0);
keytuple2str(card,"CRRES1",val,"");
qfits_replace_card(filename,"CRRES1",card);
sprintf(val,"%e",t.yrms/3600.0);
keytuple2str(card,"CRRES2",val,"");
qfits_replace_card(filename,"CRRES2",card);
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;
}