Restructure repository and pep8 changes
Also all executables now use argparse for arguments and helpers.pull/5/head
parent
626cbbb3df
commit
96389ccdc8
|
@ -11,7 +11,7 @@ from astropy.coordinates import EarthLocation
|
|||
from astropy.time import Time
|
||||
from astropy.io import fits
|
||||
import astropy.units as u
|
||||
from utils import get_sunset_and_sunrise
|
||||
from stvid.utils import get_sunset_and_sunrise
|
||||
import logging
|
||||
import configparser
|
||||
import argparse
|
||||
|
|
177
astrometry.py
177
astrometry.py
|
@ -1,177 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import numpy as np
|
||||
import astropy.units as u
|
||||
from astropy.io import fits
|
||||
from astropy import wcs
|
||||
from astropy.coordinates import SkyCoord,FK5,ICRS
|
||||
from astropy.time import Time
|
||||
from scipy import optimize
|
||||
|
||||
# Class for the Tycho 2 catalog
|
||||
class tycho2_catalog:
|
||||
"""Tycho2 catalog"""
|
||||
|
||||
def __init__(self,maxmag=9.0):
|
||||
hdu=fits.open(os.path.join(os.getenv("ST_DATADIR"),"data/tyc2.fits"))
|
||||
|
||||
ra=hdu[1].data.field('RA')*u.deg
|
||||
dec=hdu[1].data.field('DEC')*u.deg
|
||||
mag=hdu[1].data.field('MAG_VT')
|
||||
|
||||
c=mag<maxmag
|
||||
self.ra=ra[c]
|
||||
self.dec=dec[c]
|
||||
self.mag=mag[c]
|
||||
|
||||
# Estimate the WCS from a reference file
|
||||
def estimate_wcs_from_reference(ref,fname):
|
||||
# Read header of reference
|
||||
hdu=fits.open(ref)
|
||||
hdu[0].header["NAXIS"]=2
|
||||
w=wcs.WCS(hdu[0].header)
|
||||
|
||||
# Get time and position from reference
|
||||
tref=Time(hdu[0].header["MJD-OBS"],format="mjd",scale="utc")
|
||||
pref=SkyCoord(ra=w.wcs.crval[0],dec=w.wcs.crval[1],unit="deg",frame="icrs").transform_to(FK5(equinox=tref))
|
||||
|
||||
# Read time from target
|
||||
hdu=fits.open(fname)
|
||||
t=Time(hdu[0].header["MJD-OBS"],format="mjd",scale="utc")
|
||||
|
||||
# Correct wcs
|
||||
dra=t.sidereal_time("mean","greenwich")-tref.sidereal_time("mean","greenwich")
|
||||
p=FK5(ra=pref.ra+dra,dec=pref.dec,equinox=t).transform_to(ICRS)
|
||||
w.wcs.crval=np.array([p.ra.degree,p.dec.degree])
|
||||
|
||||
return w
|
||||
|
||||
# Match the astrometry and pixel catalog
|
||||
def match_catalogs(ast_catalog,pix_catalog,w,rmin):
|
||||
# Select stars towards pointing center
|
||||
ra,dec=w.wcs.crval*u.deg
|
||||
d=np.arccos(np.sin(dec)*np.sin(ast_catalog.dec)+np.cos(dec)*np.cos(ast_catalog.dec)*np.cos(ra-ast_catalog.ra))
|
||||
c=(d<30.0*u.deg)
|
||||
ra,dec,mag=ast_catalog.ra[c],ast_catalog.dec[c],ast_catalog.mag[c]
|
||||
|
||||
# Convert RA/Dec to pixels
|
||||
pix=w.wcs_world2pix(np.stack((ra,dec),axis=-1),0)
|
||||
xs,ys=pix[:,0],pix[:,1]
|
||||
|
||||
# Loop over stars
|
||||
nmatch=0
|
||||
for i in range(len(pix_catalog.x)):
|
||||
dx=xs-pix_catalog.x[i]
|
||||
dy=ys-pix_catalog.y[i]
|
||||
r=np.sqrt(dx*dx+dy*dy)
|
||||
if np.min(r)<rmin:
|
||||
j=np.argmin(r)
|
||||
pix_catalog.ra[i]=ra[j].value
|
||||
pix_catalog.dec[i]=dec[j].value
|
||||
pix_catalog.imag[i]=mag[j]
|
||||
pix_catalog.flag[i]=1
|
||||
nmatch+=1
|
||||
|
||||
return nmatch
|
||||
|
||||
# Residual function
|
||||
def residual(a,x,y,z):
|
||||
return z-(a[0]+a[1]*x+a[2]*y)
|
||||
|
||||
# Fit transformation
|
||||
def fit_wcs(w,pix_catalog):
|
||||
x0,y0=w.wcs.crpix
|
||||
ra0,dec0=w.wcs.crval
|
||||
dx,dy=pix_catalog.x-x0,pix_catalog.y-y0
|
||||
|
||||
# Iterate to remove outliers
|
||||
nstars=np.sum(pix_catalog.flag==1)
|
||||
for j in range(10):
|
||||
w=wcs.WCS(naxis=2)
|
||||
w.wcs.crpix=np.array([0.0,0.0])
|
||||
w.wcs.cd=np.array([[1.0,0.0],[0.0,1.0]])
|
||||
w.wcs.ctype=["RA---TAN","DEC--TAN"]
|
||||
w.wcs.set_pv([(2,1,45.0)])
|
||||
c=pix_catalog.flag==1
|
||||
|
||||
# Iterate to move crval to crpix location
|
||||
for i in range(5):
|
||||
w.wcs.crval=np.array([ra0,dec0])
|
||||
|
||||
world=np.stack((pix_catalog.ra,pix_catalog.dec),axis=-1)
|
||||
pix=w.wcs_world2pix(world,1)
|
||||
rx,ry=pix[:,0],pix[:,1]
|
||||
|
||||
ax,cov_q,infodict,mesg,ierr=optimize.leastsq(residual,[0.0,0.0,0.0],args=(dx[c],dy[c],rx[c]),full_output=1)
|
||||
ay,cov_q,infodict,mesg,ierr=optimize.leastsq(residual,[0.0,0.0,0.0],args=(dx[c],dy[c],ry[c]),full_output=1)
|
||||
|
||||
ra0,dec0=w.wcs_pix2world([[ax[0],ay[0]]],1)[0]
|
||||
|
||||
# Compute residuals
|
||||
drx=ax[0]+ax[1]*dx+ax[2]*dy-rx
|
||||
dry=ay[0]+ay[1]*dx+ay[2]*dy-ry
|
||||
dr=np.sqrt(drx*drx+dry*dry)
|
||||
rms=np.sqrt(np.sum(dr[c]**2)/len(dr[c]))
|
||||
|
||||
dr[~c]=1.0
|
||||
c=(dr<2.0*rms)
|
||||
pix_catalog.flag[~c]=0
|
||||
|
||||
# Break if converged
|
||||
if np.sum(c)==nstars:
|
||||
break
|
||||
nstars=np.sum(c)
|
||||
|
||||
# Compute residuals
|
||||
rmsx=np.sqrt(np.sum(drx[c]**2)/len(drx[c]))
|
||||
rmsy=np.sqrt(np.sum(dry[c]**2)/len(dry[c]))
|
||||
|
||||
# Store header
|
||||
w=wcs.WCS(naxis=2)
|
||||
w.wcs.crpix=np.array([x0,y0])
|
||||
w.wcs.crval=np.array([ra0,dec0])
|
||||
w.wcs.cd=np.array([[ax[1],ax[2]],[ay[1],ay[2]]])
|
||||
w.wcs.ctype=["RA---TAN","DEC--TAN"]
|
||||
w.wcs.set_pv([(2,1,45.0)])
|
||||
|
||||
return w,rmsx,rmsy,rms
|
||||
|
||||
def add_wcs(fname,w,rmsx,rmsy):
|
||||
# Read fits
|
||||
hdu=fits.open(fname)
|
||||
|
||||
whdr={"CRPIX1":w.wcs.crpix[0],"CRPIX2":w.wcs.crpix[1],"CRVAL1":w.wcs.crval[0],"CRVAL2":w.wcs.crval[1],"CD1_1":w.wcs.cd[0,0],"CD1_2":w.wcs.cd[0,1],"CD2_1":w.wcs.cd[1,0],"CD2_2":w.wcs.cd[1,1],"CTYPE1":"RA---TAN","CTYPE2":"DEC--TAN","CUNIT1":"DEG","CUNIT2":"DEG","CRRES1":rmsx,"CRRES2":rmsy}
|
||||
|
||||
# Add keywords
|
||||
hdr=hdu[0].header
|
||||
for k,v in whdr.items():
|
||||
hdr[k]=v
|
||||
hdu=fits.PrimaryHDU(header=hdr,data=hdu[0].data)
|
||||
hdu.writeto(fname,overwrite=True,output_verify="ignore")
|
||||
|
||||
return
|
||||
|
||||
def calibrate_from_reference(fname,ref,pix_catalog):
|
||||
# Estimated WCS
|
||||
w=estimate_wcs_from_reference(ref,fname)
|
||||
|
||||
# Default rms values
|
||||
rmsx=0.0
|
||||
rmsy=0.0
|
||||
|
||||
# Read catalogs
|
||||
if (pix_catalog.nstars>4):
|
||||
ast_catalog=tycho2_catalog(10.0)
|
||||
|
||||
# Match catalogs
|
||||
nmatch=match_catalogs(ast_catalog,pix_catalog,w,10.0)
|
||||
|
||||
# Fit transformation
|
||||
if nmatch>4:
|
||||
w,rmsx,rmsy,rms=fit_wcs(w,pix_catalog)
|
||||
|
||||
# Add wcs
|
||||
add_wcs(fname,w,rmsx,rmsy)
|
||||
|
||||
return w,rmsx,rmsy
|
13
capture_1.sh
13
capture_1.sh
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Sleep
|
||||
sleep 10
|
||||
|
||||
# Does /dev/video1 exist
|
||||
ls -l /dev/video1 >/tmp/camera_1.log 2>&1
|
||||
|
||||
# Run mplayer for 10 frames to setup easycap
|
||||
mplayer -frames 10 tv:// -tv device=/dev/video1 >/tmp/camera_1.log 2>&1
|
||||
|
||||
# Start script
|
||||
python ./acquire.py 1 >/tmp/camera_1.log 2>&1 &
|
13
capture_2.sh
13
capture_2.sh
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Sleep
|
||||
sleep 10
|
||||
|
||||
# Does /dev/video2 exist
|
||||
ls -l /dev/video2 >/tmp/camera_2.log 2>&1
|
||||
|
||||
# Run mplayer for 10 frames to setup easycap
|
||||
mplayer -frames 10 tv:// -tv device=/dev/video2 >/tmp/camera_2.log 2>&1
|
||||
|
||||
# Start script
|
||||
python ./acquire.py 2 >/tmp/camera_2.log 2>&1 &
|
|
@ -1,13 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import time
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
from stio import fourframe, satid, observation
|
||||
from stvid.stio import fourframe, satid, observation
|
||||
import numpy as np
|
||||
import ppgplot as ppg
|
||||
from scipy import optimize, ndimage
|
||||
import configparser
|
||||
import argparse
|
||||
|
||||
|
||||
# Gaussian model
|
||||
|
@ -454,15 +455,35 @@ if __name__ == '__main__':
|
|||
# Minimum track points
|
||||
ntrkmin = 10
|
||||
|
||||
# Read commandline options
|
||||
conf_parser = argparse.ArgumentParser(description='Extract satellite' +
|
||||
' tracks from frames.')
|
||||
conf_parser.add_argument("-c", "--conf_file",
|
||||
help="Specify configuration file. If no file" +
|
||||
" is specified 'configuration.ini' is used.",
|
||||
metavar="FILE")
|
||||
conf_parser.add_argument("-d", "--directory",
|
||||
help="Specify directory of observations. If no" +
|
||||
" directory is specified parent will be used.",
|
||||
metavar='DIR', dest='file_dir', default=".")
|
||||
|
||||
args = conf_parser.parse_args()
|
||||
|
||||
# Process commandline options and parse configuration
|
||||
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||
if args.conf_file:
|
||||
cfg.read([args.conf_file])
|
||||
else:
|
||||
cfg.read('configuration.ini')
|
||||
|
||||
# Create output dirs
|
||||
path = os.getenv("ST_OBSDIR")+"/"+time.strftime("%Y%m%d/%H%M%S",
|
||||
time.gmtime())
|
||||
os.makedirs(path+"/classfd")
|
||||
os.makedirs(path+"/catalog")
|
||||
os.makedirs(path+"/unid")
|
||||
path = args.file_dir
|
||||
os.makedirs(path+"classfd")
|
||||
os.makedirs(path+"catalog")
|
||||
os.makedirs(path+"unid")
|
||||
|
||||
# Get files
|
||||
files = sorted(glob.glob("2*.fits"))
|
||||
files = sorted(glob.glob(path+"2*.fits"))
|
||||
|
||||
# Process files
|
||||
for file in files:
|
||||
|
|
|
@ -4,38 +4,66 @@ import numpy as np
|
|||
from astropy.io import ascii
|
||||
import matplotlib.pyplot as plt
|
||||
import astropy.units as u
|
||||
from astropy.coordinates import SkyCoord,FK5,AltAz,EarthLocation
|
||||
from astropy.coordinates import SkyCoord, AltAz, EarthLocation
|
||||
from astropy.time import Time
|
||||
import configparser
|
||||
import argparse
|
||||
|
||||
table=ascii.read("imgstat.csv",format="csv")
|
||||
# Read commandline options
|
||||
conf_parser = argparse.ArgumentParser(description='Plot image statistics')
|
||||
conf_parser.add_argument("-c", "--conf_file",
|
||||
help="Specify configuration file. If no file" +
|
||||
" is specified 'configuration.ini' is used.",
|
||||
metavar="FILE")
|
||||
conf_parser.add_argument("-i", "--input",
|
||||
help="Specify file to be processed. If no file" +
|
||||
" is specified ./imgstat.csv will be used.",
|
||||
metavar='FILE', default="./imgstat.csv")
|
||||
conf_parser.add_argument("-o", "--output",
|
||||
help="Specify output file. Default is 'imgstat.png'",
|
||||
metavar='FILE', default="./imgstat.png")
|
||||
|
||||
t=Time(table['mjd'],format="mjd",scale="utc")
|
||||
pos=SkyCoord(ra=table['ra'],dec=table['de'],frame="icrs",unit="deg")
|
||||
args = conf_parser.parse_args()
|
||||
|
||||
loc=EarthLocation(lat=52.8344*u.deg,lon=6.3785*u.deg,height=10*u.m)
|
||||
# Process commandline options and parse configuration
|
||||
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||
if args.conf_file:
|
||||
cfg.read([args.conf_file])
|
||||
else:
|
||||
cfg.read('configuration.ini')
|
||||
|
||||
pa=pos.transform_to(AltAz(obstime=t,location=loc))
|
||||
table = ascii.read(args.input, format="csv")
|
||||
|
||||
mjd0=np.floor(np.min(table['mjd']))
|
||||
t = Time(table['mjd'], format="mjd", scale="utc")
|
||||
pos = SkyCoord(ra=table['ra'], dec=table['de'], frame="icrs", unit="deg")
|
||||
|
||||
plt.figure(figsize=(20,10))
|
||||
# Set location
|
||||
loc = EarthLocation(lat=cfg.getfloat('Common', 'observer_lat')*u.deg,
|
||||
lon=cfg.getfloat('Common', 'observer_lon')*u.deg,
|
||||
height=cfg.getfloat('Common', 'observer_el')*u.m)
|
||||
|
||||
pa = pos.transform_to(AltAz(obstime=t, location=loc))
|
||||
|
||||
mjd0 = np.floor(np.min(table['mjd']))
|
||||
|
||||
plt.figure(figsize=(20, 10))
|
||||
plt.subplot(411)
|
||||
|
||||
plt.plot(table['mjd']-mjd0,table['mean'],label='Brightness')
|
||||
plt.plot(table['mjd']-mjd0,table['std'],label='Variation')
|
||||
plt.plot(table['mjd']-mjd0, table['mean'], label='Brightness')
|
||||
plt.plot(table['mjd']-mjd0, table['std'], label='Variation')
|
||||
plt.ylabel("ADU")
|
||||
plt.legend()
|
||||
plt.subplot(412)
|
||||
plt.plot(table['mjd']-mjd0,pa.az.degree)
|
||||
plt.plot(table['mjd']-mjd0, pa.az.degree)
|
||||
plt.ylabel("Azimuth (deg)")
|
||||
plt.subplot(413)
|
||||
plt.plot(table['mjd']-mjd0,pa.alt.degree)
|
||||
plt.plot(table['mjd']-mjd0, pa.alt.degree)
|
||||
plt.ylabel("Altitude (deg)")
|
||||
plt.subplot(414)
|
||||
plt.plot(table['mjd']-mjd0,table['rmsx'],label='RA')
|
||||
plt.plot(table['mjd']-mjd0,table['rmsy'],label='Dec')
|
||||
plt.ylim(0,60)
|
||||
plt.plot(table['mjd']-mjd0, table['rmsx'], label='RA')
|
||||
plt.plot(table['mjd']-mjd0, table['rmsy'], label='Dec')
|
||||
plt.ylim(0, 60)
|
||||
plt.ylabel("Residual (arcseconds)")
|
||||
plt.xlabel("MJD - %.0f"%mjd0)
|
||||
plt.xlabel("MJD - %.0f" % mjd0)
|
||||
plt.legend()
|
||||
plt.savefig("imgstat.png")
|
||||
plt.savefig(args.output)
|
||||
|
|
|
@ -2,46 +2,79 @@
|
|||
from __future__ import print_function
|
||||
import glob
|
||||
import numpy as np
|
||||
from stio import fourframe
|
||||
from stars import pixel_catalog, generate_star_catalog
|
||||
from astrometry import calibrate_from_reference
|
||||
from stvid.stio import fourframe
|
||||
from stvid.stars import generate_star_catalog
|
||||
from stvid.astrometry import calibrate_from_reference
|
||||
import astropy.units as u
|
||||
from astropy.utils.exceptions import AstropyWarning
|
||||
from astropy.coordinates import EarthLocation, AltAz
|
||||
from astropy.coordinates import EarthLocation
|
||||
import warnings
|
||||
import configparser
|
||||
import argparse
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Read commandline options
|
||||
conf_parser = argparse.ArgumentParser(description='Process captured' +
|
||||
' video frames.')
|
||||
conf_parser.add_argument("-c", "--conf_file",
|
||||
help="Specify configuration file. If no file" +
|
||||
" is specified 'configuration.ini' is used.",
|
||||
metavar="FILE")
|
||||
conf_parser.add_argument("-d", "--directory",
|
||||
help="Specify directory of observations. If no" +
|
||||
" directory is specified parent will be used.",
|
||||
metavar='DIR', dest='file_dir', default=".")
|
||||
|
||||
args = conf_parser.parse_args()
|
||||
|
||||
# Process commandline options and parse configuration
|
||||
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||
if args.conf_file:
|
||||
cfg.read([args.conf_file])
|
||||
else:
|
||||
cfg.read('configuration.ini')
|
||||
|
||||
# Set warnings
|
||||
warnings.filterwarnings("ignore", category=UserWarning, append=True)
|
||||
warnings.simplefilter("ignore", AstropyWarning)
|
||||
|
||||
# Set location
|
||||
loc=EarthLocation(lat=52.8344*u.deg,lon=6.3785*u.deg,height=10*u.m)
|
||||
loc = EarthLocation(lat=cfg.getfloat('Common', 'observer_lat')*u.deg,
|
||||
lon=cfg.getfloat('Common', 'observer_lon')*u.deg,
|
||||
height=cfg.getfloat('Common', 'observer_el')*u.m)
|
||||
|
||||
# Get files
|
||||
files=sorted(glob.glob("2*.fits"))
|
||||
files = sorted(glob.glob(args.file_dir+"2*.fits"))
|
||||
|
||||
# Statistics file
|
||||
fstat=open("imgstat.csv","w")
|
||||
fstat = open(args.file_dir+"imgstat.csv", "w")
|
||||
fstat.write("fname,mjd,ra,de,rmsx,rmsy,mean,std,nstars,nused\n")
|
||||
|
||||
|
||||
# Loop over files
|
||||
for fname in files:
|
||||
# Generate star catalog
|
||||
pix_catalog=generate_star_catalog(fname)
|
||||
pix_catalog = generate_star_catalog(fname)
|
||||
|
||||
# Calibrate astrometry
|
||||
calibrate_from_reference(fname,"test.fits",pix_catalog)
|
||||
calibrate_from_reference(fname, args.file_dir+"test.fits", pix_catalog)
|
||||
|
||||
# Stars available and used
|
||||
nused=np.sum(pix_catalog.flag==1)
|
||||
nstars=pix_catalog.nstars
|
||||
nused = np.sum(pix_catalog.flag == 1)
|
||||
nstars = pix_catalog.nstars
|
||||
|
||||
# Get properties
|
||||
ff=fourframe(fname)
|
||||
|
||||
print("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f,%.3f,%d,%d"%(ff.fname,ff.mjd,ff.crval[0],ff.crval[1],3600*ff.crres[0],3600*ff.crres[1],np.mean(ff.zavg),np.std(ff.zavg),nstars,nused))
|
||||
fstat.write("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f,%.3f,%d,%d\n"%(ff.fname,ff.mjd,ff.crval[0],ff.crval[1],3600*ff.crres[0],3600*ff.crres[1],np.mean(ff.zavg),np.std(ff.zavg),nstars,nused))
|
||||
ff = fourframe(fname)
|
||||
|
||||
print(("%s,%.8lf,%.6f,%.6f,%.3f,%.3f," +
|
||||
"%.3f,%.3f,%d,%d") % (ff.fname, ff.mjd, ff.crval[0],
|
||||
ff.crval[1], 3600*ff.crres[0],
|
||||
3600*ff.crres[1], np.mean(ff.zavg),
|
||||
np.std(ff.zavg), nstars, nused))
|
||||
fstat.write(("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f," +
|
||||
"%.3f,%d,%d\n") % (ff.fname, ff.mjd, ff.crval[0],
|
||||
ff.crval[1], 3600*ff.crres[0],
|
||||
3600*ff.crres[1], np.mean(ff.zavg),
|
||||
np.std(ff.zavg), nstars, nused))
|
||||
|
||||
fstat.close()
|
||||
|
|
|
@ -3,3 +3,10 @@ numpy==1.14.2
|
|||
opencv-python==3.4.0.12
|
||||
scipy==1.0.1
|
||||
git+https://github.com/cbassa/ppgplot.git@master
|
||||
cycler==0.10.0
|
||||
kiwisolver==1.0.1
|
||||
matplotlib==2.2.2
|
||||
pyparsing==2.2.0
|
||||
python-dateutil==2.7.2
|
||||
pytz==2018.4
|
||||
six==1.11.0
|
||||
|
|
47
stars.py
47
stars.py
|
@ -1,47 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import subprocess
|
||||
import numpy as np
|
||||
|
||||
class pixel_catalog:
|
||||
"""Pixel catalog"""
|
||||
|
||||
def __init__(self,fname):
|
||||
d=np.loadtxt(fname)
|
||||
if len(d.shape)==2:
|
||||
self.x=d[:,0]
|
||||
self.y=d[:,1]
|
||||
self.mag=d[:,2]
|
||||
self.ra=np.empty_like(self.x)
|
||||
self.dec=np.empty_like(self.x)
|
||||
self.imag=np.empty_like(self.x)
|
||||
self.flag=np.zeros_like(self.x)
|
||||
self.nstars=len(self.mag)
|
||||
else:
|
||||
self.x=None
|
||||
self.y=None
|
||||
self.mag=None
|
||||
self.ra=None
|
||||
self.dec=None
|
||||
self.imag=None
|
||||
self.flag=None
|
||||
self.nstars=0
|
||||
|
||||
def generate_star_catalog(fname):
|
||||
# Skip if file already exists
|
||||
if not os.path.exists(fname+".cat"):
|
||||
# Get sextractor location
|
||||
env=os.getenv("ST_DATADIR")
|
||||
|
||||
# Format command
|
||||
command="sextractor %s -c %s/sextractor/default.sex"%(fname,env)
|
||||
|
||||
# Run sextractor
|
||||
output=subprocess.check_output(command,shell=True,stderr=subprocess.STDOUT)
|
||||
|
||||
# Rename file
|
||||
if os.path.exists("test.cat"):
|
||||
os.rename("test.cat",fname+".cat")
|
||||
|
||||
return pixel_catalog(fname+".cat")
|
|
@ -0,0 +1,208 @@
|
|||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import numpy as np
|
||||
import astropy.units as u
|
||||
from astropy.io import fits
|
||||
from astropy import wcs
|
||||
from astropy.coordinates import SkyCoord, FK5, ICRS
|
||||
from astropy.time import Time
|
||||
from scipy import optimize
|
||||
|
||||
|
||||
# Class for the Tycho 2 catalog
|
||||
class tycho2_catalog:
|
||||
"""Tycho2 catalog"""
|
||||
|
||||
def __init__(self, maxmag=9.0):
|
||||
hdu = fits.open(os.path.join(os.getenv("ST_DATADIR"),
|
||||
"data/tyc2.fits"))
|
||||
|
||||
ra = hdu[1].data.field('RA')*u.deg
|
||||
dec = hdu[1].data.field('DEC')*u.deg
|
||||
mag = hdu[1].data.field('MAG_VT')
|
||||
|
||||
c = mag < maxmag
|
||||
self.ra = ra[c]
|
||||
self.dec = dec[c]
|
||||
self.mag = mag[c]
|
||||
|
||||
|
||||
# Estimate the WCS from a reference file
|
||||
def estimate_wcs_from_reference(ref, fname):
|
||||
# Read header of reference
|
||||
hdu = fits.open(ref)
|
||||
hdu[0].header["NAXIS"] = 2
|
||||
w = wcs.WCS(hdu[0].header)
|
||||
|
||||
# Get time and position from reference
|
||||
tref = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
||||
pref = SkyCoord(ra=w.wcs.crval[0],
|
||||
dec=w.wcs.crval[1],
|
||||
unit="deg",
|
||||
frame="icrs").transform_to(FK5(equinox=tref))
|
||||
|
||||
# Read time from target
|
||||
hdu = fits.open(fname)
|
||||
t = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
||||
|
||||
# Correct wcs
|
||||
dra = (t.sidereal_time("mean", "greenwich")
|
||||
- tref.sidereal_time("mean", "greenwich"))
|
||||
p = FK5(ra=pref.ra+dra, dec=pref.dec, equinox=t).transform_to(ICRS)
|
||||
w.wcs.crval = np.array([p.ra.degree, p.dec.degree])
|
||||
|
||||
return w
|
||||
|
||||
|
||||
# Match the astrometry and pixel catalog
|
||||
def match_catalogs(ast_catalog, pix_catalog, w, rmin):
|
||||
# Select stars towards pointing center
|
||||
ra, dec = w.wcs.crval*u.deg
|
||||
d = np.arccos(np.sin(dec)
|
||||
* np.sin(ast_catalog.dec)
|
||||
+ np.cos(dec)
|
||||
* np.cos(ast_catalog.dec)
|
||||
* np.cos(ra-ast_catalog.ra))
|
||||
c = (d < 30.0*u.deg)
|
||||
ra, dec, mag = ast_catalog.ra[c], ast_catalog.dec[c], ast_catalog.mag[c]
|
||||
|
||||
# Convert RA/Dec to pixels
|
||||
pix = w.wcs_world2pix(np.stack((ra, dec), axis=-1), 0)
|
||||
xs, ys = pix[:, 0], pix[:, 1]
|
||||
|
||||
# Loop over stars
|
||||
nmatch = 0
|
||||
for i in range(len(pix_catalog.x)):
|
||||
dx = xs-pix_catalog.x[i]
|
||||
dy = ys-pix_catalog.y[i]
|
||||
r = np.sqrt(dx*dx+dy*dy)
|
||||
if np.min(r) < rmin:
|
||||
j = np.argmin(r)
|
||||
pix_catalog.ra[i] = ra[j].value
|
||||
pix_catalog.dec[i] = dec[j].value
|
||||
pix_catalog.imag[i] = mag[j]
|
||||
pix_catalog.flag[i] = 1
|
||||
nmatch += 1
|
||||
|
||||
return nmatch
|
||||
|
||||
|
||||
# Residual function
|
||||
def residual(a, x, y, z):
|
||||
return z-(a[0]+a[1]*x+a[2]*y)
|
||||
|
||||
|
||||
# Fit transformation
|
||||
def fit_wcs(w, pix_catalog):
|
||||
x0, y0 = w.wcs.crpix
|
||||
ra0, dec0 = w.wcs.crval
|
||||
dx, dy = pix_catalog.x-x0, pix_catalog.y-y0
|
||||
|
||||
# Iterate to remove outliers
|
||||
nstars = np.sum(pix_catalog.flag == 1)
|
||||
for j in range(10):
|
||||
w = wcs.WCS(naxis=2)
|
||||
w.wcs.crpix = np.array([0.0, 0.0])
|
||||
w.wcs.cd = np.array([[1.0, 0.0], [0.0, 1.0]])
|
||||
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
||||
w.wcs.set_pv([(2, 1, 45.0)])
|
||||
c = pix_catalog.flag == 1
|
||||
|
||||
# Iterate to move crval to crpix location
|
||||
for i in range(5):
|
||||
w.wcs.crval = np.array([ra0, dec0])
|
||||
|
||||
world = np.stack((pix_catalog.ra, pix_catalog.dec), axis=-1)
|
||||
pix = w.wcs_world2pix(world, 1)
|
||||
rx, ry = pix[:, 0], pix[:, 1]
|
||||
|
||||
ax, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
||||
[0.0, 0.0, 0.0],
|
||||
args=(dx[c],
|
||||
dy[c],
|
||||
rx[c]),
|
||||
full_output=1)
|
||||
ay, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
||||
[0.0, 0.0, 0.0],
|
||||
args=(dx[c],
|
||||
dy[c],
|
||||
ry[c]),
|
||||
full_output=1)
|
||||
|
||||
ra0, dec0 = w.wcs_pix2world([[ax[0], ay[0]]], 1)[0]
|
||||
|
||||
# Compute residuals
|
||||
drx = ax[0]+ax[1]*dx+ax[2]*dy-rx
|
||||
dry = ay[0]+ay[1]*dx+ay[2]*dy-ry
|
||||
dr = np.sqrt(drx*drx+dry*dry)
|
||||
rms = np.sqrt(np.sum(dr[c]**2)/len(dr[c]))
|
||||
|
||||
dr[~c] = 1.0
|
||||
c = (dr < 2.0*rms)
|
||||
pix_catalog.flag[~c] = 0
|
||||
|
||||
# Break if converged
|
||||
if np.sum(c) == nstars:
|
||||
break
|
||||
nstars = np.sum(c)
|
||||
|
||||
# Compute residuals
|
||||
rmsx = np.sqrt(np.sum(drx[c]**2)/len(drx[c]))
|
||||
rmsy = np.sqrt(np.sum(dry[c]**2)/len(dry[c]))
|
||||
|
||||
# Store header
|
||||
w = wcs.WCS(naxis=2)
|
||||
w.wcs.crpix = np.array([x0, y0])
|
||||
w.wcs.crval = np.array([ra0, dec0])
|
||||
w.wcs.cd = np.array([[ax[1], ax[2]], [ay[1], ay[2]]])
|
||||
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
||||
w.wcs.set_pv([(2, 1, 45.0)])
|
||||
|
||||
return w, rmsx, rmsy, rms
|
||||
|
||||
|
||||
def add_wcs(fname, w, rmsx, rmsy):
|
||||
# Read fits
|
||||
hdu = fits.open(fname)
|
||||
|
||||
whdr = {"CRPIX1": w.wcs.crpix[0], "CRPIX2": w.wcs.crpix[1],
|
||||
"CRVAL1": w.wcs.crval[0], "CRVAL2": w.wcs.crval[1],
|
||||
"CD1_1": w.wcs.cd[0, 0], "CD1_2": w.wcs.cd[0, 1],
|
||||
"CD2_1": w.wcs.cd[1, 0], "CD2_2": w.wcs.cd[1, 1],
|
||||
"CTYPE1": "RA---TAN", "CTYPE2": "DEC--TAN", "CUNIT1": "DEG",
|
||||
"CUNIT2": "DEG", "CRRES1": rmsx, "CRRES2": rmsy}
|
||||
|
||||
# Add keywords
|
||||
hdr = hdu[0].header
|
||||
for k, v in whdr.items():
|
||||
hdr[k] = v
|
||||
hdu = fits.PrimaryHDU(header=hdr, data=hdu[0].data)
|
||||
hdu.writeto(fname, overwrite=True, output_verify="ignore")
|
||||
|
||||
return
|
||||
|
||||
|
||||
def calibrate_from_reference(fname, ref, pix_catalog):
|
||||
# Estimated WCS
|
||||
w = estimate_wcs_from_reference(ref, fname)
|
||||
|
||||
# Default rms values
|
||||
rmsx = 0.0
|
||||
rmsy = 0.0
|
||||
|
||||
# Read catalogs
|
||||
if (pix_catalog.nstars > 4):
|
||||
ast_catalog = tycho2_catalog(10.0)
|
||||
|
||||
# Match catalogs
|
||||
nmatch = match_catalogs(ast_catalog, pix_catalog, w, 10.0)
|
||||
|
||||
# Fit transformation
|
||||
if nmatch > 4:
|
||||
w, rmsx, rmsy, rms = fit_wcs(w, pix_catalog)
|
||||
|
||||
# Add wcs
|
||||
add_wcs(fname, w, rmsx, rmsy)
|
||||
|
||||
return w, rmsx, rmsy
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import subprocess
|
||||
import numpy as np
|
||||
|
||||
|
||||
class pixel_catalog:
|
||||
"""Pixel catalog"""
|
||||
|
||||
def __init__(self, fname):
|
||||
d = np.loadtxt(fname)
|
||||
if len(d.shape) == 2:
|
||||
self.x = d[:, 0]
|
||||
self.y = d[:, 1]
|
||||
self.mag = d[:, 2]
|
||||
self.ra = np.empty_like(self.x)
|
||||
self.dec = np.empty_like(self.x)
|
||||
self.imag = np.empty_like(self.x)
|
||||
self.flag = np.zeros_like(self.x)
|
||||
self.nstars = len(self.mag)
|
||||
else:
|
||||
self.x = None
|
||||
self.y = None
|
||||
self.mag = None
|
||||
self.ra = None
|
||||
self.dec = None
|
||||
self.imag = None
|
||||
self.flag = None
|
||||
self.nstars = 0
|
||||
|
||||
|
||||
def generate_star_catalog(fname):
|
||||
# Skip if file already exists
|
||||
if not os.path.exists(fname+".cat"):
|
||||
# Get sextractor location
|
||||
env = os.getenv("ST_DATADIR")
|
||||
|
||||
# Format command
|
||||
command = "sextractor %s -c %s/sextractor/default.sex" % (fname, env)
|
||||
|
||||
# Run sextractor
|
||||
output = subprocess.check_output(command, shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Rename file
|
||||
if os.path.exists("test.cat"):
|
||||
os.rename("test.cat", fname+".cat")
|
||||
|
||||
return pixel_catalog(fname+".cat")
|
Loading…
Reference in New Issue